fractal/account_chooser_dialog/
mod.rs

1use adw::{prelude::*, subclass::prelude::*};
2use gtk::glib;
3
4mod account_row;
5
6use self::account_row::AccountRow;
7use crate::{
8    session_list::{SessionInfo, SessionList},
9    utils::OneshotNotifier,
10};
11
12mod imp {
13    use std::cell::OnceCell;
14
15    use glib::subclass::InitializingObject;
16
17    use super::*;
18
19    #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
20    #[template(resource = "/org/gnome/Fractal/ui/account_chooser_dialog/mod.ui")]
21    #[properties(wrapper_type = super::AccountChooserDialog)]
22    pub struct AccountChooserDialog {
23        #[template_child]
24        pub accounts: TemplateChild<gtk::ListBox>,
25        /// The list of logged-in sessions.
26        #[property(get, set = Self::set_session_list, construct)]
27        pub session_list: glib::WeakRef<SessionList>,
28        notifier: OnceCell<OneshotNotifier<Option<String>>>,
29    }
30
31    #[glib::object_subclass]
32    impl ObjectSubclass for AccountChooserDialog {
33        const NAME: &'static str = "AccountChooserDialog";
34        type Type = super::AccountChooserDialog;
35        type ParentType = adw::Dialog;
36
37        fn class_init(klass: &mut Self::Class) {
38            Self::bind_template(klass);
39            Self::Type::bind_template_callbacks(klass);
40        }
41
42        fn instance_init(obj: &InitializingObject<Self>) {
43            obj.init_template();
44        }
45    }
46
47    #[glib::derived_properties]
48    impl ObjectImpl for AccountChooserDialog {}
49
50    impl WidgetImpl for AccountChooserDialog {}
51
52    impl AdwDialogImpl for AccountChooserDialog {
53        fn closed(&self) {
54            if let Some(notifier) = self.notifier.get() {
55                notifier.notify();
56            }
57        }
58    }
59
60    impl AccountChooserDialog {
61        /// The notifier for sending the response.
62        pub(super) fn notifier(&self) -> &OneshotNotifier<Option<String>> {
63            self.notifier
64                .get_or_init(|| OneshotNotifier::new("AccountChooserDialog"))
65        }
66
67        /// Set the list of logged-in sessions.
68        fn set_session_list(&self, session_list: &SessionList) {
69            self.accounts.bind_model(Some(session_list), |session| {
70                let row = AccountRow::new(session.downcast_ref().unwrap());
71                row.upcast()
72            });
73
74            self.session_list.set(Some(session_list));
75        }
76    }
77}
78
79glib::wrapper! {
80    /// A dialog to choose an account among the ones that are connected.
81    ///
82    /// Should be used by calling [`Self::choose_account()`].
83    pub struct AccountChooserDialog(ObjectSubclass<imp::AccountChooserDialog>)
84        @extends gtk::Widget, adw::Dialog,
85        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager;
86}
87
88#[gtk::template_callbacks]
89impl AccountChooserDialog {
90    pub fn new(session_list: &SessionList) -> Self {
91        glib::Object::builder()
92            .property("session-list", session_list)
93            .build()
94    }
95
96    /// Open this dialog to choose an account.
97    pub async fn choose_account(&self, parent: &impl IsA<gtk::Widget>) -> Option<String> {
98        let receiver = self.imp().notifier().listen();
99
100        self.present(Some(parent));
101
102        receiver.await
103    }
104
105    /// Select the given row in the session list.
106    #[template_callback]
107    fn select_row(&self, row: &gtk::ListBoxRow) {
108        let index = row
109            .index()
110            .try_into()
111            .expect("selected row should have an index");
112
113        let session_id = self
114            .session_list()
115            .and_then(|l| l.item(index))
116            .and_downcast::<SessionInfo>()
117            .map(|s| s.session_id());
118
119        self.imp().notifier().notify_value(session_id);
120        self.close();
121    }
122}