Skip to main content

fractal/account_switcher/
account_switcher_button.rs

1use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
2
3use super::AccountSwitcherPopover;
4use crate::{
5    Window,
6    components::Avatar,
7    session_list::SessionInfo,
8    utils::{BoundObjectWeakRef, TemplateCallbacks},
9};
10
11mod imp {
12    use glib::subclass::InitializingObject;
13
14    use super::*;
15
16    #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
17    #[template(resource = "/org/gnome/Fractal/ui/account_switcher/account_switcher_button.ui")]
18    #[properties(wrapper_type = super::AccountSwitcherButton)]
19    pub struct AccountSwitcherButton {
20        /// The popover of this button.
21        #[property(get, set = Self::set_popover, explicit_notify, nullable)]
22        popover: BoundObjectWeakRef<AccountSwitcherPopover>,
23    }
24
25    #[glib::object_subclass]
26    impl ObjectSubclass for AccountSwitcherButton {
27        const NAME: &'static str = "AccountSwitcherButton";
28        type Type = super::AccountSwitcherButton;
29        type ParentType = gtk::ToggleButton;
30
31        fn class_init(klass: &mut Self::Class) {
32            Avatar::ensure_type();
33            SessionInfo::ensure_type();
34
35            Self::bind_template(klass);
36            Self::bind_template_callbacks(klass);
37            TemplateCallbacks::bind_template_callbacks(klass);
38        }
39
40        fn instance_init(obj: &InitializingObject<Self>) {
41            obj.init_template();
42        }
43    }
44
45    #[glib::derived_properties]
46    impl ObjectImpl for AccountSwitcherButton {
47        fn dispose(&self) {
48            self.reset();
49        }
50    }
51
52    impl WidgetImpl for AccountSwitcherButton {}
53    impl ButtonImpl for AccountSwitcherButton {}
54    impl ToggleButtonImpl for AccountSwitcherButton {}
55
56    #[gtk::template_callbacks]
57    impl AccountSwitcherButton {
58        /// Set the popover of this button.
59        fn set_popover(&self, popover: Option<&AccountSwitcherPopover>) {
60            if self.popover.obj().as_ref() == popover {
61                return;
62            }
63
64            // Reset the state.
65            self.reset();
66            let obj = self.obj();
67
68            if let Some(popover) = popover {
69                // We need to remove the popover from the previous button, if any.
70                if let Some(parent) = popover
71                    .parent()
72                    .and_downcast::<super::AccountSwitcherButton>()
73                {
74                    parent.set_popover(None::<AccountSwitcherPopover>);
75                }
76
77                let closed_handler = popover.connect_closed(clone!(
78                    #[weak]
79                    obj,
80                    move |_| {
81                        obj.set_active(false);
82                    }
83                ));
84
85                popover.set_parent(&*obj);
86                self.popover.set(popover, vec![closed_handler]);
87            }
88
89            obj.notify_popover();
90        }
91
92        /// Toggle the popover of this button.
93        #[template_callback]
94        fn toggle_popover(&self) {
95            let obj = self.obj();
96
97            if obj.is_active() {
98                let Some(window) = obj.root().and_downcast::<Window>() else {
99                    return;
100                };
101
102                let popover = window.account_switcher();
103                self.set_popover(Some(popover));
104
105                popover.popup();
106            } else if let Some(popover) = self.popover.obj() {
107                popover.popdown();
108            }
109        }
110
111        /// Reset the state of this button.
112        fn reset(&self) {
113            if let Some(popover) = self.popover.obj() {
114                popover.unparent();
115            }
116            self.popover.disconnect_signals();
117            self.obj().set_active(false);
118        }
119    }
120}
121
122glib::wrapper! {
123    /// A button showing the currently selected session and opening the account switcher popover.
124    pub struct AccountSwitcherButton(ObjectSubclass<imp::AccountSwitcherButton>)
125        @extends gtk::Widget, gtk::Button, gtk::ToggleButton,
126        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Actionable;
127}
128
129#[gtk::template_callbacks]
130impl AccountSwitcherButton {
131    pub fn new() -> Self {
132        glib::Object::new()
133    }
134}
135
136impl Default for AccountSwitcherButton {
137    fn default() -> Self {
138        Self::new()
139    }
140}