From 221aa4b703f3949622e1e00e4984decd4c2d6fcc Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 16 Dec 2021 09:38:20 -0500 Subject: [PATCH] accept dnd drops from app library prototype --- examples/app_library/grid_item/mod.rs | 19 +++-- examples/dock/dock_item/imp.rs | 3 + examples/dock/dock_item/mod.rs | 115 ++++++++++++++++++-------- examples/dock/main.rs | 1 + examples/dock/style.css | 1 - examples/dock/utils.rs | 42 ++++++++++ examples/dock/window/imp.rs | 3 + examples/dock/window/mod.rs | 74 +++++++++++++++-- examples/dock/window/window.ui | 4 + 9 files changed, 215 insertions(+), 47 deletions(-) create mode 100644 examples/dock/utils.rs diff --git a/examples/app_library/grid_item/mod.rs b/examples/app_library/grid_item/mod.rs index c798a0e..4c4c738 100644 --- a/examples/app_library/grid_item/mod.rs +++ b/examples/app_library/grid_item/mod.rs @@ -2,6 +2,7 @@ use crate::app_group::AppGroup; use gdk4::ContentProvider; use gdk4::Display; use gio::File; +use gio::Icon; use gtk4 as gtk; use gtk4::traits::WidgetExt; use gtk4::DragSource; @@ -33,19 +34,23 @@ impl GridItem { let self_ = imp::GridItem::from_instance(self); self_.name.set_text(&app_info.name()); - let drag = DragSource::builder() + let drag_controller = DragSource::builder() .name("application library drag source") .actions(gdk4::DragAction::COPY) // .content() .build(); - self.add_controller(&drag); + self.add_controller(&drag_controller); if let Some(file) = app_info.filename() { let file = File::for_path(file); let provider = ContentProvider::for_value(&file.to_value()); - drag.set_content(Some(&provider)); + drag_controller.set_content(Some(&provider)); } - if let Some(icon) = app_info.icon() { - self_.image.set_from_gicon(&icon); + let icon = app_info + .icon() + .unwrap_or(Icon::for_string("image-missing").expect("Failed to set default icon")); + self_.image.set_from_gicon(&icon); + drag_controller.connect_drag_begin(glib::clone!(@weak icon, => move |_self, drag| { + drag.set_selected_action(gdk4::DragAction::MOVE); // set drag source icon if possible... // gio Icon is not easily converted to a Paintable, but this seems to be the correct method if let Some(default_display) = &Display::default() { @@ -57,11 +62,11 @@ impl GridItem { gtk4::TextDirection::None, gtk4::IconLookupFlags::empty(), ) { - drag.set_icon(Some(&paintable_icon), 32, 32); + _self.set_icon(Some(&paintable_icon), 32, 32); } } } - } + })); } pub fn set_group_info(&self, app_group: AppGroup) { diff --git a/examples/dock/dock_item/imp.rs b/examples/dock/dock_item/imp.rs index 9592431..0b75264 100644 --- a/examples/dock/dock_item/imp.rs +++ b/examples/dock/dock_item/imp.rs @@ -2,6 +2,8 @@ use gtk::glib; use gtk::prelude::*; use gtk::subclass::prelude::*; use gtk4 as gtk; +use gtk4::DragSource; +use once_cell::sync::OnceCell; use gtk::CompositeTemplate; @@ -10,6 +12,7 @@ use gtk::CompositeTemplate; pub struct DockItem { #[template_child] pub image: TemplateChild, + pub drag_controller: OnceCell, } #[glib::object_subclass] diff --git a/examples/dock/dock_item/mod.rs b/examples/dock/dock_item/mod.rs index f702c1b..22e82c5 100644 --- a/examples/dock/dock_item/mod.rs +++ b/examples/dock/dock_item/mod.rs @@ -1,15 +1,14 @@ -use crate::icon_source; use gdk4::ContentProvider; use gdk4::Display; use gio::File; -use glib::FromVariant; -use glib::Variant; +use gio::Icon; +use gio::ListStore; use gtk4 as gtk; use gtk4::DragSource; use gtk4::IconTheme; +use gtk4::TreeIter; mod imp; -use crate::ApplicationObject; use gtk::glib; use gtk::prelude::*; use gtk::subclass::prelude::*; @@ -27,44 +26,94 @@ impl Default for DockItem { impl DockItem { pub fn new() -> Self { - glib::Object::new(&[]).expect("Failed to create DockItem") + let dc = DragSource::builder() + .name("dock drag source") + .actions(gdk4::DragAction::MOVE) + .build(); + + let self_: DockItem = glib::Object::new(&[]).expect("Failed to create DockItem"); + self_.add_controller(&dc); + imp::DockItem::from_instance(&self_) + .drag_controller + .set(dc) + .expect("Failed to set dock item"); + self_ } - pub fn set_app_info(&self, app_info: &gio::DesktopAppInfo) { + pub fn drag_controller(&self) -> &DragSource { + let imp = imp::DockItem::from_instance(self); + imp.drag_controller + .get() + .expect("Could not get drag_controller") + } + + // TODO current method seems very messy... + // refactor to emit event for removing the item? + pub fn set_app_info( + &self, + app_info: &gio::DesktopAppInfo, + i: u32, + saved_app_model: &ListStore, + ) { dbg!("setting app info"); let self_ = imp::DockItem::from_instance(self); self_.image.set_tooltip_text(Some(&app_info.name())); - let drag = DragSource::builder() - .name("application library drag source") - .actions(gdk4::DragAction::COPY) - // .content() - .build(); - self.add_controller(&drag); - if let Some(file) = app_info.filename() { - let file = File::for_path(file); - let provider = ContentProvider::for_value(&file.to_value()); - drag.set_content(Some(&provider)); - } - if let Some(icon) = app_info.icon() { - dbg!("setting icon {}", app_info.name()); - self_.image.set_from_gicon(&icon); - // set drag source icon if possible... - // gio Icon is not easily converted to a Paintable, but this seems to be the correct method - if let Some(default_display) = &Display::default() { - if let Some(icon_theme) = IconTheme::for_display(default_display) { - if let Some(paintable_icon) = icon_theme.lookup_by_gicon( - &icon, - 64, - 1, - gtk4::TextDirection::None, - gtk4::IconLookupFlags::empty(), - ) { - drag.set_icon(Some(&paintable_icon), 32, 32); + if let Some(drag_controller) = self_.drag_controller.get() { + // if let Some(file) = app_info.filename() { + // let file = File::for_path(file); + let provider = ContentProvider::for_value(&app_info.to_value()); + drag_controller.set_content(Some(&provider)); + // } + drag_controller.connect_drag_end(move |_self, _drag, delete_data| { + dbg!("removing", delete_data); + }); + drag_controller.connect_drag_cancel( + glib::clone!(@weak saved_app_model => @default-return true, move |_self, _drag, _delete_data| { + dbg!("removing {}", i); + if saved_app_model.n_items() > i { + saved_app_model.remove(i); + } + true + }), + ); + drag_controller.connect_drag_end( + glib::clone!(@weak saved_app_model => move |_self, _drag, delete_data| { + dbg!("removing {}", i); + if delete_data && saved_app_model.n_items() > i { + saved_app_model.remove(i); + } + }), + ); + + let icon = app_info + .icon() + .unwrap_or(Icon::for_string("image-missing").expect("Failed to set default icon")); + drag_controller.connect_drag_begin(glib::clone!(@weak icon, => move |_self, drag| { + drag.set_selected_action(gdk4::DragAction::MOVE); + // set drag source icon if possible... + // gio Icon is not easily converted to a Paintable, but this seems to be the correct method + if let Some(default_display) = &Display::default() { + if let Some(icon_theme) = IconTheme::for_display(default_display) { + if let Some(paintable_icon) = icon_theme.lookup_by_gicon( + &icon, + 64, + 1, + gtk4::TextDirection::None, + gtk4::IconLookupFlags::empty(), + ) { + _self.set_icon(Some(&paintable_icon), 32, 32); + } } } - } + })); } + + let icon = app_info + .icon() + .unwrap_or(Icon::for_string("image-missing").expect("Failed to set default icon")); + + self_.image.set_from_gicon(&icon); } // pub fn set_app_info(&self, app_obj: ApplicationObject) { diff --git a/examples/dock/main.rs b/examples/dock/main.rs index 060b771..08124ac 100644 --- a/examples/dock/main.rs +++ b/examples/dock/main.rs @@ -1,5 +1,6 @@ mod application_object; mod dock_item; +mod utils; mod window; use gdk4::Display; diff --git a/examples/dock/style.css b/examples/dock/style.css index 4e838cc..520093a 100644 --- a/examples/dock/style.css +++ b/examples/dock/style.css @@ -15,7 +15,6 @@ window { background: rgba(50,50,50,0.0); } box#dock-container { - padding-right: 12px; border-radius: 12px; background: #333333; } diff --git a/examples/dock/utils.rs b/examples/dock/utils.rs new file mode 100644 index 0000000..3f03b5b --- /dev/null +++ b/examples/dock/utils.rs @@ -0,0 +1,42 @@ +use gdk4::ContentProvider; +use gdk4::Display; +use gio::File; +use gio::Icon; +use gtk4 as gtk; +use gtk4::DragSource; +use gtk4::IconTheme; + +use gtk::glib; +use gtk::prelude::*; + +pub fn init_drag_controller(drag_controller: &DragSource, app_info: &gio::DesktopAppInfo) { + if let Some(file) = app_info.filename() { + let file = File::for_path(file); + let provider = ContentProvider::for_value(&file.to_value()); + drag_controller.set_content(Some(&provider)); + } + drag_controller.connect_drag_end(move |_self, _drag, delete_data| { + dbg!("removing", delete_data); + }); + let icon = app_info + .icon() + .unwrap_or(Icon::for_string("image-missing").expect("Failed to set default icon")); + drag_controller.connect_drag_begin(glib::clone!(@weak icon, => move |_self, drag| { + drag.set_selected_action(gdk4::DragAction::MOVE); + // set drag source icon if possible... + // gio Icon is not easily converted to a Paintable, but this seems to be the correct method + if let Some(default_display) = &Display::default() { + if let Some(icon_theme) = IconTheme::for_display(default_display) { + if let Some(paintable_icon) = icon_theme.lookup_by_gicon( + &icon, + 64, + 1, + gtk4::TextDirection::None, + gtk4::IconLookupFlags::empty(), + ) { + _self.set_icon(Some(&paintable_icon), 32, 32); + } + } + } + })); +} diff --git a/examples/dock/window/imp.rs b/examples/dock/window/imp.rs index 037e17c..74b30c0 100644 --- a/examples/dock/window/imp.rs +++ b/examples/dock/window/imp.rs @@ -1,5 +1,6 @@ use gtk4 as gtk; use gtk4::Box; +use gtk4::DropTarget; use gtk4::EventControllerMotion; use gtk4::Revealer; @@ -28,6 +29,7 @@ pub struct Window { pub unsaved_open_app_model: OnceCell, pub enter_event_controller: OnceCell, pub leave_event_controller: OnceCell, + pub drop_controller: OnceCell, } // The central trait for subclassing a GObject @@ -54,6 +56,7 @@ impl ObjectImpl for Window { // Setup obj.setup_event_controller(); + obj.setup_drop_target(); obj.setup_model(); obj.setup_callbacks(); obj.setup_factory(); diff --git a/examples/dock/window/mod.rs b/examples/dock/window/mod.rs index 5865ec5..68a6d93 100644 --- a/examples/dock/window/mod.rs +++ b/examples/dock/window/mod.rs @@ -5,9 +5,14 @@ use crate::X11_CONN; use gdk4::Rectangle; use gdk4::Surface; use gdk4_x11::X11Surface; +use gio::Cancellable; use gio::DesktopAppInfo; use gtk4 as gtk; +use gtk4::DropTarget; +use gtk4::DropTargetAsync; use gtk4::EventControllerMotion; +use gtk4::TreeIter; +use std::path::Path; use x11rb::connection::Connection; use x11rb::protocol::xproto::ConnectionExt; @@ -97,6 +102,10 @@ impl Window { let imp = imp::Window::from_instance(self); let window = self.clone().upcast::(); let saved_app_list_view = &imp.saved_app_list_view; + let saved_app_model = &imp + .saved_app_model + .get() + .expect("Failed to get saved app model"); let saved_app_selection_model = saved_app_list_view .model() @@ -128,12 +137,14 @@ impl Window { let enter_event_controller = &imp.enter_event_controller.get().unwrap(); let leave_event_controller = &imp.leave_event_controller.get().unwrap(); + let drop_controller = &imp.drop_controller.get().unwrap(); let revealer = &imp.revealer.get(); window.connect_show( glib::clone!(@weak revealer, @weak leave_event_controller => move |_| { dbg!(!leave_event_controller.contains_pointer()); if !leave_event_controller.contains_pointer() { - revealer.set_reveal_child(false); + // TODO uncomment + // revealer.set_reveal_child(false); } }), ); @@ -197,10 +208,40 @@ impl Window { dbg!("hello, mouse entered me :)"); revealer.set_reveal_child(true); })); + // TODO uncomment.. + // Temporarily disable hiding for dnd testing while the other options are being figured out.. leave_event_controller.connect_leave(glib::clone!(@weak revealer => move |_evc| { dbg!("hello, mouse left me :)"); - revealer.set_reveal_child(false); + // revealer.set_reveal_child(false); })); + + // drop_controller.connect_enter(move |_self, drag, x, y| { + // dbg!(x); + // dbg!(y); + // match drag.drag() { + // Some(d) => d.selected_action(), + // None => drag.actions(), + // } + // }); + drop_controller.connect_drop( + glib::clone!(@weak saved_app_model => @default-return true, move |_self, drop_value, x, y| { + dbg!("dropped it!"); + if let Ok(Some(path)) = drop_value.get::>() { + dbg!(&path); + if let Some(path) = &Path::new(&path).file_name() { + if let Some(app_info) = gio::DesktopAppInfo::new(&path.to_string_lossy()) { + dbg!(app_info.name()); + saved_app_model.append(&app_info); + } + } + } + else { + dbg!("rejecting drop"); + _self.reject(); + } + true + }), + ); } fn setup_event_controller(&self) { @@ -226,13 +267,34 @@ impl Window { .expect("Could not set event controller"); } + fn setup_drop_target(&self) { + let imp = imp::Window::from_instance(self); + let drop_target_widget = &imp.saved_app_list_view; + let mut drop_actions = gdk4::DragAction::COPY; + drop_actions.toggle(gdk4::DragAction::MOVE); + let drop_target_controller = DropTarget::builder() + .preload(true) + .actions(drop_actions) + .formats(&gdk4::ContentFormats::for_type(glib::types::Type::STRING)) + .build(); + drop_target_widget.add_controller(&drop_target_controller); + imp.drop_controller + .set(drop_target_controller) + .expect("Could not set dock dnd drop controller"); + } + fn setup_factory(&self) { let factory = SignalListItemFactory::new(); factory.connect_setup(move |_, list_item| { let dock_item = DockItem::new(); list_item.set_child(Some(&dock_item)); }); - factory.connect_bind(move |_, list_item| { + let imp = imp::Window::from_instance(self); + let saved_app_model = imp + .saved_app_model + .get() + .expect("Failed to get saved app model."); + factory.connect_bind(glib::clone!(@weak saved_app_model => move |_, list_item| { let application_object = list_item .item() .expect("The item has to exist.") @@ -244,10 +306,10 @@ impl Window { .downcast::() .expect("The list item type needs to be `DockItem`"); - dock_item.set_app_info(&application_object); - }); + let i = list_item.position(); + dock_item.set_app_info(&application_object, i, &saved_app_model); + })); // Set the factory of the list view - let imp = imp::Window::from_instance(self); imp.saved_app_list_view.set_factory(Some(&factory)); } } diff --git a/examples/dock/window/window.ui b/examples/dock/window/window.ui index f4cd9b5..09df77d 100644 --- a/examples/dock/window/window.ui +++ b/examples/dock/window/window.ui @@ -2,6 +2,7 @@