From 922dfaf7e83ebdd634bff4197af98aabb8c47de4 Mon Sep 17 00:00:00 2001 From: prime-run Date: Tue, 16 Dec 2025 11:09:06 -0700 Subject: [PATCH 1/2] feat: display image dimentions (inside_crop dims when active) fmt to projec's defaults --- src/main.rs | 29 +++++++++++++++++++++++++++++ src/sketch_board.rs | 22 ++++++++++++++++++++++ src/tools/crop.rs | 23 ++++++++++++++++++++++- src/ui/toolbars.rs | 17 +++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index d18b520e..81d440f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,6 +64,8 @@ enum AppInput { ToggleToolbarsDisplay, ToolSwitchShortcut(Tools), ColorSwitchShortcut(u64), + CropDimensionsUpdate((i32, i32)), + CropToolActivated(bool), } #[derive(Debug)] @@ -226,6 +228,21 @@ impl Component for App { ui::toolbars::ColorButtons::Palette(index), )); } + AppInput::CropDimensionsUpdate((width, height)) => { + self.style_toolbar + .sender() + .emit(StyleToolbarInput::CropDimensionsChanged((width, height))); + } + AppInput::CropToolActivated(active) => { + if !active { + // Show full image dimensions when crop tool is deactivated + self.style_toolbar + .sender() + .emit(StyleToolbarInput::CropDimensionsChanged( + self.image_dimensions, + )); + } + } } } @@ -261,6 +278,12 @@ impl Component for App { SketchBoardOutput::ColorSwitchShortcut(index) => { AppInput::ColorSwitchShortcut(index) } + SketchBoardOutput::CropDimensionsUpdate(dimensions) => { + AppInput::CropDimensionsUpdate(dimensions) + } + SketchBoardOutput::CropToolActivated(active) => { + AppInput::CropToolActivated(active) + } }); // Toolbars @@ -280,6 +303,12 @@ impl Component for App { image_dimensions, }; + // Initialize style toolbar with full image dimensions + model + .style_toolbar + .sender() + .emit(StyleToolbarInput::CropDimensionsChanged(image_dimensions)); + let widgets = view_output!(); if APP_CONFIG.read().focus_toggles_toolbars() { diff --git a/src/sketch_board.rs b/src/sketch_board.rs index e7d539ff..3533ca61 100644 --- a/src/sketch_board.rs +++ b/src/sketch_board.rs @@ -35,6 +35,7 @@ pub enum SketchBoardInput { RenderResult(RenderedImage, Vec), CommitEvent(TextEventMsg), Refresh, + Output(SketchBoardOutput), } #[derive(Debug, Clone)] @@ -42,6 +43,8 @@ pub enum SketchBoardOutput { ToggleToolbarsDisplay, ToolSwitchShortcut(Tools), ColorSwitchShortcut(u64), + CropDimensionsUpdate((i32, i32)), + CropToolActivated(bool), } #[derive(Debug, Clone)] @@ -574,6 +577,15 @@ impl SketchBoard { ) -> ToolUpdateResult { match toolbar_event { ToolbarEvent::ToolSelected(tool) => { + let crop_was_active = self.active_tool.borrow().get_tool_type() == Tools::Crop; + let crop_is_active = tool == Tools::Crop; + + if crop_was_active != crop_is_active { + sender + .output_sender() + .emit(SketchBoardOutput::CropToolActivated(crop_is_active)); + } + // deactivate old tool and save drawable, if any let old_tool = self.active_tool.clone(); let mut deactivate_result = @@ -651,6 +663,12 @@ impl SketchBoard { ToolbarEvent::SaveFileAs => self.handle_action(&[Action::SaveToFileAs]), ToolbarEvent::Resize => self.handle_resize(), ToolbarEvent::OriginalScale => self.handle_original_scale(), + ToolbarEvent::CropDimensionsUpdated((w, h)) => { + sender + .output_sender() + .emit(SketchBoardOutput::CropDimensionsUpdate((w, h))); + ToolUpdateResult::Unmodified + } } } @@ -996,6 +1014,10 @@ impl Component for SketchBoard { ToolUpdateResult::Unmodified } SketchBoardInput::Refresh => ToolUpdateResult::Redraw, + SketchBoardInput::Output(output) => { + sender.output_sender().emit(output); + ToolUpdateResult::Unmodified + } }; // println!(" Result={:?}", result); diff --git a/src/tools/crop.rs b/src/tools/crop.rs index 48d67726..d53018a1 100644 --- a/src/tools/crop.rs +++ b/src/tools/crop.rs @@ -2,7 +2,10 @@ use std::f32::consts::PI; use crate::{ math::{self, Vec2D}, - sketch_board::{KeyEventMsg, MouseButton, MouseEventMsg, MouseEventType, SketchBoardInput}, + sketch_board::{ + KeyEventMsg, MouseButton, MouseEventMsg, MouseEventType, SketchBoardInput, + SketchBoardOutput, + }, }; use anyhow::Result; use femtovg::{Color, Paint, Path}; @@ -269,6 +272,19 @@ impl CropTool { crop.size = br - tl; } + fn emit_crop_dimensions_update(&self) { + if let (Some(crop), Some(sender)) = (&self.crop, &self.sender) { + let (_pos, size) = crop.get_rectangle(); + let width = size.x.round() as i32; + let height = size.y.round() as i32; + sender + .send(SketchBoardInput::Output( + SketchBoardOutput::CropDimensionsUpdate((width, height)), + )) + .ok(); + } + } + fn begin_drag(&mut self, pos: Vec2D) -> ToolUpdateResult { match &self.crop { None => { @@ -319,10 +335,12 @@ impl CropTool { match action { CropToolAction::NewCrop => { crop.size = direction; + self.emit_crop_dimensions_update(); ToolUpdateResult::Redraw } CropToolAction::DragHandle(state) => { Self::apply_drag_handle_transformation(crop, state, direction); + self.emit_crop_dimensions_update(); ToolUpdateResult::Redraw } CropToolAction::Move(state) => { @@ -347,16 +365,19 @@ impl CropTool { CropToolAction::NewCrop => { crop.size = direction; self.action = None; + self.emit_crop_dimensions_update(); ToolUpdateResult::Redraw } CropToolAction::DragHandle(state) => { Self::apply_drag_handle_transformation(crop, state, direction); self.action = None; + self.emit_crop_dimensions_update(); ToolUpdateResult::Redraw } CropToolAction::Move(state) => { crop.pos = state.start + direction; self.action = None; + self.emit_crop_dimensions_update(); ToolUpdateResult::Redraw } } diff --git a/src/ui/toolbars.rs b/src/ui/toolbars.rs index a6bb6504..32085606 100644 --- a/src/ui/toolbars.rs +++ b/src/ui/toolbars.rs @@ -33,6 +33,7 @@ pub struct StyleToolbar { annotation_size: f32, annotation_size_formatted: String, annotation_dialog_controller: Option>, + crop_dimensions: String, } pub struct AnnotationSizeDialog { @@ -54,6 +55,7 @@ pub enum ToolbarEvent { SaveFileAs, Resize, OriginalScale, + CropDimensionsUpdated((i32, i32)), } #[derive(Debug, Copy, Clone)] @@ -72,6 +74,7 @@ pub enum StyleToolbarInput { ToggleVisibility, ShowAnnotationDialog, AnnotationDialogFinished(Option), + CropDimensionsChanged((i32, i32)), } #[derive(Debug, Copy, Clone)] @@ -555,6 +558,16 @@ impl Component for StyleToolbar { connect_clicked => StyleToolbarInput::ShowAnnotationDialog }, gtk::Separator {}, + gtk::Label { + set_focusable: false, + set_hexpand: false, + set_margin_start: 10, + + #[watch] + set_text: &model.crop_dimensions, + set_tooltip: "Crop dimensions (width x height)", + }, + gtk::Separator {}, gtk::Button { set_focusable: false, set_hexpand: false, @@ -625,6 +638,9 @@ impl Component for StyleToolbar { StyleToolbarInput::ToggleVisibility => { self.visible = !self.visible; } + StyleToolbarInput::CropDimensionsChanged((width, height)) => { + self.crop_dimensions = format!("{}x{}", width, height); + } } } @@ -692,6 +708,7 @@ impl Component for StyleToolbar { APP_CONFIG.read().annotation_size_factor() ), annotation_dialog_controller: None, + crop_dimensions: String::new(), }; // create widgets From eab87205bfbded43d6f82437c23f4368160b2a9e Mon Sep 17 00:00:00 2001 From: prime-run Date: Tue, 16 Dec 2025 11:41:15 -0700 Subject: [PATCH 2/2] feat: Clear/Cancel crop selection on Esc press --- src/main.rs | 7 ++++++- src/sketch_board.rs | 4 ++-- src/tools/crop.rs | 19 ++++++++++++------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 81d440f0..b4c0c74e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -229,9 +229,14 @@ impl Component for App { )); } AppInput::CropDimensionsUpdate((width, height)) => { + let dimensions = if (width, height) == (0, 0) { + self.image_dimensions + } else { + (width, height) + }; self.style_toolbar .sender() - .emit(StyleToolbarInput::CropDimensionsChanged((width, height))); + .emit(StyleToolbarInput::CropDimensionsChanged(dimensions)); } AppInput::CropToolActivated(active) => { if !active { diff --git a/src/sketch_board.rs b/src/sketch_board.rs index 3533ca61..d30d3033 100644 --- a/src/sketch_board.rs +++ b/src/sketch_board.rs @@ -663,10 +663,10 @@ impl SketchBoard { ToolbarEvent::SaveFileAs => self.handle_action(&[Action::SaveToFileAs]), ToolbarEvent::Resize => self.handle_resize(), ToolbarEvent::OriginalScale => self.handle_original_scale(), - ToolbarEvent::CropDimensionsUpdated((w, h)) => { + ToolbarEvent::CropDimensionsUpdated((width, height)) => { sender .output_sender() - .emit(SketchBoardOutput::CropDimensionsUpdate((w, h))); + .emit(SketchBoardOutput::CropDimensionsUpdate((width, height))); ToolUpdateResult::Unmodified } } diff --git a/src/tools/crop.rs b/src/tools/crop.rs index d53018a1..437ff969 100644 --- a/src/tools/crop.rs +++ b/src/tools/crop.rs @@ -398,15 +398,20 @@ impl Tool for CropTool { } fn handle_key_event(&mut self, event: KeyEventMsg) -> ToolUpdateResult { - if event.key == Key::Escape { - if let Some(crop) = &self.crop { - if crop.active { - // Crop is active, deactivate it - return self.handle_deactivated(); - } + if event.key == Key::Escape && self.crop.is_some() { + self.crop = None; + self.action = None; + + if let Some(sender) = &self.sender { + sender + .send(SketchBoardInput::Output( + SketchBoardOutput::CropDimensionsUpdate((0, 0)), + )) + .ok(); } + + return ToolUpdateResult::Redraw; } - // No crop exists or crop is inactive - let event bubble to global handler ToolUpdateResult::Unmodified }