[fractal/fractal-next] Make a drag and drop overlay widget



commit ec2852dc90225c774b2ea47af3d759a66cbad4f7
Author: Maximiliano Sandoval R <msandova gnome org>
Date:   Sat Dec 25 23:33:36 2021 +0100

    Make a drag and drop overlay widget

 data/resources/ui/content-room-history.ui |  59 +++++-------
 src/components/drag_overlay.rs            | 143 ++++++++++++++++++++++++++++++
 src/components/mod.rs                     |   2 +
 src/session/content/room_history/mod.rs   |  11 +--
 4 files changed, 172 insertions(+), 43 deletions(-)
---
diff --git a/data/resources/ui/content-room-history.ui b/data/resources/ui/content-room-history.ui
index 0a729ba89..081ab7806 100644
--- a/data/resources/ui/content-room-history.ui
+++ b/data/resources/ui/content-room-history.ui
@@ -129,22 +129,6 @@
             </child>
             <child>
               <object class="GtkOverlay" id="content">
-                <child type="overlay">
-                  <object class="GtkRevealer" id="drag_revealer">
-                    <property name="can-target">False</property>
-                    <property name="transition_type">crossfade</property>
-                    <property name="reveal_child">False</property>
-                    <child>
-                      <object class="AdwStatusPage">
-                        <property name="icon-name">document-send-symbolic</property>
-                        <property name="title" translatable="yes">Drop Here to Send</property>
-                        <style>
-                          <class name="drag-n-drop-overlay"/>
-                        </style>
-                      </object>
-                    </child>
-                  </object>
-                </child>
                 <child type="overlay">
                   <object class="GtkRevealer" id="scroll_btn_revealer">
                     <property name="transition_type">crossfade</property>
@@ -169,30 +153,35 @@
                   </object>
                 </child>
                 <child>
-                  <object class="GtkScrolledWindow" id="scrolled_window">
-                    <property name="vexpand">True</property>
-                    <property name="hscrollbar-policy">never</property>
-                    <style>
-                      <class name="room-history"/>
-                    </style>
+                  <object class="DragOverlay" id="drag_overlay">
+                    <property name="title" translatable="yes">Drop Here to Send</property>
                     <property name="child">
-                      <object class="AdwClampScrollable">
+                      <object class="GtkScrolledWindow" id="scrolled_window">
                         <property name="vexpand">True</property>
-                        <property name="hexpand">True</property>
+                        <property name="hscrollbar-policy">never</property>
+                        <style>
+                          <class name="room-history"/>
+                        </style>
                         <property name="child">
-                          <object class="GtkListView" id="listview">
-                            <style>
-                              <class name="navigation-sidebar"/>
-                            </style>
-                            <property name="factory">
-                              <object class="GtkBuilderListItemFactory">
-                                <property name="resource">/org/gnome/FractalNext/content-item.ui</property>
+                          <object class="AdwClampScrollable">
+                            <property name="vexpand">True</property>
+                            <property name="hexpand">True</property>
+                            <property name="child">
+                              <object class="GtkListView" id="listview">
+                                <style>
+                                  <class name="navigation-sidebar"/>
+                                </style>
+                                <property name="factory">
+                                  <object class="GtkBuilderListItemFactory">
+                                    <property 
name="resource">/org/gnome/FractalNext/content-item.ui</property>
+                                  </object>
+                                </property>
+                                <property name="single-click-activate">True</property>
+                                <accessibility>
+                                  <property name="label" translatable="yes">Room History</property>
+                                </accessibility>
                               </object>
                             </property>
-                            <property name="single-click-activate">True</property>
-                            <accessibility>
-                              <property name="label" translatable="yes">Room History</property>
-                            </accessibility>
                           </object>
                         </property>
                       </object>
diff --git a/src/components/drag_overlay.rs b/src/components/drag_overlay.rs
new file mode 100644
index 000000000..93098dd10
--- /dev/null
+++ b/src/components/drag_overlay.rs
@@ -0,0 +1,143 @@
+use gtk::{glib, prelude::*, subclass::prelude::*};
+
+mod imp {
+    use std::cell::RefCell;
+
+    use adw::subclass::prelude::*;
+    use once_cell::sync::Lazy;
+
+    use super::*;
+
+    #[derive(Debug, Default)]
+    pub struct DragOverlay {
+        pub overlay: gtk::Overlay,
+        pub revealer: gtk::Revealer,
+        pub status: adw::StatusPage,
+        pub drop_target: RefCell<Option<gtk::DropTarget>>,
+        pub handler_id: RefCell<Option<glib::SignalHandlerId>>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for DragOverlay {
+        const NAME: &'static str = "DragOverlay";
+        type Type = super::DragOverlay;
+        type ParentType = adw::Bin;
+
+        fn class_init(klass: &mut Self::Class) {
+            klass.set_css_name("dragoverlay");
+        }
+    }
+
+    impl ObjectImpl for DragOverlay {
+        fn properties() -> &'static [glib::ParamSpec] {
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![
+                    glib::ParamSpecString::new(
+                        "title",
+                        "title",
+                        "title",
+                        None,
+                        glib::ParamFlags::READWRITE,
+                    ),
+                    glib::ParamSpecObject::new(
+                        "child",
+                        "child",
+                        "child",
+                        gtk::Widget::static_type(),
+                        glib::ParamFlags::READWRITE,
+                    ),
+                    glib::ParamSpecObject::new(
+                        "drop-target",
+                        "drop-target",
+                        "drop-target",
+                        gtk::DropTarget::static_type(),
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+                    ),
+                ]
+            });
+            PROPERTIES.as_ref()
+        }
+
+        fn property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "title" => self.status.title().to_value(),
+                "child" => self.overlay.child().to_value(),
+                "drop-target" => self.drop_target.borrow().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn set_property(
+            &self,
+            obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "title" => self.status.set_title(value.get().unwrap()),
+                "child" => self
+                    .overlay
+                    .set_child(value.get::<gtk::Widget>().ok().as_ref()),
+                "drop-target" => obj.set_drop_target(&value.get::<gtk::DropTarget>().unwrap()),
+                _ => unimplemented!(),
+            };
+        }
+
+        fn constructed(&self, obj: &Self::Type) {
+            self.overlay.set_parent(obj);
+            self.overlay.add_overlay(&self.revealer);
+
+            self.revealer.set_can_target(false);
+            self.revealer
+                .set_transition_type(gtk::RevealerTransitionType::Crossfade);
+            self.revealer.set_reveal_child(false);
+
+            self.status.set_icon_name(Some("document-send-symbolic"));
+
+            self.revealer.set_child(Some(&self.status));
+        }
+    }
+    impl WidgetImpl for DragOverlay {}
+    impl BinImpl for DragOverlay {}
+}
+
+glib::wrapper! {
+    pub struct DragOverlay(ObjectSubclass<imp::DragOverlay>)
+        @extends gtk::Widget, adw::Bin;
+}
+
+impl Default for DragOverlay {
+    fn default() -> Self {
+        glib::Object::new(&[]).unwrap()
+    }
+}
+
+impl DragOverlay {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    pub fn set_drop_target(&self, drop_target: &gtk::DropTarget) {
+        let priv_ = self.imp();
+
+        if let Some(target) = priv_.drop_target.borrow_mut().take() {
+            self.remove_controller(&target);
+
+            if let Some(handler_id) = priv_.handler_id.borrow_mut().take() {
+                target.disconnect(handler_id);
+            }
+        }
+
+        let handler_id = drop_target.connect_current_drop_notify(
+            glib::clone!(@weak priv_.revealer as revealer => move |target| {
+                revealer.set_reveal_child(target.current_drop().is_some());
+            }),
+        );
+        priv_.handler_id.replace(Some(handler_id));
+
+        self.add_controller(drop_target);
+        priv_.drop_target.replace(Some(drop_target.clone()));
+        self.notify("drop-target");
+    }
+}
diff --git a/src/components/mod.rs b/src/components/mod.rs
index 4a7118ca9..06958227e 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -6,6 +6,7 @@ mod badge;
 mod button_row;
 mod context_menu_bin;
 mod custom_entry;
+mod drag_overlay;
 mod editable_avatar;
 mod entry_row;
 mod in_app_notification;
@@ -29,6 +30,7 @@ pub use self::{
     button_row::ButtonRow,
     context_menu_bin::{ContextMenuBin, ContextMenuBinExt, ContextMenuBinImpl},
     custom_entry::CustomEntry,
+    drag_overlay::DragOverlay,
     editable_avatar::EditableAvatar,
     entry_row::EntryRow,
     in_app_notification::InAppNotification,
diff --git a/src/session/content/room_history/mod.rs b/src/session/content/room_history/mod.rs
index 9410994f5..0a98eaaea 100644
--- a/src/session/content/room_history/mod.rs
+++ b/src/session/content/room_history/mod.rs
@@ -26,7 +26,7 @@ use self::{
 };
 use crate::spawn;
 use crate::{
-    components::{CustomEntry, Pill, RoomTitle},
+    components::{CustomEntry, DragOverlay, Pill, RoomTitle},
     session::{
         content::{MarkdownPopover, RoomDetails},
         room::{Item, Room, RoomType, Timeline, TimelineState},
@@ -90,7 +90,7 @@ mod imp {
         pub stack: TemplateChild<gtk::Stack>,
         pub is_loading: Cell<bool>,
         #[template_child]
-        pub drag_revealer: TemplateChild<gtk::Revealer>,
+        pub drag_overlay: TemplateChild<DragOverlay>,
     }
 
     #[glib::object_subclass]
@@ -758,12 +758,7 @@ impl RoomHistory {
             }),
         );
 
-        target.connect_current_drop_notify(glib::clone!(@weak self as obj => move |target| {
-            let priv_ = imp::RoomHistory::from_instance(&obj);
-            priv_.drag_revealer.set_reveal_child(target.current_drop().is_some());
-        }));
-
-        priv_.scrolled_window.add_controller(&target);
+        priv_.drag_overlay.set_drop_target(&target);
     }
 
     fn open_attach_dialog(&self, bytes: Vec<u8>, mime: &str, title: &str) {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]