diff --git a/crates/ui-egui/src/prepare.rs b/crates/ui-egui/src/prepare.rs index 22cd4e640..13767e55f 100644 --- a/crates/ui-egui/src/prepare.rs +++ b/crates/ui-egui/src/prepare.rs @@ -569,7 +569,8 @@ pub(crate) fn delete_selected(view: &mut DocView) -> Option { /// as the object nudge does (`object_ui::nudge_keys`) and as dragging it does (#2608). The keys /// are consumed before the view turns the page with them; one press is one undo step. pub(crate) fn nudge_keys(ctx: &egui::Context, view: &mut DocView, doc: &Document) { - if ctx.egui_wants_keyboard_input() || view.prepare.grab.is_some() { + // `consume_key` lets an extra Alt through, and Alt+↑/↓ belongs to Calculation order's move. + if ctx.egui_wants_keyboard_input() || view.prepare.grab.is_some() || ctx.input(|i| i.modifiers.alt) { return; } let Some((name, wi)) = view.prepare.selected.clone() else { return }; diff --git a/crates/ui-egui/tests/content.rs b/crates/ui-egui/tests/content.rs index 2c2ad4c6b..d75de6118 100644 --- a/crates/ui-egui/tests/content.rs +++ b/crates/ui-egui/tests/content.rs @@ -713,7 +713,7 @@ fn mode_tab(h: &Harness<'static, PdfCraftApp>, label: &str) -> egui::Rect { } /// Scroll the page until `what` (on screen) lies across the mode bar, then the cursor over each -/// tab it lies under: with the All tools panel closed, Read, Edit, Convert and E-Sign. +/// tab it lies under: with the All tools panel closed, every tab: All tools, Edit, Convert and E-Sign. fn mode_tab_cursors( h: &mut Harness<'static, PdfCraftApp>, what: impl Fn(&Harness<'static, PdfCraftApp>) -> egui::Rect, @@ -727,7 +727,7 @@ fn mode_tab_cursors( h.state_mut().views[0].goto = Some((0, (top - (tabs - y)) / page(h).height())); h.run_steps(4); let under = what(h); - ["Read", "Edit", "Convert", "E-Sign"].map(|label| { + ["All tools", "Edit", "Convert", "E-Sign"].map(|label| { let at = mode_tab(h, label).center(); assert!(under.contains(at), "{under:?} lies under {label} at {at:?}"); h.hover_at(at); @@ -737,7 +737,7 @@ fn mode_tab_cursors( } const HAND_ON_EVERY_TAB: [(&str, egui::CursorIcon); 4] = [ - ("Read", egui::CursorIcon::PointingHand), + ("All tools", egui::CursorIcon::PointingHand), ("Edit", egui::CursorIcon::PointingHand), ("Convert", egui::CursorIcon::PointingHand), ("E-Sign", egui::CursorIcon::PointingHand), diff --git a/crates/ui-egui/tests/editing.rs b/crates/ui-egui/tests/editing.rs index ebb910a3d..b0136a2bd 100644 --- a/crates/ui-egui/tests/editing.rs +++ b/crates/ui-egui/tests/editing.rs @@ -5879,12 +5879,13 @@ fn mode_tab(h: &Harness<'static, PdfCraftApp>, label: &str) -> egui::Rect { } /// The cursor over each mode bar tab that page 1's text lies under, once that text is scrolled -/// up under the bar. With the All tools panel closed it lies under Read, Edit, Convert and E-Sign. -fn mode_tab_cursors(h: &mut Harness<'static, PdfCraftApp>, text: egui::Rect) -> [(&'static str, egui::CursorIcon); 4] { +/// up under the bar. With the All tools panel closed it lies under Edit, Convert and E-Sign; it +/// starts right of All tools. +fn mode_tab_cursors(h: &mut Harness<'static, PdfCraftApp>, text: egui::Rect) -> [(&'static str, egui::CursorIcon); 3] { h.state_mut().views[0].goto = Some((0, 0.5)); h.run_steps(4); let offset = h.state().views[0].page_screen_rect(0).expect("on screen").top(); - ["Read", "Edit", "Convert", "E-Sign"].map(|label| { + ["Edit", "Convert", "E-Sign"].map(|label| { let at = mode_tab(h, label).center(); assert!(text.translate(egui::vec2(0.0, offset)).contains(at), "the text lies under {label}"); h.hover_at(at); @@ -5893,12 +5894,8 @@ fn mode_tab_cursors(h: &mut Harness<'static, PdfCraftApp>, text: egui::Rect) -> }) } -const HAND_ON_EVERY_TAB: [(&str, egui::CursorIcon); 4] = [ - ("Read", egui::CursorIcon::PointingHand), - ("Edit", egui::CursorIcon::PointingHand), - ("Convert", egui::CursorIcon::PointingHand), - ("E-Sign", egui::CursorIcon::PointingHand), -]; +const HAND_ON_EVERY_TAB: [(&str, egui::CursorIcon); 3] = + [("Edit", egui::CursorIcon::PointingHand), ("Convert", egui::CursorIcon::PointingHand), ("E-Sign", egui::CursorIcon::PointingHand)]; /// Page 1's text block on screen, relative to the page's top edge, in the window as it is now. fn page_text(h: &Harness<'static, PdfCraftApp>) -> egui::Rect { diff --git a/crates/ui-egui/tests/fill_sign.rs b/crates/ui-egui/tests/fill_sign.rs index 7a54fc2ca..61fb5fe6d 100644 --- a/crates/ui-egui/tests/fill_sign.rs +++ b/crates/ui-egui/tests/fill_sign.rs @@ -1331,6 +1331,13 @@ fn lines_grow_and_shrink_with_their_handles_before_release() { let _gpu = gpu(); for (document_rotation, view_rotation) in [(0, 0), (90, 0), (0, 90), (90, 270)] { let rot = (document_rotation, view_rotation); + // A line runs across the page as /Rotate displays it (#3781): along user y on a quarter turn. + // `d` is the distance along the line from the click at (100, 250). + let along = + |h: &Harness<'static, PdfCraftApp>, d: f32| if document_rotation == 90 { at(h, 100.0, 250.0 + d) } else { at(h, 100.0 + d, 250.0) }; + let line = |from: f32, to: f32| { + if document_rotation == 90 { [98.0, 250.0 + from, 102.0, 250.0 + to] } else { [100.0 + from, 248.0, 100.0 + to, 252.0] } + }; let mut h = harness(); if document_rotation != 0 { h.state_mut().apply_edit(pdfcraft_engine::Edit::RotatePages { pages: vec![0], degrees: document_rotation }); @@ -1341,36 +1348,36 @@ fn lines_grow_and_shrink_with_their_handles_before_release() { h.run_steps(3); assert!(h.state_mut().execute("sign.fill.line")); click(&mut h, 100.0, 250.0); - assert_rect(rects(&h)[0], [82.0, 248.0, 118.0, 252.0]); + assert_rect(rects(&h)[0], line(-18.0, 18.0)); // In line mode a click on the line picks it up, with its handles. click(&mut h, 100.0, 250.0); assert_eq!(h.state().views[0].comments.selected, Some((0, 0)), "{rot:?}"); let generation = h.state().session.get(h.state().views[0].id).unwrap().edit_generation(); - // The handle at the line's end, dragged out to 160. - h.drag_at(at(&h, 118.0, 250.0)); + // The handle at the line's end, dragged out to 60 from the middle. + h.drag_at(along(&h, 18.0)); h.run_steps(1); - h.hover_at(at(&h, 160.0, 250.0)); + h.hover_at(along(&h, 60.0)); // Wait only for the one-time layers. Later pointer frames are immediate. let mut grown = h.render().unwrap(); for _ in 0..50 { - if ink_near(&grown, at(&h, 150.0, 250.0)) { + if ink_near(&grown, along(&h, 50.0)) { break; } h.run_steps(2); std::thread::sleep(std::time::Duration::from_millis(10)); grown = h.render().unwrap(); } - assert!(ink_near(&grown, at(&h, 150.0, 250.0)), "{rot:?}: the line grows with the handle before release"); - assert!(ink_near(&grown, at(&h, 90.0, 250.0)), "{rot:?}: from where it starts"); + assert!(ink_near(&grown, along(&h, 50.0)), "{rot:?}: the line grows with the handle before release"); + assert!(ink_near(&grown, along(&h, -10.0)), "{rot:?}: from where it starts"); // Back in, shorter than it was, still without letting go. - h.hover_at(at(&h, 100.0, 250.0)); + h.hover_at(along(&h, 0.0)); h.run_steps(2); let shrunk = h.render().unwrap(); - assert!(ink_near(&shrunk, at(&h, 90.0, 250.0)), "{rot:?}: the line still starts where it did"); - for x in [110.0, 150.0] { - assert!(!ink_near(&shrunk, at(&h, x, 250.0)), "{rot:?}: the line shrinks with the handle before release (ink at {x})"); + assert!(ink_near(&shrunk, along(&h, -10.0)), "{rot:?}: the line still starts where it did"); + for d in [10.0, 50.0] { + assert!(!ink_near(&shrunk, along(&h, d)), "{rot:?}: the line shrinks with the handle before release (ink at {d})"); } let doc = h.state().session.get(h.state().views[0].id).unwrap(); assert_eq!((doc.edit_generation(), doc.can_undo()), (generation, Some("Add line")), "{rot:?}: pointer frames never edit the document"); @@ -1378,12 +1385,12 @@ fn lines_grow_and_shrink_with_their_handles_before_release() { grown.save(format!("{dir}/line-live-grow-{document_rotation}-{view_rotation}.png")).unwrap(); shrunk.save(format!("{dir}/line-live-shrink-{document_rotation}-{view_rotation}.png")).unwrap(); } - h.drop_at(at(&h, 100.0, 250.0)); + h.drop_at(along(&h, 0.0)); h.run_steps(3); let doc = h.state().session.get(h.state().views[0].id).unwrap(); assert_eq!(doc.can_undo(), Some("Resize comment"), "{rot:?}"); let [now] = rects(&h)[..] else { panic!("{rot:?}: still one line: {:?}", rects(&h)) }; - assert!(now.iter().zip([82.0, 248.0, 100.0, 252.0]).all(|(a, b)| (a - b).abs() < 0.01), "{rot:?}: {now:?}"); + assert!(now.iter().zip(line(-18.0, 0.0)).all(|(a, b)| (a - b).abs() < 0.01), "{rot:?}: {now:?}"); } } @@ -1667,7 +1674,7 @@ fn mode_tab(h: &Harness<'static, PdfCraftApp>, label: &str) -> egui::Rect { } /// Scroll the page until `what` (on screen) lies across the mode bar, then the cursor over each -/// tab it lies under: with the All tools panel closed, Read, Edit, Convert and E-Sign. +/// tab it lies under: with the All tools panel closed, every tab: All tools, Edit, Convert and E-Sign. fn mode_tab_cursors( h: &mut Harness<'static, PdfCraftApp>, what: impl Fn(&Harness<'static, PdfCraftApp>) -> egui::Rect, @@ -1681,7 +1688,7 @@ fn mode_tab_cursors( h.state_mut().views[0].goto = Some((0, (top - (tabs - y)) / page(h).height())); h.run_steps(4); let under = what(h); - ["Read", "Edit", "Convert", "E-Sign"].map(|label| { + ["All tools", "Edit", "Convert", "E-Sign"].map(|label| { let at = mode_tab(h, label).center(); assert!(under.contains(at), "{under:?} lies under {label} at {at:?}"); h.hover_at(at); @@ -1691,7 +1698,7 @@ fn mode_tab_cursors( } const HAND_ON_EVERY_TAB: [(&str, egui::CursorIcon); 4] = [ - ("Read", egui::CursorIcon::PointingHand), + ("All tools", egui::CursorIcon::PointingHand), ("Edit", egui::CursorIcon::PointingHand), ("Convert", egui::CursorIcon::PointingHand), ("E-Sign", egui::CursorIcon::PointingHand), diff --git a/crates/ui-egui/tests/forms.rs b/crates/ui-egui/tests/forms.rs index 1528d4c5e..efc27dc83 100644 --- a/crates/ui-egui/tests/forms.rs +++ b/crates/ui-egui/tests/forms.rs @@ -1282,11 +1282,12 @@ fn mode_tab(h: &Harness<'static, PdfCraftApp>, label: &str) -> egui::Rect { } /// Scroll the page until `what` (on screen) lies across the mode bar, then the cursor over each -/// tab it lies under: with the All tools panel closed, Read, Edit, Convert and E-Sign. +/// tab it lies under: with the All tools panel closed, Edit, Convert and E-Sign (the name field +/// starts right of All tools). fn mode_tab_cursors( h: &mut Harness<'static, PdfCraftApp>, what: impl Fn(&Harness<'static, PdfCraftApp>) -> egui::Rect, -) -> [(&'static str, egui::CursorIcon); 4] { +) -> [(&'static str, egui::CursorIcon); 3] { let page = |h: &Harness<'static, PdfCraftApp>| h.state().views[0].page_screen_rect(0).expect("on screen"); let y = what(h).center().y - page(h).top(); let tabs = mode_tab(h, "Convert").center().y; @@ -1296,7 +1297,7 @@ fn mode_tab_cursors( h.state_mut().views[0].goto = Some((0, (top - (tabs - y)) / page(h).height())); h.run_steps(4); let under = what(h); - ["Read", "Edit", "Convert", "E-Sign"].map(|label| { + ["Edit", "Convert", "E-Sign"].map(|label| { let at = mode_tab(h, label).center(); assert!(under.contains(at), "{under:?} lies under {label} at {at:?}"); h.hover_at(at); @@ -1305,12 +1306,8 @@ fn mode_tab_cursors( }) } -const HAND_ON_EVERY_TAB: [(&str, egui::CursorIcon); 4] = [ - ("Read", egui::CursorIcon::PointingHand), - ("Edit", egui::CursorIcon::PointingHand), - ("Convert", egui::CursorIcon::PointingHand), - ("E-Sign", egui::CursorIcon::PointingHand), -]; +const HAND_ON_EVERY_TAB: [(&str, egui::CursorIcon); 3] = + [("Edit", egui::CursorIcon::PointingHand), ("Convert", egui::CursorIcon::PointingHand), ("E-Sign", egui::CursorIcon::PointingHand)]; /// A text field scrolled up under the mode bar leaves its tabs the hand (#2755). The field gave /// them its I-beam, and its open editor floated over them and took their clicks. diff --git a/crates/ui-egui/tests/snapshots/panel_compare.txt b/crates/ui-egui/tests/snapshots/panel_compare.txt index a2d8cc5a6..2559b6d45 100644 --- a/crates/ui-egui/tests/snapshots/panel_compare.txt +++ b/crates/ui-egui/tests/snapshots/panel_compare.txt @@ -5,6 +5,9 @@ Label = "2 Replaced" Label = "0 Inserted" Label = "0 Deleted" Label = "1 Visual" +Label = "Show:" +CheckBox "Text" [on] +CheckBox "Visual" [on] Button "Side by side" Button "New file only" Button "Mark as comments" diff --git a/crates/ui-egui/tests/snapshots/panel_layers.txt b/crates/ui-egui/tests/snapshots/panel_layers.txt index 1e75f6bf6..004cf313b 100644 --- a/crates/ui-egui/tests/snapshots/panel_layers.txt +++ b/crates/ui-egui/tests/snapshots/panel_layers.txt @@ -1,4 +1,5 @@ Label = "Layers" Label = "1" Button "Close" [off] +Button "Layer options" [off] CheckBox "Draft notes" [on] diff --git a/crates/ui-egui/tests/snapshots/panel_pages.txt b/crates/ui-egui/tests/snapshots/panel_pages.txt index 726d58208..4e746f100 100644 --- a/crates/ui-egui/tests/snapshots/panel_pages.txt +++ b/crates/ui-egui/tests/snapshots/panel_pages.txt @@ -1,6 +1,11 @@ Label = "Pages" Label = "2" Button "Close" [off] +Label = "Thumbnail size" +Label = "100%" +Button "Smaller thumbnails" [off] +Slider "Thumbnail size" +Button "Larger thumbnails" [off] Button "Page 1" [off] Label = "1" Button "Page 2" [off]