From 6477f8b50035e1756c4d8aa639e6daa9fd596895 Mon Sep 17 00:00:00 2001 From: rope Date: Sat, 19 Sep 2026 09:52:32 +0100 Subject: [PATCH] Add optional automatic annotation copying --- data/org.gnome.Loupe.gschema.xml.in | 4 ++++ src/widgets/image.rs | 33 +++++++++++++++++++++++++++-- src/widgets/image/input_handling.rs | 10 +++++++-- src/widgets/image_window.rs | 14 ++++++++++++ src/widgets/image_window.ui | 10 +++++++++ 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/data/org.gnome.Loupe.gschema.xml.in b/data/org.gnome.Loupe.gschema.xml.in index 13ac2ea..9a9d1d5 100644 --- a/data/org.gnome.Loupe.gschema.xml.in +++ b/data/org.gnome.Loupe.gschema.xml.in @@ -5,5 +5,9 @@ false Show the image properties sidebar + + true + Copy the image to the clipboard after annotation changes + diff --git a/src/widgets/image.rs b/src/widgets/image.rs index 94535f2..d5a0d88 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -459,13 +459,24 @@ impl LpImage { } pub fn undo_stroke(&self) { - self.imp().strokes.borrow_mut().pop(); + let changed = self.imp().strokes.borrow_mut().pop().is_some(); self.queue_draw(); + if changed { + self.copy_annotated_to_clipboard(); + } } pub fn clear_strokes(&self) { - self.imp().strokes.borrow_mut().clear(); + let changed = { + let mut strokes = self.imp().strokes.borrow_mut(); + let changed = !strokes.is_empty(); + strokes.clear(); + changed + }; self.queue_draw(); + if changed { + self.copy_annotated_to_clipboard(); + } } pub fn has_strokes(&self) -> bool { @@ -506,4 +517,22 @@ impl LpImage { renderer.unrealize(); Some(texture) } + + /// Copy the current image and its annotations after a completed edit. + pub fn copy_annotated_to_clipboard(&self) { + if !crate::application::LpApplication::default() + .settings() + .boolean("auto-copy-annotations") + { + return; + } + let Some(texture) = self.render_annotated_texture() else { + tracing::error!("Failed to render annotated image for clipboard"); + return; + }; + let content = gdk::ContentProvider::for_value(&texture.to_value()); + if let Err(err) = self.clipboard().set_content(Some(&content)) { + tracing::error!("Failed to copy annotated image: {err}"); + } + } } diff --git a/src/widgets/image/input_handling.rs b/src/widgets/image/input_handling.rs index d9f5e33..38b5142 100644 --- a/src/widgets/image/input_handling.rs +++ b/src/widgets/image/input_handling.rs @@ -95,10 +95,16 @@ impl imp::LpImage { } } } - if let Some(stroke) = cur.and_then(|s| s.into_stroke()) { + let changed = if let Some(stroke) = cur.and_then(|s| s.into_stroke()) { imp.strokes.borrow_mut().push(stroke); - } + true + } else { + false + }; obj.queue_draw(); + if changed { + obj.copy_annotated_to_clipboard(); + } } )); } diff --git a/src/widgets/image_window.rs b/src/widgets/image_window.rs index b83d54b..4cf0fd7 100644 --- a/src/widgets/image_window.rs +++ b/src/widgets/image_window.rs @@ -133,6 +133,8 @@ mod imp { pub(super) draw_line_width_btn: TemplateChild, #[template_child] pub(super) draw_clear_btn: TemplateChild, + #[template_child] + pub(super) draw_auto_copy_btn: TemplateChild, } #[glib::object_subclass] @@ -475,6 +477,18 @@ mod imp { imp.draw_color_btn .set_rgba(&gdk::RGBA::new(0.96, 0.22, 0.22, 1.0)); + // Persist automatic clipboard updates as an app preference. + let settings = LpApplication::default().settings(); + imp.draw_auto_copy_btn + .set_active(settings.boolean("auto-copy-annotations")); + imp.draw_auto_copy_btn.connect_toggled(move |btn| { + if let Err(err) = + settings.set_boolean("auto-copy-annotations", btn.is_active()) + { + tracing::warn!("Failed to save auto-copy setting: {err}"); + } + }); + // Connect draw mode button (headerbar toggle) imp.draw_mode_button.connect_toggled(glib::clone!( #[weak] diff --git a/src/widgets/image_window.ui b/src/widgets/image_window.ui index a9571a2..e6c0ac6 100644 --- a/src/widgets/image_window.ui +++ b/src/widgets/image_window.ui @@ -265,6 +265,16 @@ + + + + Auto-copy + Copy to Clipboard After Each Change + + +