Skip to main content

fractal/components/dialogs/
toastable.rs

1use adw::{prelude::*, subclass::prelude::*};
2use gtk::glib;
3
4mod imp {
5    use std::marker::PhantomData;
6
7    use glib::subclass::InitializingObject;
8
9    use super::*;
10
11    #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
12    #[template(resource = "/org/gnome/Fractal/ui/components/dialogs/toastable.ui")]
13    #[properties(wrapper_type = super::ToastableDialog)]
14    pub struct ToastableDialog {
15        #[template_child]
16        toast_overlay: TemplateChild<adw::ToastOverlay>,
17        /// The child widget containing the content of this dialog.
18        #[property(get = Self::child_content, set = Self::set_child_content, nullable)]
19        child_content: PhantomData<Option<gtk::Widget>>,
20    }
21
22    #[glib::object_subclass]
23    impl ObjectSubclass for ToastableDialog {
24        const NAME: &'static str = "ToastableDialog";
25        const ABSTRACT: bool = true;
26        type Type = super::ToastableDialog;
27        type ParentType = adw::Dialog;
28
29        fn class_init(klass: &mut Self::Class) {
30            Self::bind_template(klass);
31        }
32
33        fn instance_init(obj: &InitializingObject<Self>) {
34            obj.init_template();
35        }
36    }
37
38    #[glib::derived_properties]
39    impl ObjectImpl for ToastableDialog {}
40
41    impl WidgetImpl for ToastableDialog {}
42    impl AdwDialogImpl for ToastableDialog {}
43
44    impl ToastableDialog {
45        /// The child widget containing the content of this dialog.
46        fn child_content(&self) -> Option<gtk::Widget> {
47            self.toast_overlay.child()
48        }
49
50        /// Set the child widget containing the content of this dialog.
51        fn set_child_content(&self, content: Option<&gtk::Widget>) {
52            self.toast_overlay.set_child(content);
53        }
54
55        /// Present the given toast in this dialog.
56        pub(super) fn add_toast(&self, toast: adw::Toast) {
57            self.toast_overlay.add_toast(toast);
58        }
59    }
60}
61
62glib::wrapper! {
63    /// A dialog that can display toasts.
64    pub struct ToastableDialog(ObjectSubclass<imp::ToastableDialog>)
65        @extends gtk::Widget, adw::Dialog,
66        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager;
67}
68
69pub trait ToastableDialogExt: 'static {
70    /// Get the content of this dialog.
71    #[allow(dead_code)]
72    fn child_content(&self) -> Option<gtk::Widget>;
73
74    /// Set the content of this dialog.
75    ///
76    /// Use this instead of `set_child` or `set_content`, otherwise it will
77    /// panic.
78    #[allow(dead_code)]
79    fn set_child_content(&self, content: Option<&gtk::Widget>);
80
81    /// Add a toast.
82    fn add_toast(&self, toast: adw::Toast);
83}
84
85impl<O: IsA<ToastableDialog>> ToastableDialogExt for O {
86    fn child_content(&self) -> Option<gtk::Widget> {
87        self.upcast_ref().child_content()
88    }
89
90    fn set_child_content(&self, content: Option<&gtk::Widget>) {
91        self.upcast_ref().set_child_content(content);
92    }
93
94    fn add_toast(&self, toast: adw::Toast) {
95        self.upcast_ref().imp().add_toast(toast);
96    }
97}
98
99/// Public trait that must be implemented for everything that derives from
100/// `ToastableDialog`.
101pub trait ToastableDialogImpl: AdwDialogImpl {}
102
103unsafe impl<T> IsSubclassable<T> for ToastableDialog where T: ToastableDialogImpl {}