diff --git a/crates/ui-egui/src/app.rs b/crates/ui-egui/src/app.rs index 43a1e5ce1d..af588e6aaf 100644 --- a/crates/ui-egui/src/app.rs +++ b/crates/ui-egui/src/app.rs @@ -591,9 +591,15 @@ impl eframe::App for PdfCraftApp { // opening a modal/palette, or returning to a window that lost focus. let blocked = self.modal_open() || self.palette_open || !ctx.input(|i| i.focused); for (index, view) in self.views.iter_mut().enumerate() { - view.objects.set_input_blocked(blocked || self.active != Some(index)); - if blocked || self.active != Some(index) { + let shut = blocked || self.active != Some(index); + view.objects.set_input_blocked(shut); + if shut { view.auto_scroll.cancel(); + // Edit text ignores input while it is blocked: its drags end rather than wait, to + // land wherever the pointer is once it opens again. + if self.quick_tool == QuickTool::EditText { + view.cancel_edit_text_gestures(); + } } } // A field that refused its value this frame keeps the shortcut from saving or printing diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 6855850615..7e77d473be 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -890,6 +890,18 @@ impl DocView { } } + /// Abandon Edit text's pointer gestures (a paragraph moved or rewrapped, a picture moved, + /// resized, turned or cropped, added content moved, the objects' area selection or move), as + /// [`Self::cancel_pointer_gestures`] does: what they were previewing is not applied. + pub(crate) fn cancel_edit_text_gestures(&mut self) { + self.block_drag = None; + self.content.cancel_pointer_gesture(); + self.objects.cancel_pointer_gesture(); + if let Some(image) = &mut self.image_selection { + image.cancel_pointer_gesture(); + } + } + /// A view of a newly opened document, shown as `defaults` say. Opening a file then applies /// any layout or zoom the PDF itself asks for. pub fn new(id: DocId, info: &DocInfo, defaults: ViewDefaults) -> Self { diff --git a/crates/ui-egui/tests/it/editing.rs b/crates/ui-egui/tests/it/editing.rs index f8c56ae6ab..59de9d1374 100644 --- a/crates/ui-egui/tests/it/editing.rs +++ b/crates/ui-egui/tests/it/editing.rs @@ -7419,6 +7419,36 @@ fn an_add_text_click_pressed_beside_a_paragraph_starts_a_box_there() { assert!(h.state().views[0].line_editor.is_none(), "the paragraph stays closed"); } +/// A paragraph drag in Edit text ends when Edit text's input is blocked (the window loses focus), as +/// a drag of the objects selected together does: nothing moves once the input comes back. It +/// waited, and moved the paragraph to wherever the pointer then was. +#[test] +fn a_paragraph_drag_ends_when_the_window_loses_focus() { + let mut h = edit_page(pdfcraft_ui_egui::QuickTool::EditText); + let id = h.state().views[0].id; + let before = h.state().session.get(id).unwrap().text_blocks(0)[0].rect; + let from = point_on(&h, (before[0] + before[2]) / 2.0, (before[1] + before[3]) / 2.0); + h.hover_at(from); + h.run_steps(1); + h.drag_at(from); + h.run_steps(1); + for k in 1..=4 { + h.hover_at(from + egui::vec2(0.0, 10.0 * k as f32)); + h.run_steps(1); + } + assert!(h.state().views[0].block_drag.is_some(), "the paragraph is being moved"); + h.input_mut().focused = false; + h.run_steps(2); + h.drop_at(from + egui::vec2(0.0, 40.0)); + h.run_steps(2); + h.input_mut().focused = true; + h.hover_at(from + egui::vec2(0.0, 80.0)); + h.run_steps(4); + assert!(h.state().views[0].block_drag.is_none(), "the drag is over"); + assert_eq!(h.state().session.get(id).unwrap().text_blocks(0)[0].rect, before, "nothing moved"); + assert!(!dirty(&h)); +} + /// The rotate knob of a picture that fills its page lies past the page's edge, and a drag there /// still turns the picture: the press is the page's whose knob it is. #[test]