Skip to main content

fractal/components/
drag_overlay.rs

1use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
2
3use crate::utils::BoundObject;
4
5mod imp {
6    use std::marker::PhantomData;
7
8    use super::*;
9
10    #[derive(Debug, Default, glib::Properties)]
11    #[properties(wrapper_type = super::DragOverlay)]
12    pub struct DragOverlay {
13        overlay: gtk::Overlay,
14        revealer: gtk::Revealer,
15        status: adw::StatusPage,
16        /// The title of this `DragOverlay`.
17        #[property(get = Self::title, set = Self::set_title)]
18        title: PhantomData<glib::GString>,
19        /// The child of this `DragOverlay`.
20        #[property(get = Self::child, set = Self::set_child, nullable)]
21        child: PhantomData<Option<gtk::Widget>>,
22        /// The [`gtk::DropTarget`] of this `DragOverlay`.
23        #[property(get, set = Self::set_drop_target)]
24        drop_target: BoundObject<gtk::DropTarget>,
25    }
26
27    #[glib::object_subclass]
28    impl ObjectSubclass for DragOverlay {
29        const NAME: &'static str = "DragOverlay";
30        type Type = super::DragOverlay;
31        type ParentType = gtk::Widget;
32
33        fn class_init(klass: &mut Self::Class) {
34            klass.set_css_name("dragoverlay");
35            klass.set_layout_manager_type::<gtk::BinLayout>();
36        }
37    }
38
39    #[glib::derived_properties]
40    impl ObjectImpl for DragOverlay {
41        fn constructed(&self) {
42            let obj = self.obj();
43
44            self.overlay.set_parent(&*obj);
45            self.overlay.add_overlay(&self.revealer);
46
47            self.revealer.set_can_target(false);
48            self.revealer
49                .set_transition_type(gtk::RevealerTransitionType::Crossfade);
50            self.revealer.set_reveal_child(false);
51            self.revealer.set_visible(false);
52
53            self.status.set_icon_name(Some("attachment-symbolic"));
54
55            self.revealer.set_child(Some(&self.status));
56
57            self.revealer.connect_child_revealed_notify(|revealer| {
58                // Hide the revealer when we don't want to show the child and the animation is
59                // finished.
60                if !revealer.reveals_child() && !revealer.is_child_revealed() {
61                    revealer.set_visible(false);
62                }
63            });
64        }
65
66        fn dispose(&self) {
67            self.overlay.unparent();
68        }
69    }
70
71    impl WidgetImpl for DragOverlay {}
72
73    impl DragOverlay {
74        /// The title of this `DragOverlay`.
75        fn title(&self) -> glib::GString {
76            self.status.title()
77        }
78
79        /// Set the title of this `DragOverlay`.
80        fn set_title(&self, title: &str) {
81            self.status.set_title(title);
82            self.obj()
83                .update_property(&[gtk::accessible::Property::Label(title)]);
84        }
85
86        /// The child of this `DragOverlay`.
87        fn child(&self) -> Option<gtk::Widget> {
88            self.overlay.child()
89        }
90
91        /// Set the child of this `DragOverlay`.
92        fn set_child(&self, child: Option<&gtk::Widget>) {
93            self.overlay.set_child(child);
94        }
95
96        /// Set the [`gtk::DropTarget`] of this `DragOverlay`.
97        fn set_drop_target(&self, drop_target: gtk::DropTarget) {
98            let obj = self.obj();
99
100            if let Some(target) = self.drop_target.obj() {
101                obj.remove_controller(&target);
102            }
103            self.drop_target.disconnect_signals();
104
105            let handler_id = drop_target.connect_current_drop_notify(clone!(
106                #[weak(rename_to = revealer)]
107                self.revealer,
108                move |target| {
109                    let reveal = target.current_drop().is_some();
110
111                    if reveal {
112                        revealer.set_visible(true);
113                    }
114
115                    revealer.set_reveal_child(reveal);
116                }
117            ));
118
119            obj.add_controller(drop_target.clone());
120            self.drop_target.set(drop_target, vec![handler_id]);
121            obj.notify_drop_target();
122        }
123    }
124}
125
126glib::wrapper! {
127    /// A widget displaying an overlay when something is dragged onto it.
128    pub struct DragOverlay(ObjectSubclass<imp::DragOverlay>)
129        @extends gtk::Widget,
130        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
131}
132
133impl DragOverlay {
134    pub fn new() -> Self {
135        glib::Object::new()
136    }
137}
138
139impl Default for DragOverlay {
140    fn default() -> Self {
141        Self::new()
142    }
143}