add eyedropper tool
This commit is contained in:
parent
a0875b1bc0
commit
2bb9aecf31
|
|
@ -83,6 +83,15 @@ impl Action for PaintBucketAction {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the path is closed - winding number only makes sense for closed paths
|
||||||
|
use vello::kurbo::PathEl;
|
||||||
|
let is_closed = shape.path().elements().iter().any(|el| matches!(el, PathEl::ClosePath));
|
||||||
|
|
||||||
|
if !is_closed {
|
||||||
|
// Skip non-closed paths - can't use winding number test
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Apply the object's transform to get the transformed path
|
// Apply the object's transform to get the transformed path
|
||||||
let transform_affine = object.transform.to_affine();
|
let transform_affine = object.transform.to_affine();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,7 @@ struct EditorApp {
|
||||||
selected_tool: Tool, // Currently selected drawing tool
|
selected_tool: Tool, // Currently selected drawing tool
|
||||||
fill_color: egui::Color32, // Fill color for drawing
|
fill_color: egui::Color32, // Fill color for drawing
|
||||||
stroke_color: egui::Color32, // Stroke color for drawing
|
stroke_color: egui::Color32, // Stroke color for drawing
|
||||||
|
active_color_mode: panes::ColorMode, // Which color (fill/stroke) was last interacted with
|
||||||
pane_instances: HashMap<NodePath, PaneInstance>, // Pane instances per path
|
pane_instances: HashMap<NodePath, PaneInstance>, // Pane instances per path
|
||||||
menu_system: Option<MenuSystem>, // Native menu system for event checking
|
menu_system: Option<MenuSystem>, // Native menu system for event checking
|
||||||
pending_view_action: Option<MenuAction>, // Pending view action (zoom, recenter) to be handled by hovered pane
|
pending_view_action: Option<MenuAction>, // Pending view action (zoom, recenter) to be handled by hovered pane
|
||||||
|
|
@ -311,6 +312,7 @@ impl EditorApp {
|
||||||
selected_tool: Tool::Select, // Default tool
|
selected_tool: Tool::Select, // Default tool
|
||||||
fill_color: egui::Color32::from_rgb(100, 100, 255), // Default blue fill
|
fill_color: egui::Color32::from_rgb(100, 100, 255), // Default blue fill
|
||||||
stroke_color: egui::Color32::from_rgb(0, 0, 0), // Default black stroke
|
stroke_color: egui::Color32::from_rgb(0, 0, 0), // Default black stroke
|
||||||
|
active_color_mode: panes::ColorMode::default(), // Default to fill color
|
||||||
pane_instances: HashMap::new(), // Initialize empty, panes created on-demand
|
pane_instances: HashMap::new(), // Initialize empty, panes created on-demand
|
||||||
menu_system,
|
menu_system,
|
||||||
pending_view_action: None,
|
pending_view_action: None,
|
||||||
|
|
@ -637,6 +639,7 @@ impl eframe::App for EditorApp {
|
||||||
&mut self.selected_tool,
|
&mut self.selected_tool,
|
||||||
&mut self.fill_color,
|
&mut self.fill_color,
|
||||||
&mut self.stroke_color,
|
&mut self.stroke_color,
|
||||||
|
&mut self.active_color_mode,
|
||||||
&mut self.pane_instances,
|
&mut self.pane_instances,
|
||||||
&Vec::new(), // Root path
|
&Vec::new(), // Root path
|
||||||
&mut self.pending_view_action,
|
&mut self.pending_view_action,
|
||||||
|
|
@ -727,6 +730,7 @@ fn render_layout_node(
|
||||||
selected_tool: &mut Tool,
|
selected_tool: &mut Tool,
|
||||||
fill_color: &mut egui::Color32,
|
fill_color: &mut egui::Color32,
|
||||||
stroke_color: &mut egui::Color32,
|
stroke_color: &mut egui::Color32,
|
||||||
|
active_color_mode: &mut panes::ColorMode,
|
||||||
pane_instances: &mut HashMap<NodePath, PaneInstance>,
|
pane_instances: &mut HashMap<NodePath, PaneInstance>,
|
||||||
path: &NodePath,
|
path: &NodePath,
|
||||||
pending_view_action: &mut Option<MenuAction>,
|
pending_view_action: &mut Option<MenuAction>,
|
||||||
|
|
@ -744,7 +748,7 @@ fn render_layout_node(
|
||||||
) {
|
) {
|
||||||
match node {
|
match node {
|
||||||
LayoutNode::Pane { name } => {
|
LayoutNode::Pane { name } => {
|
||||||
render_pane(ui, name, rect, selected_pane, layout_action, split_preview_mode, icon_cache, tool_icon_cache, selected_tool, fill_color, stroke_color, pane_instances, path, pending_view_action, fallback_pane_priority, pending_handlers, theme, action_executor, selection, active_layer_id, tool_state, pending_actions, draw_simplify_mode, rdp_tolerance, schneider_max_error);
|
render_pane(ui, name, rect, selected_pane, layout_action, split_preview_mode, icon_cache, tool_icon_cache, selected_tool, fill_color, stroke_color, active_color_mode, pane_instances, path, pending_view_action, fallback_pane_priority, pending_handlers, theme, action_executor, selection, active_layer_id, tool_state, pending_actions, draw_simplify_mode, rdp_tolerance, schneider_max_error);
|
||||||
}
|
}
|
||||||
LayoutNode::HorizontalGrid { percent, children } => {
|
LayoutNode::HorizontalGrid { percent, children } => {
|
||||||
// Handle dragging
|
// Handle dragging
|
||||||
|
|
@ -782,6 +786,7 @@ fn render_layout_node(
|
||||||
selected_tool,
|
selected_tool,
|
||||||
fill_color,
|
fill_color,
|
||||||
stroke_color,
|
stroke_color,
|
||||||
|
active_color_mode,
|
||||||
pane_instances,
|
pane_instances,
|
||||||
&left_path,
|
&left_path,
|
||||||
pending_view_action,
|
pending_view_action,
|
||||||
|
|
@ -814,6 +819,7 @@ fn render_layout_node(
|
||||||
selected_tool,
|
selected_tool,
|
||||||
fill_color,
|
fill_color,
|
||||||
stroke_color,
|
stroke_color,
|
||||||
|
active_color_mode,
|
||||||
pane_instances,
|
pane_instances,
|
||||||
&right_path,
|
&right_path,
|
||||||
pending_view_action,
|
pending_view_action,
|
||||||
|
|
@ -938,6 +944,7 @@ fn render_layout_node(
|
||||||
selected_tool,
|
selected_tool,
|
||||||
fill_color,
|
fill_color,
|
||||||
stroke_color,
|
stroke_color,
|
||||||
|
active_color_mode,
|
||||||
pane_instances,
|
pane_instances,
|
||||||
&top_path,
|
&top_path,
|
||||||
pending_view_action,
|
pending_view_action,
|
||||||
|
|
@ -970,6 +977,7 @@ fn render_layout_node(
|
||||||
selected_tool,
|
selected_tool,
|
||||||
fill_color,
|
fill_color,
|
||||||
stroke_color,
|
stroke_color,
|
||||||
|
active_color_mode,
|
||||||
pane_instances,
|
pane_instances,
|
||||||
&bottom_path,
|
&bottom_path,
|
||||||
pending_view_action,
|
pending_view_action,
|
||||||
|
|
@ -1074,6 +1082,7 @@ fn render_pane(
|
||||||
selected_tool: &mut Tool,
|
selected_tool: &mut Tool,
|
||||||
fill_color: &mut egui::Color32,
|
fill_color: &mut egui::Color32,
|
||||||
stroke_color: &mut egui::Color32,
|
stroke_color: &mut egui::Color32,
|
||||||
|
active_color_mode: &mut panes::ColorMode,
|
||||||
pane_instances: &mut HashMap<NodePath, PaneInstance>,
|
pane_instances: &mut HashMap<NodePath, PaneInstance>,
|
||||||
path: &NodePath,
|
path: &NodePath,
|
||||||
pending_view_action: &mut Option<MenuAction>,
|
pending_view_action: &mut Option<MenuAction>,
|
||||||
|
|
@ -1258,6 +1267,7 @@ fn render_pane(
|
||||||
selected_tool,
|
selected_tool,
|
||||||
fill_color,
|
fill_color,
|
||||||
stroke_color,
|
stroke_color,
|
||||||
|
active_color_mode,
|
||||||
pending_view_action,
|
pending_view_action,
|
||||||
fallback_pane_priority,
|
fallback_pane_priority,
|
||||||
theme,
|
theme,
|
||||||
|
|
@ -1301,6 +1311,7 @@ fn render_pane(
|
||||||
selected_tool,
|
selected_tool,
|
||||||
fill_color,
|
fill_color,
|
||||||
stroke_color,
|
stroke_color,
|
||||||
|
active_color_mode,
|
||||||
pending_view_action,
|
pending_view_action,
|
||||||
fallback_pane_priority,
|
fallback_pane_priority,
|
||||||
theme,
|
theme,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,19 @@ pub mod piano_roll;
|
||||||
pub mod node_editor;
|
pub mod node_editor;
|
||||||
pub mod preset_browser;
|
pub mod preset_browser;
|
||||||
|
|
||||||
|
/// Which color mode is active for the eyedropper tool
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ColorMode {
|
||||||
|
Fill,
|
||||||
|
Stroke,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ColorMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
ColorMode::Fill
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shared state that all panes can access
|
/// Shared state that all panes can access
|
||||||
pub struct SharedPaneState<'a> {
|
pub struct SharedPaneState<'a> {
|
||||||
pub tool_icon_cache: &'a mut crate::ToolIconCache,
|
pub tool_icon_cache: &'a mut crate::ToolIconCache,
|
||||||
|
|
@ -34,6 +47,8 @@ pub struct SharedPaneState<'a> {
|
||||||
pub selected_tool: &'a mut Tool,
|
pub selected_tool: &'a mut Tool,
|
||||||
pub fill_color: &'a mut egui::Color32,
|
pub fill_color: &'a mut egui::Color32,
|
||||||
pub stroke_color: &'a mut egui::Color32,
|
pub stroke_color: &'a mut egui::Color32,
|
||||||
|
/// Tracks which color (fill or stroke) was last interacted with, for eyedropper tool
|
||||||
|
pub active_color_mode: &'a mut ColorMode,
|
||||||
pub pending_view_action: &'a mut Option<crate::menu::MenuAction>,
|
pub pending_view_action: &'a mut Option<crate::menu::MenuAction>,
|
||||||
/// Tracks the priority of the best fallback pane for view actions
|
/// Tracks the priority of the best fallback pane for view actions
|
||||||
/// Lower number = higher priority. None = no fallback pane seen yet
|
/// Lower number = higher priority. None = no fallback pane seen yet
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use super::{NodePath, PaneRenderer, SharedPaneState};
|
use super::{NodePath, PaneRenderer, SharedPaneState};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
use vello::kurbo::Shape;
|
use vello::kurbo::Shape;
|
||||||
|
|
||||||
/// Shared Vello resources (created once, reused by all Stage panes)
|
/// Shared Vello resources (created once, reused by all Stage panes)
|
||||||
|
|
@ -163,7 +163,7 @@ impl InstanceVelloResources {
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
format: wgpu::TextureFormat::Rgba8Unorm,
|
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||||
usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
|
usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_SRC,
|
||||||
view_formats: &[],
|
view_formats: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -205,6 +205,7 @@ struct VelloCallback {
|
||||||
fill_color: egui::Color32, // Current fill color for previews
|
fill_color: egui::Color32, // Current fill color for previews
|
||||||
stroke_color: egui::Color32, // Current stroke color for previews
|
stroke_color: egui::Color32, // Current stroke color for previews
|
||||||
selected_tool: lightningbeam_core::tool::Tool, // Current tool for rendering mode-specific UI
|
selected_tool: lightningbeam_core::tool::Tool, // Current tool for rendering mode-specific UI
|
||||||
|
eyedropper_request: Option<(egui::Pos2, super::ColorMode)>, // Pending eyedropper sample
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VelloCallback {
|
impl VelloCallback {
|
||||||
|
|
@ -221,8 +222,9 @@ impl VelloCallback {
|
||||||
fill_color: egui::Color32,
|
fill_color: egui::Color32,
|
||||||
stroke_color: egui::Color32,
|
stroke_color: egui::Color32,
|
||||||
selected_tool: lightningbeam_core::tool::Tool,
|
selected_tool: lightningbeam_core::tool::Tool,
|
||||||
|
eyedropper_request: Option<(egui::Pos2, super::ColorMode)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { rect, pan_offset, zoom, instance_id, document, tool_state, active_layer_id, drag_delta, selection, fill_color, stroke_color, selected_tool }
|
Self { rect, pan_offset, zoom, instance_id, document, tool_state, active_layer_id, drag_delta, selection, fill_color, stroke_color, selected_tool, eyedropper_request }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -921,6 +923,93 @@ impl egui_wgpu::CallbackTrait for VelloCallback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle eyedropper pixel sampling if requested
|
||||||
|
if let Some((screen_pos, color_mode)) = self.eyedropper_request {
|
||||||
|
if let Some(texture) = &instance_resources.texture {
|
||||||
|
// Convert screen position to texture coordinates
|
||||||
|
let tex_x = ((screen_pos.x - self.rect.min.x).max(0.0).min(self.rect.width())) as u32;
|
||||||
|
let tex_y = ((screen_pos.y - self.rect.min.y).max(0.0).min(self.rect.height())) as u32;
|
||||||
|
|
||||||
|
// Clamp to texture bounds
|
||||||
|
if tex_x < width && tex_y < height {
|
||||||
|
// Create a staging buffer to read back the pixel
|
||||||
|
let bytes_per_pixel = 4; // RGBA8
|
||||||
|
// Align bytes_per_row to 256 (wgpu::COPY_BYTES_PER_ROW_ALIGNMENT)
|
||||||
|
let bytes_per_row_alignment = 256u32;
|
||||||
|
let bytes_per_row = bytes_per_row_alignment; // Single pixel, use minimum alignment
|
||||||
|
let buffer_size = bytes_per_row as u64;
|
||||||
|
|
||||||
|
let staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("eyedropper_staging_buffer"),
|
||||||
|
size: buffer_size,
|
||||||
|
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a command encoder for the copy operation
|
||||||
|
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some("eyedropper_copy_encoder"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy the pixel from texture to staging buffer
|
||||||
|
encoder.copy_texture_to_buffer(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d { x: tex_x, y: tex_y, z: 0 },
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
},
|
||||||
|
wgpu::ImageCopyBuffer {
|
||||||
|
buffer: &staging_buffer,
|
||||||
|
layout: wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(bytes_per_row),
|
||||||
|
rows_per_image: Some(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Submit the copy command
|
||||||
|
queue.submit(Some(encoder.finish()));
|
||||||
|
|
||||||
|
// Map the buffer and read the pixel (synchronous for simplicity)
|
||||||
|
let buffer_slice = staging_buffer.slice(..);
|
||||||
|
let (sender, receiver) = std::sync::mpsc::channel();
|
||||||
|
buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
|
||||||
|
sender.send(result).ok();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Poll the device to complete the mapping
|
||||||
|
device.poll(wgpu::Maintain::Wait);
|
||||||
|
|
||||||
|
// Read the pixel data
|
||||||
|
if receiver.recv().is_ok() {
|
||||||
|
let data = buffer_slice.get_mapped_range();
|
||||||
|
if data.len() >= 4 {
|
||||||
|
let r = data[0];
|
||||||
|
let g = data[1];
|
||||||
|
let b = data[2];
|
||||||
|
let a = data[3];
|
||||||
|
|
||||||
|
let sampled_color = egui::Color32::from_rgba_unmultiplied(r, g, b, a);
|
||||||
|
|
||||||
|
// Store the result in the global eyedropper results
|
||||||
|
if let Ok(mut results) = EYEDROPPER_RESULTS
|
||||||
|
.get_or_init(|| Arc::new(Mutex::new(std::collections::HashMap::new())))
|
||||||
|
.lock() {
|
||||||
|
results.insert(self.instance_id, (sampled_color, color_mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmap the buffer
|
||||||
|
let _ = buffer_slice;
|
||||||
|
staging_buffer.unmap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -970,11 +1059,16 @@ pub struct StagePane {
|
||||||
last_pan_pos: Option<egui::Pos2>,
|
last_pan_pos: Option<egui::Pos2>,
|
||||||
// Unique ID for this stage instance (for Vello resources)
|
// Unique ID for this stage instance (for Vello resources)
|
||||||
instance_id: u64,
|
instance_id: u64,
|
||||||
|
// Eyedropper state
|
||||||
|
pending_eyedropper_sample: Option<(egui::Pos2, super::ColorMode)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global counter for generating unique instance IDs
|
// Global counter for generating unique instance IDs
|
||||||
static INSTANCE_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
static INSTANCE_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
||||||
|
|
||||||
|
// Global storage for eyedropper results (instance_id -> (color, color_mode))
|
||||||
|
static EYEDROPPER_RESULTS: OnceLock<Arc<Mutex<std::collections::HashMap<u64, (egui::Color32, super::ColorMode)>>>> = OnceLock::new();
|
||||||
|
|
||||||
impl StagePane {
|
impl StagePane {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let instance_id = INSTANCE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
let instance_id = INSTANCE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
@ -984,6 +1078,7 @@ impl StagePane {
|
||||||
is_panning: false,
|
is_panning: false,
|
||||||
last_pan_pos: None,
|
last_pan_pos: None,
|
||||||
instance_id,
|
instance_id,
|
||||||
|
pending_eyedropper_sample: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1616,6 +1711,19 @@ impl StagePane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_eyedropper_tool(
|
||||||
|
&mut self,
|
||||||
|
_ui: &mut egui::Ui,
|
||||||
|
response: &egui::Response,
|
||||||
|
screen_pos: egui::Pos2,
|
||||||
|
shared: &mut SharedPaneState,
|
||||||
|
) {
|
||||||
|
// On click, store the screen position and color mode for sampling
|
||||||
|
if response.clicked() {
|
||||||
|
self.pending_eyedropper_sample = Some((screen_pos, *shared.active_color_mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a rectangle path from lines (easier for curve editing later)
|
/// Create a rectangle path from lines (easier for curve editing later)
|
||||||
fn create_rectangle_path(width: f64, height: f64) -> vello::kurbo::BezPath {
|
fn create_rectangle_path(width: f64, height: f64) -> vello::kurbo::BezPath {
|
||||||
use vello::kurbo::{BezPath, Point};
|
use vello::kurbo::{BezPath, Point};
|
||||||
|
|
@ -2880,6 +2988,9 @@ impl StagePane {
|
||||||
Tool::Polygon => {
|
Tool::Polygon => {
|
||||||
self.handle_polygon_tool(ui, &response, world_pos, shift_held, ctrl_held, shared);
|
self.handle_polygon_tool(ui, &response, world_pos, shift_held, ctrl_held, shared);
|
||||||
}
|
}
|
||||||
|
Tool::Eyedropper => {
|
||||||
|
self.handle_eyedropper_tool(ui, &response, mouse_pos, shared);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Other tools not implemented yet
|
// Other tools not implemented yet
|
||||||
}
|
}
|
||||||
|
|
@ -2951,6 +3062,25 @@ impl PaneRenderer for StagePane {
|
||||||
_path: &NodePath,
|
_path: &NodePath,
|
||||||
shared: &mut SharedPaneState,
|
shared: &mut SharedPaneState,
|
||||||
) {
|
) {
|
||||||
|
// Check for completed eyedropper samples from GPU readback and apply them
|
||||||
|
if let Ok(mut results) = EYEDROPPER_RESULTS
|
||||||
|
.get_or_init(|| Arc::new(Mutex::new(std::collections::HashMap::new())))
|
||||||
|
.lock() {
|
||||||
|
if let Some((color, color_mode)) = results.remove(&self.instance_id) {
|
||||||
|
// Apply the sampled color to the appropriate mode
|
||||||
|
match color_mode {
|
||||||
|
super::ColorMode::Fill => {
|
||||||
|
*shared.fill_color = color;
|
||||||
|
}
|
||||||
|
super::ColorMode::Stroke => {
|
||||||
|
*shared.stroke_color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear the pending request since we've processed it
|
||||||
|
self.pending_eyedropper_sample = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle input for pan/zoom and tool controls
|
// Handle input for pan/zoom and tool controls
|
||||||
self.handle_input(ui, rect, shared);
|
self.handle_input(ui, rect, shared);
|
||||||
|
|
||||||
|
|
@ -3040,6 +3170,7 @@ impl PaneRenderer for StagePane {
|
||||||
*shared.fill_color,
|
*shared.fill_color,
|
||||||
*shared.stroke_color,
|
*shared.stroke_color,
|
||||||
*shared.selected_tool,
|
*shared.selected_tool,
|
||||||
|
self.pending_eyedropper_sample,
|
||||||
);
|
);
|
||||||
|
|
||||||
let cb = egui_wgpu::Callback::new_paint_callback(
|
let cb = egui_wgpu::Callback::new_paint_callback(
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,11 @@ impl PaneRenderer for ToolbarPane {
|
||||||
|
|
||||||
// Show fill color picker popup
|
// Show fill color picker popup
|
||||||
egui::popup::popup_below_widget(ui, fill_button_id, &fill_response, egui::popup::PopupCloseBehavior::CloseOnClickOutside, |ui: &mut egui::Ui| {
|
egui::popup::popup_below_widget(ui, fill_button_id, &fill_response, egui::popup::PopupCloseBehavior::CloseOnClickOutside, |ui: &mut egui::Ui| {
|
||||||
egui::color_picker::color_picker_color32(ui, shared.fill_color, egui::color_picker::Alpha::OnlyBlend);
|
let changed = egui::color_picker::color_picker_color32(ui, shared.fill_color, egui::color_picker::Alpha::OnlyBlend);
|
||||||
|
// Track that the user interacted with the fill color
|
||||||
|
if changed {
|
||||||
|
*shared.active_color_mode = super::ColorMode::Fill;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
y += color_button_size + button_spacing;
|
y += color_button_size + button_spacing;
|
||||||
|
|
@ -188,7 +192,11 @@ impl PaneRenderer for ToolbarPane {
|
||||||
|
|
||||||
// Show stroke color picker popup
|
// Show stroke color picker popup
|
||||||
egui::popup::popup_below_widget(ui, stroke_button_id, &stroke_response, egui::popup::PopupCloseBehavior::CloseOnClickOutside, |ui: &mut egui::Ui| {
|
egui::popup::popup_below_widget(ui, stroke_button_id, &stroke_response, egui::popup::PopupCloseBehavior::CloseOnClickOutside, |ui: &mut egui::Ui| {
|
||||||
egui::color_picker::color_picker_color32(ui, shared.stroke_color, egui::color_picker::Alpha::OnlyBlend);
|
let changed = egui::color_picker::color_picker_color32(ui, shared.stroke_color, egui::color_picker::Alpha::OnlyBlend);
|
||||||
|
// Track that the user interacted with the stroke color
|
||||||
|
if changed {
|
||||||
|
*shared.active_color_mode = super::ColorMode::Stroke;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue