From 12e17a1f648e31fbcc831536d144639b0279f503 Mon Sep 17 00:00:00 2001 From: alehel Date: Mon, 2 Nov 2026 10:52:29 +0100 Subject: [PATCH] refactor(ui): crop mode's hit is a hit test of its own MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Crop mode owns every press on its page, the controls beside its frame included (0595d4d39). `PageHits::at` made that hit itself. A `Hit` comes from a module's hit test, named `hit` or `…_hit`, so the cursor, the tip and the press agree, and the Page presses guard holds the page modules to it. The picture module's `crop_hit` makes it now, and `PageHits::at` asks it first, as before. No change in behaviour. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/canvas.rs | 12 +++--------- crates/ui-egui/src/tools/edit_text_ui.rs | 3 ++- crates/ui-egui/src/tools/edit_text_ui/image.rs | 8 ++++++++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 56d2674213..b281d044a5 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -1804,18 +1804,12 @@ impl PageHits<'_> { images, clip, } = *self; - // Crop mode owns presses on its page, including the controls beside its - // frame. Underlying paragraphs and the empty-page marquee must not take - // a crop button's press before the image module receives it. + // Crop mode owns every press on its page (`edit_text_ui::crop_hit`). if tool == QuickTool::EditText && can_modify - && let Some(selection) = view.image_selection.as_ref().filter(|s| s.page == page && s.crop.is_some()) + && let Some(crop) = crate::edit_text_ui::crop_hit(view, page) { - return vec![crate::page_router::Hit::new( - crate::page_router::Owner::Picture { index: selection.index, part: crate::page_router::PicturePart::Body }, - crate::page_cursor::Layer::Handle, - egui::CursorIcon::Default, - )]; + return vec![crop]; } let fields = if preparing { crate::prepare::hit(xf, info, page, form, field_tool, prepare_allowed, &view.prepare, p) diff --git a/crates/ui-egui/src/tools/edit_text_ui.rs b/crates/ui-egui/src/tools/edit_text_ui.rs index 19a165c152..9100c6d603 100644 --- a/crates/ui-egui/src/tools/edit_text_ui.rs +++ b/crates/ui-egui/src/tools/edit_text_ui.rs @@ -23,7 +23,8 @@ mod layout; pub(crate) use background::TextBackground; pub use image::{CropMode, DragTarget, ImageAction, ImageDrag, ImageHandle, ImageSelection, image_menu_id}; pub(crate) use image::{ - ImageCx, Matrix, body_hit as picture_hit, handle_hit as picture_handle_hit, image_context_menu, image_input, quad_contains, user_to_screen, + ImageCx, Matrix, body_hit as picture_hit, crop_hit, handle_hit as picture_handle_hit, image_context_menu, image_input, quad_contains, + user_to_screen, }; #[cfg(test)] use image::{rotate_knob, turn_degrees, turn_degrees_delta}; diff --git a/crates/ui-egui/src/tools/edit_text_ui/image.rs b/crates/ui-egui/src/tools/edit_text_ui/image.rs index 281f7de80e..53d3fb1483 100644 --- a/crates/ui-egui/src/tools/edit_text_ui/image.rs +++ b/crates/ui-egui/src/tools/edit_text_ui/image.rs @@ -519,6 +519,14 @@ pub(crate) fn handle_hit( Some(Hit::new(picture(PicturePart::Corner(grip)), Layer::Handle, obb.handle_cursor(handle))) } +/// The picture being cropped on `page`: crop mode owns every press there, the controls beside its +/// frame included, so the paragraphs under it and the empty page's marquee never take a crop +/// button's press before the picture does. `None` when no picture on `page` is being cropped. +pub(crate) fn crop_hit(view: &DocView, page: usize) -> Option { + let selection = view.image_selection.as_ref().filter(|s| s.page == page && s.crop.is_some())?; + Some(Hit::new(Owner::Picture { index: selection.index, part: PicturePart::Body }, Layer::Handle, egui::CursorIcon::Default)) +} + /// The picture under `p` (its shape, not only its box): a click selects it, a drag moves the /// selected one. On the text layer, asked before the paragraphs: editable text on top of a /// picture takes the press.