From 04cee34bfe1f6c64fb581a625b145c5a04391d6f Mon Sep 17 00:00:00 2001 From: ohagmann <268387159+ohagmann@users.noreply.github.com> Date: Thu, 5 Nov 2026 13:21:29 +0100 Subject: [PATCH] M5: a double-click on a comment card's text edits it (#4544) * A double-click on a comment card's text edits it On the selected card in the Comments panel only the header took the double-click: the text is a selectable label, so a double-click there selected a word and the editor stayed closed (a tester's report). It now opens the editor too; dragging over the text still selects it for copying. Co-Authored-By: Claude Opus 5.5 * Tests: the double-clicked card's editor is drawn too Co-Authored-By: Claude Opus 5.5 * Review: the double-click test also starts from a card that isn't selected It selected the card before any input, so it never covered what the change is for in the Comments panel as one uses it: a first click on the text of an unselected card selects it, the second edits it. The test now runs both ways. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- crates/ui-egui/src/panels/comments_panel.rs | 12 ++++-- crates/ui-egui/tests/it/comments.rs | 47 +++++++++++++++++++++ parity/acrobat-features.toml | 4 +- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/panels/comments_panel.rs b/crates/ui-egui/src/panels/comments_panel.rs index d54daf5717..591a667159 100644 --- a/crates/ui-egui/src/panels/comments_panel.rs +++ b/crates/ui-egui/src/panels/comments_panel.rs @@ -433,7 +433,7 @@ fn card(ui: &mut egui::Ui, view: &mut DocView, context: CardContext<'_>) -> Opti buttons_left = ui.min_rect().left(); }); }); - egui::Frame::NONE.inner_margin(egui::Margin { left: 34, right: 0, top: 4, bottom: 0 }).show(ui, |ui| { + let body = egui::Frame::NONE.inner_margin(egui::Margin { left: 34, right: 0, top: 4, bottom: 0 }).show(ui, |ui| { ui.set_width(ui.available_width()); if editing { if let Some(e) = inline_editor(ui, t, view, key, 2) { @@ -531,7 +531,7 @@ fn card(ui: &mut egui::Ui, view: &mut DocView, context: CardContext<'_>) -> Opti // The header selects the card, except where its buttons are. let mut r = head.response.rect; r.max.x = r.max.x.min(buttons_left - 2.0); - r + (r, body.response.rect) }); let rect = frame.response.rect; if selected { @@ -545,7 +545,8 @@ fn card(ui: &mut egui::Ui, view: &mut DocView, context: CardContext<'_>) -> Opti ui.add_space(5.0); // The header row selects the card (and shows the comment on the page); unselected cards are // clickable everywhere. - let hit = if selected { frame.inner } else { rect }; + let (head, body) = frame.inner; + let hit = if selected { head } else { rect }; let click = ui .interact(hit, ui.scope_id().with(("card", key)), Sense::click()) .on_hover_cursor(egui::CursorIcon::PointingHand) @@ -555,7 +556,10 @@ fn card(ui: &mut egui::Ui, view: &mut DocView, context: CardContext<'_>) -> Opti view.comments.reply.clear(); *nav = Some(Nav::Flash(a.page, a.shown_rect(rotation))); } - if click.double_clicked() && allowed { + // On the selected card a double-click on the text edits it too (dragging over it selects + // words of it, for copying). + let body = (selected && !editing).then(|| ui.interact(body, ui.scope_id().with(("card-body", key)), Sense::click())); + if (click.double_clicked() || body.is_some_and(|b| b.double_clicked())) && allowed { view.comments.open_editor(a.page, a.index, a.contents.clone().unwrap_or_default()); } let menu_id = ui.scope_id().with(("card-menu", key)); diff --git a/crates/ui-egui/tests/it/comments.rs b/crates/ui-egui/tests/it/comments.rs index 129bcbf3e4..29396830f7 100644 --- a/crates/ui-egui/tests/it/comments.rs +++ b/crates/ui-egui/tests/it/comments.rs @@ -143,6 +143,53 @@ fn sidebar_comment_centres_the_annotation_at_high_zoom() { } } +/// A double-click on the text of a card in the Comments panel edits it, as one on its header does +/// (a click there would select a word): on a selected card, and on one the first click selects. +#[test] +fn double_clicking_a_cards_text_edits_it() { + for selected in [true, false] { + double_click_a_cards_text(selected); + } +} + +fn double_click_a_cards_text(selected: bool) { + // Frames 1/60 s apart, so two clicks make a double-click. + let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(1.0 / 60.0).build_eframe(move |_cc| { + let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); + app.open_bytes("text.pdf", None, TEXT_FIXTURE.to_vec()).expect("opens"); + app.set_option("left", "closed").unwrap(); + app.set_option("panel", "comments").unwrap(); + let shape = pdfcraft_engine::Shape::Note { at: [150.0, 90.0], icon: pdfcraft_engine::NoteIcon::Comment }; + assert!(app.apply_edit(pdfcraft_engine::Edit::AddAnnotation(pdfcraft_engine::NewAnnotation { + page: 0, + style: pdfcraft_engine::Style::default_for(&shape), + shape, + contents: "Edit this note".into(), + author: "Tester".into(), + }))); + app.views[0].comments.selected = selected.then_some((0, 0)); + app + }); + h.run_steps(4); + crate::support::settle(&mut h); + assert_eq!(h.state().views[0].comments.selected.is_some(), selected); + let p = h.query_all_by_label("Edit this note").next().expect("the card's text").rect().center(); + h.hover_at(p); + h.run_steps(1); + for _ in 0..2 { + for pressed in [true, false] { + h.event(egui::Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed, modifiers: egui::Modifiers::NONE }); + } + h.run_steps(1); + } + h.run_steps(2); + assert_eq!(h.state().views[0].comments.selected, Some((0, 0)), "selected: {selected}"); + assert_eq!(h.state().views[0].comments.editing, Some((0, 0, "Edit this note".into())), "selected: {selected}"); + // And the editor is drawn. + note_editor(&h); +} + #[test] fn dragging_over_text_with_the_highlighter_highlights_it() { let mut h = harness(|app| app.set_option("quick", "highlight").unwrap()); diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 6ff5eca28e..f543284d54 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -4118,8 +4118,8 @@ milestone = "M5" status = "shipped" commands = ["comment.list"] tools = ["comment_edit"] -evidence = ["crates/annot/src/tests.rs::text_box_text_and_colour_changes_are_drawn", "crates/annot/src/tests.rs::editing_a_text_box_refits_its_rectangle", "crates/automation/tests/it/automation.rs::comments_through_tools", "crates/ui-egui/tests/it/comments.rs::ctrl_enter_posts_a_highlight_comment_and_shift_enter_adds_a_line", "crates/ui-egui/tests/it/comments.rs::one_reply_is_deleted_from_its_menu_and_the_thread_stays", "crates/ui-egui/tests/it/comments.rs::undo_and_redo_close_a_reply_editor_left_open"] -notes = "double-click a comment or card, or Edit in the menus (a reply from its own \"…\" menu); Ctrl+Enter (also Command+Enter) saves text in the focused comment editor, Shift+Enter adds a newline; a text box's rectangle follows the new text (wrap width and top edge stay, the height fits the wrapped lines). Undo or Redo closes the panel's editor without saving it, since either can move the comment it was open on" +evidence = ["crates/annot/src/tests.rs::text_box_text_and_colour_changes_are_drawn", "crates/annot/src/tests.rs::editing_a_text_box_refits_its_rectangle", "crates/automation/tests/it/automation.rs::comments_through_tools", "crates/ui-egui/tests/it/comments.rs::ctrl_enter_posts_a_highlight_comment_and_shift_enter_adds_a_line", "crates/ui-egui/tests/it/comments.rs::one_reply_is_deleted_from_its_menu_and_the_thread_stays", "crates/ui-egui/tests/it/comments.rs::undo_and_redo_close_a_reply_editor_left_open", "crates/ui-egui/tests/it/comments.rs::double_clicking_a_cards_text_edits_it"] +notes = "double-click a comment or card (its header or its text), or Edit in the menus; double-click a comment or card, or Edit in the menus (a reply from its own \"…\" menu); Ctrl+Enter (also Command+Enter) saves text in the focused comment editor, Shift+Enter adds a newline; a text box's rectangle follows the new text (wrap width and top edge stay, the height fits the wrapped lines). Undo or Redo closes the panel's editor without saving it, since either can move the comment it was open on" [[feature]] id = "comment.copy-comment-text"