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 <noreply@anthropic.com>

* Tests: the double-clicked card's editor is drawn too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ohagmannandClaude Opus 5.5 authored and GitHub committed 2026-11-05 13:21:29 +01:00
1 parent 8d569066b8
commit 04cee34bfe
3 files changed
+57 -6

No files matched your search

+8 -4
View File
@@ -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));
+47
View File
@@ -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());
+2 -2
View File
@@ -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"