Flatten comments from the page's right-click menu and the Comments panel's menu

Reviewers flatten their comments and Fill & Sign signature once done, so
nothing on the signed or reviewed pages moves any more. The command was
only in All tools and the palette; it now also ends the page's
right-click menu (with or without a comment selected, while the document
has comments) and the Comments panel's More options menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Oliver Hagmann
2026-10-22 22:08:35 +02:00
co-authored by Claude Opus 5.5
parent d5047aa4ac
commit 60ce45f9ba
5 changed files with 69 additions and 4 deletions
+1 -1
View File
@@ -4031,7 +4031,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {
ui.separator();
}
}
canvas_action = comments::context_menu(ui, view, info, prefs, allowed).or(copy_image);
canvas_action = comments::context_menu(ui, view, info, prefs, allowed, can_modify).or(copy_image);
});
(wanted, visible_now)
});
+19 -1
View File
@@ -1841,7 +1841,15 @@ pub(crate) fn attachment_items(ui: &mut egui::Ui, view: &mut DocView, info: &Doc
}
/// Items of the canvas context menu for the selected comment (or the page).
pub(crate) fn context_menu(ui: &mut egui::Ui, view: &mut DocView, info: &DocInfo, prefs: &CommentPrefs, allowed: bool) -> Option<CanvasAction> {
/// `can_modify`: the document's pages may change (Flatten comments draws into them).
pub(crate) fn context_menu(
ui: &mut egui::Ui,
view: &mut DocView,
info: &DocInfo,
prefs: &CommentPrefs,
allowed: bool,
can_modify: bool,
) -> Option<CanvasAction> {
let mut action = None;
let selected = view.comments.selected.and_then(|(p, i)| info.annotations.iter().find(|a| a.page == p && a.index == i && a.in_reply_to.is_none()));
match selected {
@@ -1925,6 +1933,16 @@ pub(crate) fn context_menu(ui: &mut egui::Ui, view: &mut DocView, info: &DocInfo
}
}
}
// Every comment of the document into its pages, as Comment ▸ Flatten comments does: signed
// or reviewed pages whose comments should no longer move.
if !info.annotations.is_empty() {
ui.separator();
if ui.add_enabled(can_modify, egui::Button::new(tl!("Flatten comments"))).clicked() {
view.comments.selected = None;
action = Some(CanvasAction::Edit(Box::new(Edit::Flatten { comments: true, fields: false })));
ui.close();
}
}
action
}
+6
View File
@@ -585,6 +585,12 @@ pub(crate) fn header_controls(ui: &mut egui::Ui, info: &DocInfo, view: &mut DocV
ui.close();
}
}
// Into the pages, for good once saved: apart from the list's other commands.
ui.separator();
if ui.button(tl!("Flatten comments")).clicked() {
command = Some("comment.flatten");
ui.close();
}
});
let filter = icons::button(ui, "filter", 26.0, active, tl!("Filter comments"));
egui::Popup::menu(&filter).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| {
+41
View File
@@ -1265,6 +1265,47 @@ fn page_groups_collapse_and_expand() {
assert!(shown(&h, "First page note") && shown(&h, "Second page note"), "every page is open again");
}
/// Flatten comments is in the page's right-click menu and in the Comments panel's "…" menu: the
/// comments go into the page, one undo step brings them back.
#[test]
fn flatten_comments_from_the_page_menu_and_the_panel_menu() {
let mut h = harness(|app| {
app.set_option("panel", "comments").unwrap();
app.set_option("quick", "select").unwrap();
});
let shape = pdfcraft_engine::Shape::Rectangle { rect: [20.0, 20.0, 80.0, 60.0] };
h.state_mut().apply_edit(pdfcraft_engine::Edit::AddAnnotation(pdfcraft_engine::NewAnnotation {
page: 0,
style: pdfcraft_engine::Style::default_for(&shape),
shape,
contents: "Reviewed".into(),
author: "Tester".into(),
}));
h.state_mut().views[0].comments.selected = None;
settle(&mut h);
assert_eq!(comments(&h).len(), 1);
// Right-click on the page, away from the comment.
let p = at(&h, 200.0, 150.0);
h.hover_at(p);
h.run_steps(1);
h.event(egui::Event::PointerButton { pos: p, button: egui::PointerButton::Secondary, pressed: true, modifiers: Default::default() });
h.event(egui::Event::PointerButton { pos: p, button: egui::PointerButton::Secondary, pressed: false, modifiers: Default::default() });
h.run_steps(3);
h.get_by_label("Flatten comments").click();
h.run_steps(4);
assert!(comments(&h).is_empty(), "the comment went into the page");
h.state_mut().undo();
h.run_steps(3);
assert_eq!(comments(&h).len(), 1, "one undo step brings it back");
h.get_by_label("More options").click();
h.run_steps(2);
h.get_by_label("Flatten comments").click();
h.run_steps(4);
assert!(comments(&h).is_empty(), "the panel's menu flattens too");
}
#[test]
fn comments_take_checkmarks_lock_hide_and_summarize() {
let mut h = harness(|app| {
+2 -2
View File
@@ -4232,8 +4232,8 @@ milestone = "M5"
status = "shipped"
commands = ["comment.flatten"]
tools = ["doc_flatten"]
evidence = ["crates/edit/src/tests.rs::flattening_draws_appearances_into_the_page_and_removes_the_comments", "crates/edit/src/tests.rs::flattening_keeps_an_undrawn_reply_to_a_flattened_comment", "crates/engine/src/tests.rs::flattening_keeps_the_look_and_drops_the_objects", "crates/automation/tests/it/automation.rs::flattening_through_tools", "crates/edit/src/tests.rs::flattening_comments_keeps_annotations_that_are_not_comments"]
notes = "appearances become page content; pop-ups and replies removed; a comment or reply whose appearance cannot be drawn (a cloudy border, an unsupported subtype) stays as it is instead of being deleted; annotations that are not comments (links, 3D, Screen, Movie, RichMedia, PrinterMark, TrapNet, Watermark) are left in place (#3087)"
evidence = ["crates/edit/src/tests.rs::flattening_draws_appearances_into_the_page_and_removes_the_comments", "crates/edit/src/tests.rs::flattening_keeps_an_undrawn_reply_to_a_flattened_comment", "crates/engine/src/tests.rs::flattening_keeps_the_look_and_drops_the_objects", "crates/automation/tests/it/automation.rs::flattening_through_tools", "crates/edit/src/tests.rs::flattening_comments_keeps_annotations_that_are_not_comments", "crates/ui-egui/tests/it/comments.rs::flatten_comments_from_the_page_menu_and_the_panel_menu"]
notes = "also in the page's right-click menu and the Comments panel's More options menu; appearances become page content; pop-ups and replies removed; a comment or reply whose appearance cannot be drawn (a cloudy border, an unsupported subtype) stays as it is instead of being deleted; annotations that are not comments (links, 3D, Screen, Movie, RichMedia, PrinterMark, TrapNet, Watermark) are left in place (#3087)"
[[feature]]
id = "comment.migrate"