1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};

use crate::utils::BoundObject;

mod imp {
    use std::marker::PhantomData;

    use super::*;

    #[derive(Debug, Default, glib::Properties)]
    #[properties(wrapper_type = super::DragOverlay)]
    pub struct DragOverlay {
        pub overlay: gtk::Overlay,
        pub revealer: gtk::Revealer,
        pub status: adw::StatusPage,
        /// The title of this `DragOverlay`.
        #[property(get = Self::title, set = Self::set_title)]
        pub title: PhantomData<glib::GString>,
        /// The child of this `DragOverlay`.
        #[property(get = Self::child, set = Self::set_child, nullable)]
        pub child: PhantomData<Option<gtk::Widget>>,
        /// The [`gtk::DropTarget`] of this `DragOverlay`.
        #[property(get, set = Self::set_drop_target)]
        pub drop_target: BoundObject<gtk::DropTarget>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for DragOverlay {
        const NAME: &'static str = "DragOverlay";
        type Type = super::DragOverlay;
        type ParentType = gtk::Widget;

        fn class_init(klass: &mut Self::Class) {
            klass.set_css_name("dragoverlay");
            klass.set_layout_manager_type::<gtk::BinLayout>();
        }
    }

    #[glib::derived_properties]
    impl ObjectImpl for DragOverlay {
        fn constructed(&self) {
            let obj = self.obj();

            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.revealer.set_visible(false);

            self.status.set_icon_name(Some("attachment-symbolic"));

            self.revealer.set_child(Some(&self.status));

            self.revealer.connect_child_revealed_notify(|revealer| {
                // Hide the revealer when we don't want to show the child and the animation is
                // finished.
                if !revealer.reveals_child() && !revealer.is_child_revealed() {
                    revealer.set_visible(false);
                }
            });
        }

        fn dispose(&self) {
            self.overlay.unparent();
        }
    }

    impl WidgetImpl for DragOverlay {}

    impl DragOverlay {
        /// The title of this `DragOverlay`.
        pub fn title(&self) -> glib::GString {
            self.status.title()
        }

        /// Set the title of this `DragOverlay`.
        pub fn set_title(&self, title: &str) {
            self.status.set_title(title);
            self.obj()
                .update_property(&[gtk::accessible::Property::Label(title)]);
        }

        /// The child of this `DragOverlay`.
        pub fn child(&self) -> Option<gtk::Widget> {
            self.overlay.child()
        }

        /// Set the child of this `DragOverlay`.
        pub fn set_child(&self, child: Option<&gtk::Widget>) {
            self.overlay.set_child(child)
        }

        /// Set the [`gtk::DropTarget`] of this `DragOverlay`.
        fn set_drop_target(&self, drop_target: gtk::DropTarget) {
            let obj = self.obj();

            if let Some(target) = self.drop_target.obj() {
                obj.remove_controller(&target);
            }
            self.drop_target.disconnect_signals();

            let handler_id = drop_target.connect_current_drop_notify(clone!(
                #[weak(rename_to = revealer)]
                self.revealer,
                move |target| {
                    let reveal = target.current_drop().is_some();

                    if reveal {
                        revealer.set_visible(true);
                    }

                    revealer.set_reveal_child(reveal);
                }
            ));

            obj.add_controller(drop_target.clone());
            self.drop_target.set(drop_target, vec![handler_id]);
            obj.notify_drop_target();
        }
    }
}

glib::wrapper! {
    /// A widget displaying an overlay when something is dragged onto it.
    pub struct DragOverlay(ObjectSubclass<imp::DragOverlay>)
        @extends gtk::Widget, @implements gtk::Accessible;
}

impl DragOverlay {
    pub fn new() -> Self {
        glib::Object::new()
    }
}

impl Default for DragOverlay {
    fn default() -> Self {
        Self::new()
    }
}