diff --git a/crates/ui-egui/src/comments.rs b/crates/ui-egui/src/comments.rs index 20ea284e..fe75ba96 100644 --- a/crates/ui-egui/src/comments.rs +++ b/crates/ui-egui/src/comments.rs @@ -275,6 +275,13 @@ impl CommentPrefs { } } + /// The tool's opacity, 10–100 % (an invisible comment can't be found again). + pub fn set_opacity(&mut self, tool: CommentTool, o: f64) { + if let Some((_, s)) = self.styles.iter_mut().find(|(t, _)| *t == tool) { + s.opacity = if o.is_finite() { o.clamp(0.1, 1.0) } else { 1.0 }; + } + } + pub fn set_style(&mut self, tool: CommentTool, style: Style) { if let Some((_, s)) = self.styles.iter_mut().find(|(t, _)| *t == tool) { *s = style; @@ -1253,7 +1260,7 @@ pub fn swatch_grid(ui: &mut egui::Ui, current: Option) -> Option { picked } -/// The comment tools' extra quick-bar controls: pin, colour and thickness. +/// The comment tools' extra quick-bar controls: pin, colour, opacity and thickness. pub(crate) fn quick_bar_controls(ui: &mut egui::Ui, tool: CommentTool, prefs: &mut CommentPrefs) { let style = prefs.style(tool); if icons::button(ui, "pin", 32.0, prefs.pinned, if prefs.pinned { "Keep tool selected: on" } else { "Keep tool selected" }).clicked() { @@ -1269,6 +1276,15 @@ pub(crate) fn quick_bar_controls(ui: &mut egui::Ui, tool: CommentTool, prefs: &m ui.close(); } }); + let resp = icons::button(ui, "blend", 32.0, false, "Opacity"); + egui::Popup::menu(&resp).align(egui::RectAlign::RIGHT_START).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| { + ui.set_min_width(180.0); + let mut percent = (style.opacity * 100.0).round(); + ui.label(egui::RichText::new("Opacity").font(theme::semibold(12.0))); + if ui.add(egui::Slider::new(&mut percent, 10.0..=100.0).step_by(5.0).suffix(" %")).changed() { + prefs.set_opacity(tool, percent / 100.0); + } + }); if tool.has_width() { let resp = icons::button(ui, "sliders-horizontal", 32.0, false, "Line thickness"); egui::Popup::menu(&resp).align(egui::RectAlign::RIGHT_START).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| { diff --git a/crates/ui-egui/tests/comments.rs b/crates/ui-egui/tests/comments.rs index 499f7840..f58216f5 100644 --- a/crates/ui-egui/tests/comments.rs +++ b/crates/ui-egui/tests/comments.rs @@ -568,3 +568,33 @@ fn the_squiggly_tool_marks_selected_text() { assert!(printcraft_ui_egui::comments::GROUPS[1].contains(&printcraft_ui_egui::comments::CommentTool::Squiggly)); assert_eq!(printcraft_ui_egui::comments::tool_for(&c[0]), Some(printcraft_ui_egui::comments::CommentTool::Squiggly)); } + +#[test] +fn the_opacity_set_for_a_tool_goes_into_its_new_comments() { + use printcraft_ui_egui::comments::CommentTool; + let mut h = harness(|app| app.comment_prefs.set_opacity(CommentTool::StrikeOut, 0.5)); + // Clamped: an invisible comment can't be found again. + h.state_mut().comment_prefs.set_opacity(CommentTool::Underline, 0.0); + assert!((h.state().comment_prefs.style(CommentTool::Underline).opacity - 0.1).abs() < 1e-9); + h.state_mut().views[0].select_text(0, 4, 8); + assert!(h.state_mut().execute("comment.strikeout")); + h.run_steps(2); + let c = comments(&h); + assert_eq!(c.len(), 1, "{c:?}"); + // The new comment carries /CA 0.5 (read back from the document's bytes). + let bytes = h.state().session.get(h.state().views[0].id).unwrap().bytes.as_ref().clone(); + let doc = printcraft_cos::Document::open(std::sync::Arc::new(bytes)).unwrap(); + let opacities: Vec = doc + .object_numbers() + .into_iter() + .filter_map(|n| doc.try_get(n).ok()) + .filter_map(|o| o.as_dict().cloned()) + .filter(|d| d.name(b"Subtype") == Some(b"StrikeOut")) + .filter_map(|d| d.get(b"CA").and_then(|v| v.as_f64())) + .collect(); + assert_eq!(opacities, [0.5]); + // The quick bar offers the control. + h.state_mut().set_option("quick", "strikeout").unwrap(); + h.run_steps(2); + h.get_by_label("Opacity"); +} diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 9cf0b43e..d4875498 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -3767,11 +3767,11 @@ title = "Comment properties bar" area = "E" tier = "P1" milestone = "M5" -status = "partial" +status = "shipped" commands = ["comment.note"] tools = ["comment_edit"] -evidence = ["crates/annot/src/tests.rs::restyle_and_resize_redraw_the_appearance"] -notes = "quick-bar colour swatches and line thickness per tool, colour from the context menu; no opacity control in the UI yet" +evidence = ["crates/annot/src/tests.rs::restyle_and_resize_redraw_the_appearance", "crates/ui-egui/tests/comments.rs::the_opacity_set_for_a_tool_goes_into_its_new_comments"] +notes = "quick-bar colour swatches, opacity (10–100 %) and line thickness per tool; colour from the context menu" [[feature]] id = "comment.lock-comment"