From 13d7a215010c42d2007ee401cfdd7d186d2c24fb Mon Sep 17 00:00:00 2001 From: Maximiliano Sandoval R Date: Sat, 8 Jan 2022 16:21:27 +0100 Subject: [PATCH] Implement drag source on images Using a 128x128 thumbnail. --- src/main.rs | 1 + src/meson.build | 1 + src/thumbnail.rs | 90 +++++++++++++++++++++++++++++++++++++++ src/widgets/image.rs | 18 ++++++++ src/widgets/image_view.rs | 25 +++++++++++ 5 files changed, 135 insertions(+) create mode 100644 src/thumbnail.rs diff --git a/src/main.rs b/src/main.rs index 72907c1..9e83087 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ use gtk::gio::{self, prelude::*}; mod application; mod config; +mod thumbnail; mod util; mod widgets; mod window; diff --git a/src/meson.build b/src/meson.build index 8045fdf..a6eb1fb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -20,6 +20,7 @@ rust_sources = files( 'config.rs', 'main.rs', 'util.rs', + 'thumbnail.rs', 'window.rs', ) diff --git a/src/thumbnail.rs b/src/thumbnail.rs new file mode 100644 index 0000000..ea37bef --- /dev/null +++ b/src/thumbnail.rs @@ -0,0 +1,90 @@ +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{gdk, glib}; + +mod imp { + use super::*; + + use once_cell::sync::OnceCell; + + const SIZE: i32 = 128; + + #[derive(Debug, Default)] + pub struct Thumbnail { + pub image: OnceCell, + } + + #[glib::object_subclass] + impl ObjectSubclass for Thumbnail { + const NAME: &'static str = "Thumbnail"; + type Type = super::Thumbnail; + type ParentType = glib::Object; + type Interfaces = (gdk::Paintable,); + } + + impl ObjectImpl for Thumbnail {} + impl PaintableImpl for Thumbnail { + fn intrinsic_height(&self, _paintable: &Self::Type) -> i32 { + let image = self.image.get().unwrap(); + let width = image.intrinsic_width(); + let height = image.intrinsic_height(); + let aspect_ratio = width as f64 / (height as f64).max(std::f64::EPSILON); + + if width >= height { + (SIZE as f64 / aspect_ratio) as i32 + } else { + SIZE + } + } + + fn intrinsic_width(&self, _paintable: &Self::Type) -> i32 { + let image = self.image.get().unwrap(); + let width = image.intrinsic_width(); + let height = image.intrinsic_height(); + let aspect_ratio = width as f64 / (height as f64).max(std::f64::EPSILON); + + if width >= height { + SIZE + } else { + (SIZE as f64 * aspect_ratio) as i32 + } + } + + fn intrinsic_aspect_ratio(&self, _paintable: &Self::Type) -> f64 { + self.image.get().unwrap().intrinsic_aspect_ratio() + } + + fn snapshot( + &self, + paintable: &Self::Type, + snapshot: &gdk::Snapshot, + _width: f64, + _height: f64, + ) { + let width = paintable.intrinsic_width() as f64; + let height = paintable.intrinsic_height() as f64; + + self.image.get().unwrap().snapshot(snapshot, width, height); + } + } +} + +glib::wrapper! { + /// Thumbnail Paintable + /// + /// Creates a thumbnail from another paintable. The thumbnail will be drawn + /// at a maximum size of 128 × 128 taking into account the original + /// paintable aspect ratio. + pub struct Thumbnail(ObjectSubclass) @implements gdk::Paintable; +} + +impl Thumbnail { + pub fn new(paintable: &impl glib::IsA) -> Self { + let object = glib::Object::new::(&[]).unwrap(); + let imp = object.imp(); + + imp.image.set(paintable.clone().upcast()).unwrap(); + + object + } +} diff --git a/src/widgets/image.rs b/src/widgets/image.rs index 29f7b27..3fa4efa 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -210,4 +210,22 @@ impl LpImage { let imp = self.imp(); imp.texture.borrow().clone() } + + pub fn content_provider(&self) -> anyhow::Result { + let imp = self.imp(); + let mut contents = vec![]; + + if let Some(file) = imp.file.borrow().as_ref() { + let content = gdk::ContentProvider::for_value(&file.to_value()); + contents.push(content); + } + + if let Some(texture) = imp.texture.borrow().as_ref() { + let bytes = texture.save_to_png_bytes(); + let content = gdk::ContentProvider::for_bytes("image/png", &bytes); + contents.push(content); + } + + Ok(gdk::ContentProvider::new_union(contents.as_slice())) + } } diff --git a/src/widgets/image_view.rs b/src/widgets/image_view.rs index 648cfcc..86250c0 100644 --- a/src/widgets/image_view.rs +++ b/src/widgets/image_view.rs @@ -31,6 +31,7 @@ use ashpd::WindowIdentifier; use once_cell::sync::Lazy; use std::cell::{Cell, RefCell}; +use crate::thumbnail::Thumbnail; use crate::util; use crate::widgets::LpImage; @@ -131,6 +132,30 @@ mod imp { .connect_pressed(clone!(@weak obj => move |_, x, y| { obj.show_popover_at(x, y); })); + + let source = gtk::DragSource::new(); + + source.connect_prepare( + glib::clone!(@weak obj => @default-return None, move |_, _, _| { + match obj.imp().picture.content_provider() { + Ok(content) => Some(content), + Err(e) => { + log::error!("Could not get content provider: {:?}", e); + None + } + } + }), + ); + + source.connect_drag_begin(glib::clone!(@weak obj => move |source, _| { + let imp = obj.imp(); + if let Some(texture) = imp.picture.texture() { + let thumbnail = Thumbnail::new(&texture); + source.set_icon(Some(&thumbnail), 0, 0); + }; + })); + + obj.add_controller(&source); } }