Comments: a click inside a selected balloon moves the caret in its text (#1013) (#1051)

* test(engine): drop a needless drop() that clippy 1.97 rejects (drop_non_drop)

* Comments: a click inside a selected balloon moves the caret in its text (#1013)

- The canvas under the markup area also "contains" the pointer, so a press on the balloon counted as a click on the page and left it
- Presses over a balloon card now belong to the card, in both the deselect check and the document click in mouse()
- Test: press, drag and release inside the selected balloon; a click on the page still leaves it

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
gossardla
2026-10-14 16:23:31 -04:00
committed by GitHub
co-authored by Brandon Thomas
parent a4870128b5
commit 896c1bbebf
3 changed files with 72 additions and 6 deletions
+3 -1
View File
@@ -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 (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
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** trivial (progress log: clicking in a comment's text moves its caret, #1013; previously 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.
@@ -136,6 +136,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
| Date | What landed |
|---|---|
| 2026-10-12 | Clicking inside a selected comment's balloon moves the caret in its text (#1013); the click used to leave the balloon, so the caret could only be moved with the arrow keys. A press on any balloon now belongs to the balloon, not the page behind it |
| 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 | Tracked insertions and deletions inside a field result (a `=SUM(ABOVE)` subtotal) are kept as revisions: Accept All and Reject All act on them and a save writes the `w:del` back (#1037) |
@@ -335,6 +336,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | trivial | Progress log: clicking in a comment's text moves its caret (#1013) |
| 2026-10-12 | trivial | Progress log: a press on the ribbon no longer scrolls the document (#1019) |
| 2026-10-12 | trivial | Progress log: Ctrl+drag copies an object (#1014) |
| 2026-10-12 | trivial | Document title opens Rename Document; the account badge opens Options › General for user name and initials. |
+12 -5
View File
@@ -1248,10 +1248,12 @@ pub fn show(app: &mut WordApp, ui: &mut Ui) {
// the scroll area is built, so it reads last frame's viewport.
app.canvas.viewport = Some(out.inner_rect);
let (resp, rects) = out.inner;
// A click on the page leaves the selected comment balloon; a click inside a balloon (its
// Delete or Resolve button, say) does not (#1085).
let in_balloon = ui.input(|i| i.pointer.interact_pos()).is_some_and(|p| app.canvas.balloon_rects.iter().any(|(_, r)| r.contains(p)));
if app.canvas.balloon.is_some() && ui.input(|i| i.pointer.any_pressed()) && resp.contains_pointer() && !in_balloon {
// A click on the page leaves the selected comment balloon.
if app.canvas.balloon.is_some()
&& ui.input(|i| i.pointer.any_pressed())
&& resp.contains_pointer()
&& !ui.input(|i| i.pointer.latest_pos()).is_some_and(|p| over_balloon(app, p))
{
deselect_balloon(app, ui.ctx());
}
// Space and Enter also set `Response::clicked` on a focused click-sensing widget
@@ -1710,6 +1712,11 @@ pub fn select_balloon(app: &mut WordApp, ctx: &egui::Context, id: u32) {
app.canvas.balloon_h = 0.0;
}
/// A press over a balloon card belongs to the card, though the canvas under it also contains the pointer (#1013).
fn over_balloon(app: &WordApp, p: Pos2) -> bool {
app.canvas.balloon_rects.iter().any(|(_, r)| r.contains(p))
}
/// Leave the selected balloon, writing back what was typed in it (its fields may not be drawn
/// again to notice they lost focus).
pub fn deselect_balloon(app: &mut WordApp, ctx: &egui::Context) {
@@ -1888,7 +1895,7 @@ fn mouse(app: &mut WordApp, ui: &Ui, resp: &egui::Response, rects: &[Rect], layo
let _ = app.run("select.paragraph", json!({}));
return;
}
let pressed = ui.input(|i| i.pointer.primary_pressed()) && resp.contains_pointer();
let pressed = ui.input(|i| i.pointer.primary_pressed()) && resp.contains_pointer() && !over_balloon(app, p);
if pressed {
let now = ui.input(|i| i.time);
let (delay, dist) = ui.ctx().options(|o| (o.input_options.max_double_click_delay, o.input_options.max_click_dist));
+57
View File
@@ -3153,6 +3153,63 @@ mod tests {
assert!(!a.session.sel.is_collapsed(), "the drag left a selection");
}
/// #1013: a press inside the selected comment's balloon (to move the caret in its text) left
/// the balloon, because the canvas under it also "contained" the pointer.
#[test]
fn a_press_inside_the_selected_comment_keeps_it_selected() {
let ctx = egui::Context::default();
let mut a = app();
a.run("text.insert", json!({"text": "Some text to comment on. ".repeat(40)})).unwrap();
a.run("select.word", json!({})).unwrap();
a.run("review.newComment", json!({"text": "A comment with a few words in it"})).unwrap();
a.session.view.comments_pane = false;
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1400.0, 700.0));
let mut t = 0.0;
let mut frame = |a: &mut WordApp, events: Vec<egui::Event>| {
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());
}
let (id, card) = *a.canvas.balloon_rects.first().expect("the comment has a balloon");
frame(&mut a, vec![egui::Event::PointerMoved(card.center())]);
frame(&mut a, vec![btn(card.center(), true)]);
frame(&mut a, vec![btn(card.center(), false)]);
for _ in 0..5 {
frame(&mut a, Vec::new());
}
assert_eq!(a.canvas.balloon, Some(id), "a click on the balloon selects it");
let (_, card) = *a.canvas.balloon_rects.iter().find(|(i, _)| *i == id).unwrap();
let (focus, scroll) = (a.session.sel.clone(), a.canvas.scroll_offset);
// Press, drag a little and release inside the comment's text.
let (from, to) = (card.left_top() + egui::vec2(30.0, 30.0), card.left_top() + egui::vec2(80.0, 30.0));
frame(&mut a, vec![egui::Event::PointerMoved(from)]);
frame(&mut a, vec![btn(from, true)]);
assert_eq!(a.canvas.balloon, Some(id), "the press stays in the balloon");
for _ in 0..3 {
frame(&mut a, vec![egui::Event::PointerMoved(to)]);
}
assert!(!a.canvas.drag_selecting(), "the press didn't start a drag in the document");
frame(&mut a, vec![btn(to, false)]);
assert_eq!(a.canvas.balloon, Some(id), "still selected after the release");
assert_eq!(a.session.sel, focus, "the document's caret didn't move");
assert_eq!(a.canvas.scroll_offset, scroll);
// A press on the page itself still leaves the balloon.
let page = a.canvas.page_rects.first().copied().unwrap();
let body = page.left_top() + egui::vec2(120.0, 160.0);
frame(&mut a, vec![egui::Event::PointerMoved(body)]);
frame(&mut a, vec![btn(body, true)]);
frame(&mut a, vec![btn(body, false)]);
assert_eq!(a.canvas.balloon, None, "a click on the page leaves the balloon");
}
/// #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]