From f3e65f85e141bdf903f018be2f727a4a7cfd3670 Mon Sep 17 00:00:00 2001 From: "Rene D. Obermueller" Date: Sun, 22 Feb 2026 20:43:15 +0100 Subject: [PATCH] feat: indicate active editing in toolbar This adds a highlight (using adwaita @accent_color) for the button for a tool that is actively editing. This works for: - arrow - blur - crop - ellipse - highlight - line - rectangle The idea is that it's better visible when esc/enter, which normally trigger actions, are masked by a tool. --- src/assets/default.css | 4 ++++ src/main.rs | 9 +++++++++ src/sketch_board.rs | 12 ++++++++++++ src/tools/arrow.rs | 20 +++++++++++++++++--- src/tools/blur.rs | 21 ++++++++++++++++++--- src/tools/crop.rs | 8 ++++++++ src/tools/ellipse.rs | 21 ++++++++++++++++++--- src/tools/highlight.rs | 20 +++++++++++++++++--- src/tools/line.rs | 20 +++++++++++++++++--- src/tools/rectangle.rs | 21 ++++++++++++++++++--- src/ui/toolbars.rs | 17 +++++++++++++++++ 11 files changed, 155 insertions(+), 18 deletions(-) diff --git a/src/assets/default.css b/src/assets/default.css index ae09302..99786dc 100644 --- a/src/assets/default.css +++ b/src/assets/default.css @@ -25,3 +25,7 @@ .overlay .toolbar-bottom { border-radius: 6px 6px 0px 0px; } .overlay .toolbar-top { border-radius: 0px 0px 6px 6px; } + +button.editing { + color: @accent_color; +} diff --git a/src/main.rs b/src/main.rs index 114c599..895f78d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,6 +72,7 @@ enum AppInput { ScaleFactorChanged, FullscreenChanged(bool), DimensionsUpdate(Option<(i32, i32)>), + ToolEditingChanged(bool), } #[derive(Debug)] @@ -289,6 +290,11 @@ impl Component for App { .sender() .emit(StyleToolbarInput::DimensionsChanged(d)); } + AppInput::ToolEditingChanged(editing) => { + self.tools_toolbar + .sender() + .emit(ToolsToolbarInput::SetToolEditing(editing)); + } } } @@ -326,6 +332,9 @@ impl Component for App { SketchBoardOutput::DimensionsUpdate(dimensions) => { AppInput::DimensionsUpdate(dimensions) } + SketchBoardOutput::ToolEditingChanged(editing) => { + AppInput::ToolEditingChanged(editing) + } }); // Toolbars diff --git a/src/sketch_board.rs b/src/sketch_board.rs index b63c7de..7a3ad77 100644 --- a/src/sketch_board.rs +++ b/src/sketch_board.rs @@ -47,6 +47,7 @@ pub enum SketchBoardOutput { ToolSwitchShortcut(Tools), ColorSwitchShortcut(u64), DimensionsUpdate(Option<(i32, i32)>), + ToolEditingChanged(bool), } #[derive(Debug, Clone)] @@ -239,6 +240,7 @@ impl InputEvent { pub struct SketchBoard { renderer: FemtoVGArea, active_tool: Rc>, + tool_edit_mode: bool, tools: ToolsManager, style: Style, im_context: gtk::IMMulticontext, @@ -974,6 +976,7 @@ impl Component for SketchBoard { fn update(&mut self, msg: SketchBoardInput, sender: ComponentSender, _root: &Self::Root) { // handle resize ourselves, pass everything else to tool + let sender_clone = sender.clone(); let result = match msg { SketchBoardInput::InputEvent(mut ie) => { if let InputEvent::Key(ke) = ie { @@ -1142,6 +1145,14 @@ impl Component for SketchBoard { } }; + let editing = self.active_tool.borrow().active(); + if editing != self.tool_edit_mode { + self.tool_edit_mode = editing; + sender_clone + .output_sender() + .emit(SketchBoardOutput::ToolEditingChanged(editing)); + } + // println!(" Result={:?}", result); match result { ToolUpdateResult::Commit(drawable) => { @@ -1171,6 +1182,7 @@ impl Component for SketchBoard { let mut model = Self { renderer: FemtoVGArea::default(), active_tool: tools.get(&config.initial_tool()), + tool_edit_mode: false, style: Style::default(), tools, im_context, diff --git a/src/tools/arrow.rs b/src/tools/arrow.rs index 7d6547c..b918708 100644 --- a/src/tools/arrow.rs +++ b/src/tools/arrow.rs @@ -37,6 +37,10 @@ impl Tool for ArrowTool { self.input_enabled = value; } + fn active(&self) -> bool { + self.arrow.is_some() + } + fn get_tool_type(&self) -> super::Tools { Tools::Arrow } @@ -107,9 +111,19 @@ impl Tool for ArrowTool { } fn handle_key_event(&mut self, event: crate::sketch_board::KeyEventMsg) -> ToolUpdateResult { - if event.key == Key::Escape && self.arrow.is_some() { - self.arrow = None; - ToolUpdateResult::Redraw + if let Some(arrow) = &self.arrow { + match event.key { + Key::Escape => { + self.arrow = None; + ToolUpdateResult::Redraw + } + Key::Return if arrow.end.is_some() => { + let result = arrow.clone_box(); + self.arrow = None; + ToolUpdateResult::Commit(result) + } + _ => ToolUpdateResult::Unmodified, + } } else { ToolUpdateResult::Unmodified } diff --git a/src/tools/blur.rs b/src/tools/blur.rs index 7bda6d4..35f8480 100644 --- a/src/tools/blur.rs +++ b/src/tools/blur.rs @@ -161,6 +161,10 @@ impl Tool for BlurTool { self.input_enabled = value; } + fn active(&self) -> bool { + self.blur.is_some() + } + fn get_tool_type(&self) -> super::Tools { Tools::Blur } @@ -227,9 +231,20 @@ impl Tool for BlurTool { } fn handle_key_event(&mut self, event: crate::sketch_board::KeyEventMsg) -> ToolUpdateResult { - if event.key == Key::Escape && self.blur.is_some() { - self.blur = None; - ToolUpdateResult::Redraw + if let Some(blur) = &mut self.blur { + match event.key { + Key::Escape => { + self.blur = None; + ToolUpdateResult::Redraw + } + Key::Return if blur.size.is_some() => { + blur.editing = false; + let result = blur.clone_box(); + self.blur = None; + ToolUpdateResult::Commit(result) + } + _ => ToolUpdateResult::Unmodified, + } } else { ToolUpdateResult::Unmodified } diff --git a/src/tools/crop.rs b/src/tools/crop.rs index 69c1954..f871f40 100644 --- a/src/tools/crop.rs +++ b/src/tools/crop.rs @@ -406,6 +406,14 @@ impl CropTool { } impl Tool for CropTool { + fn active(&self) -> bool { + if let Some(c) = &self.crop { + c.active + } else { + false + } + } + fn input_enabled(&self) -> bool { self.input_enabled } diff --git a/src/tools/ellipse.rs b/src/tools/ellipse.rs index 9c6d9cf..e8c120e 100644 --- a/src/tools/ellipse.rs +++ b/src/tools/ellipse.rs @@ -118,6 +118,10 @@ impl Tool for EllipseTool { self.input_enabled = value; } + fn active(&self) -> bool { + self.ellipse.is_some() + } + fn get_tool_type(&self) -> super::Tools { Tools::Ellipse } @@ -182,9 +186,20 @@ impl Tool for EllipseTool { } fn handle_key_event(&mut self, event: crate::sketch_board::KeyEventMsg) -> ToolUpdateResult { - if event.key == Key::Escape && self.ellipse.is_some() { - self.ellipse = None; - ToolUpdateResult::Redraw + if let Some(ellipse) = &mut self.ellipse { + match event.key { + Key::Escape => { + self.ellipse = None; + ToolUpdateResult::Redraw + } + Key::Return if ellipse.radii.is_some() => { + ellipse.finishing = true; + let result = ellipse.clone_box(); + self.ellipse = None; + ToolUpdateResult::Commit(result) + } + _ => ToolUpdateResult::Unmodified, + } } else { ToolUpdateResult::Unmodified } diff --git a/src/tools/highlight.rs b/src/tools/highlight.rs index db927d0..2d8069a 100644 --- a/src/tools/highlight.rs +++ b/src/tools/highlight.rs @@ -164,6 +164,10 @@ impl Tool for HighlightTool { self.input_enabled = value; } + fn active(&self) -> bool { + self.highlighter.is_some() + } + fn get_tool_type(&self) -> super::Tools { Tools::Highlight } @@ -296,9 +300,19 @@ impl Tool for HighlightTool { } fn handle_key_event(&mut self, event: crate::sketch_board::KeyEventMsg) -> ToolUpdateResult { - if event.key == Key::Escape && self.highlighter.is_some() { - self.highlighter = None; - return ToolUpdateResult::Redraw; + if self.highlighter.is_some() { + match event.key { + Key::Escape => { + self.highlighter = None; + return ToolUpdateResult::Redraw; + } + Key::Return => { + let result = self.highlighter.as_ref().unwrap().clone_box(); + self.highlighter = None; + return ToolUpdateResult::Commit(result); + } + _ => {} + } } ToolUpdateResult::Unmodified } diff --git a/src/tools/line.rs b/src/tools/line.rs index 1222d52..7d6f55b 100644 --- a/src/tools/line.rs +++ b/src/tools/line.rs @@ -63,6 +63,10 @@ impl Tool for LineTool { self.input_enabled = value; } + fn active(&self) -> bool { + self.line.is_some() + } + fn handle_mouse_event(&mut self, event: MouseEventMsg) -> ToolUpdateResult { match event.type_ { MouseEventType::BeginDrag => { @@ -125,9 +129,19 @@ impl Tool for LineTool { } fn handle_key_event(&mut self, event: crate::sketch_board::KeyEventMsg) -> ToolUpdateResult { - if event.key == Key::Escape && self.line.is_some() { - self.line = None; - ToolUpdateResult::Redraw + if let Some(line) = &self.line { + match event.key { + Key::Escape => { + self.line = None; + ToolUpdateResult::Redraw + } + Key::Return if line.direction.is_some() => { + let result = line.clone_box(); + self.line = None; + ToolUpdateResult::Commit(result) + } + _ => ToolUpdateResult::Unmodified, + } } else { ToolUpdateResult::Unmodified } diff --git a/src/tools/rectangle.rs b/src/tools/rectangle.rs index 0b7b5cb..f0a9fee 100644 --- a/src/tools/rectangle.rs +++ b/src/tools/rectangle.rs @@ -117,6 +117,10 @@ impl Tool for RectangleTool { self.input_enabled = value; } + fn active(&self) -> bool { + self.rectangle.is_some() + } + fn handle_mouse_event(&mut self, event: MouseEventMsg) -> ToolUpdateResult { match event.type_ { MouseEventType::BeginDrag => { @@ -176,9 +180,20 @@ impl Tool for RectangleTool { } fn handle_key_event(&mut self, event: crate::sketch_board::KeyEventMsg) -> ToolUpdateResult { - if event.key == Key::Escape && self.rectangle.is_some() { - self.rectangle = None; - ToolUpdateResult::Redraw + if let Some(rectangle) = &mut self.rectangle { + match event.key { + Key::Escape => { + self.rectangle = None; + ToolUpdateResult::Redraw + } + Key::Return if rectangle.size.is_some() => { + rectangle.finishing = true; + let result = rectangle.clone_box(); + self.rectangle = None; + ToolUpdateResult::Commit(result) + } + _ => ToolUpdateResult::Unmodified, + } } else { ToolUpdateResult::Unmodified } diff --git a/src/ui/toolbars.rs b/src/ui/toolbars.rs index 585df84..4bca782 100644 --- a/src/ui/toolbars.rs +++ b/src/ui/toolbars.rs @@ -62,6 +62,7 @@ pub enum ToolsToolbarInput { SetVisibility(bool), ToggleVisibility, SwitchSelectedTool(Tools), + SetToolEditing(bool), } #[derive(Debug, Copy, Clone)] @@ -298,10 +299,22 @@ impl SimpleComponent for ToolsToolbar { // Change state of action, let GTK update the UI self.tool_action.change_state(&tool.to_variant()); + if let Some(button) = self.active_button.as_ref() { + button.remove_css_class("editing"); + } if let Some(selected_tool_button) = self.tool_buttons.get(&tool) { self.active_button = Some(selected_tool_button.clone()); } } + ToolsToolbarInput::SetToolEditing(editing) => { + if let Some(button) = self.active_button.as_ref() { + if editing { + button.add_css_class("editing"); + } else { + button.remove_css_class("editing"); + } + } + } } } @@ -319,6 +332,10 @@ impl SimpleComponent for ToolsToolbar { sender_tmp .output_sender() .emit(ToolbarEvent::ToolSelected(*state)); + // also change tracked active button + sender_tmp + .input_sender() + .emit(ToolsToolbarInput::SwitchSelectedTool(*state)) }, );