diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 434f73181..3066d1b9e 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -4084,7 +4084,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) }); diff --git a/crates/ui-egui/src/comments.rs b/crates/ui-egui/src/comments.rs index c520466bf..05ccd5f77 100644 --- a/crates/ui-egui/src/comments.rs +++ b/crates/ui-egui/src/comments.rs @@ -1913,7 +1913,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 { +/// `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 { 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 { @@ -1998,6 +2006,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 } diff --git a/crates/ui-egui/src/comments_panel.rs b/crates/ui-egui/src/comments_panel.rs index 0f1a387e0..6c13d78d3 100644 --- a/crates/ui-egui/src/comments_panel.rs +++ b/crates/ui-egui/src/comments_panel.rs @@ -588,6 +588,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| { diff --git a/crates/ui-egui/tests/it/comments.rs b/crates/ui-egui/tests/it/comments.rs index f6c843458..34a38b8cb 100644 --- a/crates/ui-egui/tests/it/comments.rs +++ b/crates/ui-egui/tests/it/comments.rs @@ -1391,6 +1391,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| { diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 800105fb3..af69e7ace 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -4256,8 +4256,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"