From 917411a3541d4ba33467d68d16d346a7e439091d Mon Sep 17 00:00:00 2001 From: Dan Green Date: Wed, 11 Nov 2026 15:15:22 -0800 Subject: [PATCH] Rulers: hiding the rulers during a drag of their origin cancels it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drag out of the rulers' corner was ended only by the rulers' own input, which the canvas skips while the rulers are hidden (View › Rulers, Presentation Mode). Hiding them mid-drag left the drag's snapping guides on the canvas and its state open until the rulers came back. The canvas now ends the drag, cancelled, while the rulers are hidden, through the same helper a release uses. --- crates/ui-egui/src/canvas.rs | 4 ++-- crates/ui-egui/src/canvas/grid.rs | 21 +++++++++++++++++---- crates/ui-egui/src/tests/ruler_origin.rs | 11 ++++++++++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 50136f1a9..6e6806e07 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -50,7 +50,7 @@ use fill::fill_uncovered; use fill::{fill_view, polygon_area}; #[cfg(test)] use grid::ruler_rects; -use grid::{LineLook, PIXEL_GRID, PIXEL_GRID_ZOOM, checker, clip_segment, grid, ruler_input, rulers, textured_quad, view_bounds}; +use grid::{LineLook, PIXEL_GRID, PIXEL_GRID_ZOOM, checker, clip_segment, grid, ruler_input, rulers, rulers_hidden, textured_quad, view_bounds}; pub(crate) use grid::{RulerScale, checker_texture, ruler_label, ruler_origin_dragging}; #[cfg(test)] pub(crate) use grid::{TRANSPARENCY_GRID, vertical_label_height}; @@ -283,7 +283,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { ui.ctx().request_repaint_after_secs(secs as f32); } let hover_still = wait.is_none(); - let origin_preview = if app.ui.view.rulers && app.ui.screen_mode < 3 { ruler_input(app, ui, full, rect, &xf) } else { None }; + let origin_preview = if app.ui.view.rulers && app.ui.screen_mode < 3 { ruler_input(app, ui, full, rect, &xf) } else { rulers_hidden(app, ui) }; crate::document_drag::drop_on_canvas(app, &resp, &xf); panel_drop(app, ui, &resp, &xf); // In Presentation Mode a right click or a long touch opens no context menu. diff --git a/crates/ui-egui/src/canvas/grid.rs b/crates/ui-egui/src/canvas/grid.rs index 0f6fc7d91..2c99c618d 100644 --- a/crates/ui-egui/src/canvas/grid.rs +++ b/crates/ui-egui/src/canvas/grid.rs @@ -285,8 +285,7 @@ pub(super) fn ruler_input(app: &mut VectorcraftApp, ui: &Ui, full: egui::Rect, c app.session.ruler_origin_drag(pointer, app.view_info()) }; if corner.drag_stopped_by(egui::PointerButton::Primary) { - ui.data_mut(|d| d.remove::<(Option, bool)>(drag_id)); - app.session.end_ruler_origin_drag(); + end_ruler_origin_drag(app, ui); if !cancelled && let Some(at) = at && let Err(e) = app.run("document.setRulerOrigin", json!({"x": at.x, "y": at.y, "artboard": app.view().map_or(0, |v| v.artboard)})) @@ -294,8 +293,7 @@ pub(super) fn ruler_input(app: &mut VectorcraftApp, ui: &Ui, full: egui::Rect, c app.status(e.to_string()); } } else if !ui.input(|i| i.pointer.primary_down()) { - ui.data_mut(|d| d.remove::<(Option, bool)>(drag_id)); - app.session.end_ruler_origin_drag(); + end_ruler_origin_drag(app, ui); } else { ui.data_mut(|d| d.insert_temp(drag_id, (started_in, cancelled))); if !cancelled { @@ -306,6 +304,21 @@ pub(super) fn ruler_input(app: &mut VectorcraftApp, ui: &Ui, full: egui::Rect, c preview } +/// The rulers are hidden (View › Rulers, Presentation Mode): a drag of their origin they were +/// showing for is cancelled, so its snapping guides don't stay on the canvas. Nothing previews. +pub(super) fn rulers_hidden(app: &mut VectorcraftApp, ui: &Ui) -> Option { + if ruler_origin_dragging(ui.ctx()) { + end_ruler_origin_drag(app, ui); + } + None +} + +/// Ends a drag from the rulers' corner, released or cancelled: its state and its snapping guides go. +fn end_ruler_origin_drag(app: &mut VectorcraftApp, ui: &Ui) { + ui.data_mut(|d| d.remove::<(Option, bool)>(ruler_origin_drag_id())); + app.session.end_ruler_origin_drag(); +} + /// The key of the ruler origin drag's state in egui's temporary data. fn ruler_origin_drag_id() -> egui::Id { egui::Id::unique("ruler-origin-drag") diff --git a/crates/ui-egui/src/tests/ruler_origin.rs b/crates/ui-egui/src/tests/ruler_origin.rs index 55e20df8c..5e93993fe 100644 --- a/crates/ui-egui/src/tests/ruler_origin.rs +++ b/crates/ui-egui/src/tests/ruler_origin.rs @@ -153,8 +153,9 @@ fn ruler_origin_cancelled_drags_do_not_modify_either_document() { Escape, Focus, Document, + HideRulers, } - for cancel in [Cancel::Outside, Cancel::Escape, Cancel::Focus, Cancel::Document] { + for cancel in [Cancel::Outside, Cancel::Escape, Cancel::Focus, Cancel::Document, Cancel::HideRulers] { let mut app = app(); app.run("document.setRulerOrigin", json!({"x": 25, "y": 30})).unwrap(); let ctx = context(); @@ -179,6 +180,14 @@ fn ruler_origin_cancelled_drags_do_not_modify_either_document() { app.run("file.new", json!({"width": 400, "height": 300})).unwrap(); to } + Cancel::HideRulers => { + assert!(!app.session.overlays(app.view_info()).is_empty(), "the snapping guides show"); + app.ui.view.rulers = false; + frame(&mut app, &ctx, vec![], canvas::show); + assert!(app.session.overlays(app.view_info()).is_empty(), "the snapping guides go with the rulers"); + assert!(!canvas::ruler_origin_dragging(&ctx)); + to + } }; frame(&mut app, &ctx, vec![Event::PointerMoved(release), press(release, false)], canvas::show); if cancel == Cancel::Document {