diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 4030d7c6e4..065d01fe21 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -2001,7 +2001,11 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { let field_props = field_props.then(|| view.prepare.selected.clone()).flatten(); match canvas_action { Some(comments::CanvasAction::Edit(e)) => view.pending_edit = Some(*e), - Some(comments::CanvasAction::OpenComments) => app.right = Some(RightPanel::Comments), + // `choose_right_panel` would borrow all of `app` while `view` is held; same effect. + Some(comments::CanvasAction::OpenComments) => { + app.right = Some(RightPanel::Comments); + app.comments_panel_closed = false; + } Some(comments::CanvasAction::Properties(p, i)) => open_props = Some((p, i)), None => {} } diff --git a/crates/ui-egui/src/chrome.rs b/crates/ui-egui/src/chrome.rs index 41df36e7c1..bc4a717a42 100644 --- a/crates/ui-egui/src/chrome.rs +++ b/crates/ui-egui/src/chrome.rs @@ -240,7 +240,7 @@ fn main_menu(app: &mut PdfCraftApp, ui: &mut egui::Ui) { (RightPanel::Compare, tl!("Compare")), ] { if ui.radio(app.right == Some(p), tl!(label)).clicked() { - app.right = Some(p); + app.choose_right_panel(Some(p)); } } }); @@ -278,7 +278,7 @@ pub fn right_rail(app: &mut PdfCraftApp, ui: &mut egui::Ui) { ui.painter().circle_filled(c, 3.0, t.accent); } if r.clicked() { - app.right = if selected { None } else { Some(panel) }; + app.choose_right_panel(if selected { None } else { Some(panel) }); } }; rail_button(ui, RightPanel::Comments, "message-square-text", "Comments", has_comments); diff --git a/crates/ui-egui/src/commands.rs b/crates/ui-egui/src/commands.rs index 6d3f97f9bd..126b7f319c 100644 --- a/crates/ui-egui/src/commands.rs +++ b/crates/ui-egui/src/commands.rs @@ -180,13 +180,13 @@ impl PdfCraftApp { "view.theme.system" => self.set_theme_preference(ThemePreference::System), "view.theme.light" => self.set_theme_preference(ThemePreference::Light), "view.theme.dark" => self.set_theme_preference(ThemePreference::Dark), - "comment.list" => self.right = Some(RightPanel::Comments), + "comment.list" => self.choose_right_panel(Some(RightPanel::Comments)), tool if crate::comments::CommentTool::from_command(tool).is_some() => { let Some(tool) = crate::comments::CommentTool::from_command(tool) else { return false }; self.comment_prefs.group_tool[tool.group()] = tool; self.quick_tool = crate::QuickTool::Comment(tool); - // Acrobat opens the Comments panel with the commenting tools. - if self.right.is_none() { + // Acrobat opens the Comments panel with the commenting tools, unless the user closed it. + if self.right.is_none() && !self.comments_panel_closed { self.right = Some(RightPanel::Comments); } // A text selection made before picking a markup tool is marked right away. diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index db6137b4e0..91f2edd884 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -356,6 +356,9 @@ pub struct PdfCraftApp { pub left: LeftPanel, pub left_open: bool, pub right: Option, + /// The user closed the Comments panel, so picking a comment tool leaves it closed until they + /// open it again (#225). Remembered across restarts. + pub comments_panel_closed: bool, pub quick_tool: QuickTool, /// The tool to go back to when Space, held for a temporary Hand, is released. space_hand: Option, @@ -602,6 +605,7 @@ impl PdfCraftApp { left: LeftPanel::AllTools, left_open: true, right: None, + comments_panel_closed: false, quick_tool: QuickTool::Select, space_hand: None, comment_prefs: Default::default(), @@ -1165,6 +1169,17 @@ impl PdfCraftApp { }; } + /// Opens a right panel, or closes it with `None`, because the user chose to. Closing Comments + /// keeps comment tools from reopening it; opening it again lets them (#225). + pub fn choose_right_panel(&mut self, panel: Option) { + if panel == Some(RightPanel::Comments) { + self.comments_panel_closed = false; + } else if panel.is_none() && self.right == Some(RightPanel::Comments) { + self.comments_panel_closed = true; + } + self.right = panel; + } + /// Serialize the user's persistent state (recent files, theme). Local only. pub fn persist(&self) -> String { let trusted: Vec = self.session.trusted_certificates().iter().map(pdfcraft_engine::sign::x509::to_pem).collect(); @@ -1190,6 +1205,7 @@ impl PdfCraftApp { "javascript": self.session.javascript(), "actions": actions_ui::encode(&self.custom_actions), "combine_columns": self.combine_columns.to_json(), + "comments_panel_closed": self.comments_panel_closed, }) .to_string() } @@ -1217,6 +1233,9 @@ impl PdfCraftApp { if let Some(on) = v["highlight_fields"].as_bool() { self.view_defaults.highlight_fields = on; } + if let Some(closed) = v["comments_panel_closed"].as_bool() { + self.comments_panel_closed = closed; + } if let Some(defaults) = v["default_zoom"].as_str().and_then(|zoom| self.view_defaults.with_zoom(zoom)) { self.view_defaults = defaults; } diff --git a/crates/ui-egui/src/panels.rs b/crates/ui-egui/src/panels.rs index 5475947703..27ac404e8e 100644 --- a/crates/ui-egui/src/panels.rs +++ b/crates/ui-egui/src/panels.rs @@ -584,7 +584,7 @@ pub fn right_panel(app: &mut PdfCraftApp, ui: &mut egui::Ui) { }); } if close { - app.right = None; + app.choose_right_panel(None); } if let Some(e) = panel_edit { app.apply_edit(e); diff --git a/crates/ui-egui/tests/comments.rs b/crates/ui-egui/tests/comments.rs index 688a962ede..e596e74fcf 100644 --- a/crates/ui-egui/tests/comments.rs +++ b/crates/ui-egui/tests/comments.rs @@ -600,3 +600,43 @@ fn the_opacity_set_for_a_tool_goes_into_its_new_comments() { h.run_steps(2); h.get_by_label("Opacity"); } + +/// #225: comment tools open the Comments panel until the user closes it; then it stays closed, +/// across restarts, until they open it again. +#[test] +fn a_closed_comments_panel_stays_closed_when_picking_comment_tools() { + use pdfcraft_ui_egui::RightPanel; + let mut h = harness(|_| {}); + assert_eq!(h.state().right, None); + assert!(h.state_mut().execute("comment.highlight")); + h.run_steps(2); + assert_eq!(h.state().right, Some(RightPanel::Comments), "Acrobat opens Comments with the tools"); + h.get_by_label("Close").click(); + h.run_steps(2); + assert_eq!(h.state().right, None); + for tool in ["select", "comment.underline", "comment.note", "comment.highlight"] { + if tool == "select" { + h.state_mut().quick_tool = QuickTool::Select; + } else { + assert!(h.state_mut().execute(tool)); + } + h.run_steps(2); + assert_eq!(h.state().right, None, "{tool} reopened the closed Comments panel"); + } + let mut again = PdfCraftApp::new(); + again.restore(&h.state().persist()); + assert!(again.comments_panel_closed, "the choice survives a restart"); + // Opening it again from the rail lets the tools open it once more. + h.get_by_label("Comments").click(); + h.run_steps(2); + assert_eq!(h.state().right, Some(RightPanel::Comments)); + assert!(!h.state().comments_panel_closed); + // What the rail and View menu call; "Bookmarks" labels two controls on screen. + h.state_mut().choose_right_panel(Some(RightPanel::Bookmarks)); + h.state_mut().choose_right_panel(None); + h.run_steps(2); + assert!(!h.state().comments_panel_closed, "switching to and closing another panel isn't closing Comments"); + assert!(h.state_mut().execute("comment.strikeout")); + h.run_steps(2); + assert_eq!(h.state().right, Some(RightPanel::Comments)); +}