From 85fcbabcb16ab34cef692057654b85f7ee09ced8 Mon Sep 17 00:00:00 2001 From: gossardla Date: Wed, 14 Oct 2026 22:03:52 +0200 Subject: [PATCH] Ribbon: holding a press no longer scrolls the document (#1019) (#1041) * test(engine): drop a needless drop() that clippy 1.97 rejects (drop_non_drop) * Ribbon: a held press no longer scrolls the document (#1019) - Double-click release paths in the canvas returned before ending the text drag - The stale drag made any later press drive the edge auto-scroll - A drag now ends once the button is up, wherever it was released - Test: header double-click, then a held press on the ribbon --------- Co-authored-by: Brandon Thomas --- ROADMAP.md | 4 ++- crates/ui-egui/src/canvas.rs | 8 +++++- crates/ui-egui/src/lib.rs | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 9f28bb38a..eb8d358ca 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,7 +2,7 @@ **Stage: alpha** · next: beta, ~15 points (60% → 75% ready for real work) and ~150–230 h away -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** trivial (title rename and account settings; previously trivial: progress log: Down and Up no longer skip a table, #1007; previously trivial: progress log: privacy options on save, #1062; previously trivial: progress log: equation pictures in theme colours on Windows, #1002; previously trivial: symbol fonts, #776; previously trivial: the save prompt answers from the keyboard, #861; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously trivial: formatting revisions landed, #41; previously trivial: Draw tab ink, #307, and Draw Table and Eraser, #303, landed: catalog 402/434; previously minor: readiness table with hours per audience; full number back to the additive weighted sum, 60%; mainstream and essentials numbers added; alpha gate checked; previously major: full re-measure against Word for Mac 16.113.4; restructured to the craftrules progress-docs standard) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** trivial (progress log: a press on the ribbon no longer scrolls the document, #1019; previously trivial: title rename and account settings; previously trivial: progress log: Down and Up no longer skip a table, #1007; previously trivial: progress log: privacy options on save, #1062; previously trivial: progress log: equation pictures in theme colours on Windows, #1002; previously trivial: symbol fonts, #776; previously trivial: the save prompt answers from the keyboard, #861; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously trivial: formatting revisions landed, #41; previously trivial: Draw tab ink, #307, and Draw Table and Eraser, #303, landed: catalog 402/434; previously minor: readiness table with hours per audience; full number back to the additive weighted sum, 60%; mainstream and essentials numbers added; alpha gate checked; previously major: full re-measure against Word for Mac 16.113.4; restructured to the craftrules progress-docs standard) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 > **Printing update:** 2026-10-12 · direct desktop printing through craft-print; details below. @@ -137,6 +137,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm | Date | What landed | |---|---| | 2026-10-12 | Page background pictures and textures (Design › Page Color › Fill Effects) are drawn (tiled or stretched) and kept on save instead of being dropped from the package (#999); PDF export paints them too | +| 2026-10-12 | Holding a click on the ribbon or toolbar no longer scrolls the document up (#1019): a double-click in the header or footer area left a text drag running, so the next press anywhere drove the edge auto-scroll; a drag now ends with the button, wherever it's released | | 2026-10-12 | Document title opens Rename Document; the account badge opens Options › General for user name and initials. `file.rename {name}` preserves file extensions and refuses an existing destination; unsaved names become the suggested save name without changing the Title property. | | 2026-10-13 | Rounded rectangles (#1476): a `roundRect` keeps the corner radius its file gives it (`adj`), on screen, in PDF and when saved; all were drawn with a 16% corner | | 2026-10-13 | PAGEREF in headers and footers (#1477): "Page X of Y" with Y a page reference to a bookmark at each letter's end shows each letter's own total instead of the result stored in the file | @@ -332,6 +333,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm | Date | Change | Summary | |---|---|---| +| 2026-10-12 | trivial | Progress log: a press on the ribbon no longer scrolls the document (#1019) | | 2026-10-12 | trivial | Document title opens Rename Document; the account badge opens Options › General for user name and initials. | | 2026-10-13 | trivial | Progress log: rounded rectangle corners (#1476) | | 2026-10-13 | trivial | Progress log: PAGEREF in headers and footers (#1477) | diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 9888f66eb..ec820514f 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -874,7 +874,13 @@ pub fn show(app: &mut WordApp, ui: &mut Ui) { }; // Dragging a selection near the viewport's edge scrolls on (#688): from a crawl at the margin // to full speed at the edge, through the same offset the wheel writes. - let (down, pointer, dt) = ui.input(|i| (i.pointer.primary_down(), i.pointer.latest_pos(), i.stable_dt)); + let (down, released, pointer, dt) = ui.input(|i| (i.pointer.primary_down(), i.pointer.primary_released(), i.pointer.latest_pos(), i.stable_dt)); + // A release `mouse` returned before seeing (double-click paths, off the canvas) ends the drag here (#1019). + if app.canvas.dragging && !down && !released { + app.canvas.dragging = false; + app.canvas.column_drag = None; + app.canvas.unit_drag = None; + } let auto_delta = if !app.session.view.read_mode && app.canvas.drag_selecting() && down diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index af91b3f4e..8682ada1f 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -3153,6 +3153,60 @@ mod tests { assert!(!a.session.sel.is_collapsed(), "the drag left a selection"); } + /// #1019: a double-click in the header area returned before the release ended the drag, so + /// a later press held on the ribbon edge-scrolled the document up. + #[test] + fn a_press_on_the_ribbon_after_a_header_double_click_does_not_scroll() { + let ctx = egui::Context::default(); + let mut a = app(); + a.run("insert.editHeader", json!({})).unwrap(); + a.run("text.insert", json!({"text": "Header"})).unwrap(); + a.run("insert.closeHeader", json!({})).unwrap(); + a.run("text.insert", json!({"text": "lorem ipsum dolor sit amet consectetur ".repeat(1200)})).unwrap(); + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1280.0, 600.0)); + let mut t = 0.0; + let mut frame = |a: &mut WordApp, events: Vec| { + t += 1.0 / 60.0; + let input = egui::RawInput { events, time: Some(t), screen_rect: Some(screen), ..Default::default() }; + ctx.run_ui(input, |ui| { + a.logic(ui.ctx()); + a.ui(ui); + }) + .drop_without_applying_deltas(); + }; + let btn = |pos, pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; + for _ in 0..30 { + frame(&mut a, Vec::new()); + } + a.run("caret.docStart", json!({})).unwrap(); + for _ in 0..30 { + frame(&mut a, Vec::new()); + } + let (Some(vp), Some(page)) = (a.canvas.viewport, a.canvas.page_rects.first().copied()) else { panic!("the canvas laid out") }; + let header = egui::pos2(page.left() + 150.0, page.top() + 70.0); + assert!(vp.contains(header)); + frame(&mut a, vec![egui::Event::PointerMoved(header)]); + for _ in 0..2 { + frame(&mut a, vec![btn(header, true)]); + frame(&mut a, vec![btn(header, false)]); + } + frame(&mut a, Vec::new()); + assert!(!a.canvas.drag_selecting(), "the double-click's release ended the drag"); + a.run("insert.closeHeader", json!({})).unwrap(); + a.run("caret.docEnd", json!({})).unwrap(); + for _ in 0..30 { + frame(&mut a, Vec::new()); + } + let ribbon = egui::pos2(vp.center().x, vp.min.y - 40.0); + let before = a.canvas.scroll_offset; + frame(&mut a, vec![egui::Event::PointerMoved(ribbon), btn(ribbon, true)]); + for _ in 0..30 { + frame(&mut a, vec![egui::Event::PointerMoved(ribbon)]); + } + assert_eq!(a.canvas.scroll_offset, before, "holding a press on the ribbon leaves the document where it is"); + frame(&mut a, vec![btn(ribbon, false)]); + } + /// The control channel's `ui.wheel` scrolls the canvas like a mouse wheel over it. #[test] fn control_wheel_scrolls_the_canvas() {