fractal/account_chooser_dialog/
account_row.rs

1use gtk::{self, glib, prelude::*, subclass::prelude::*, CompositeTemplate};
2
3use crate::{
4    components::{Avatar, AvatarData},
5    prelude::*,
6    session::model::Session,
7    session_list::{FailedSession, SessionInfo},
8};
9
10mod imp {
11    use std::cell::RefCell;
12
13    use glib::subclass::InitializingObject;
14
15    use super::*;
16
17    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
18    #[template(resource = "/org/gnome/Fractal/ui/account_chooser_dialog/account_row.ui")]
19    #[properties(wrapper_type = super::AccountRow)]
20    pub struct AccountRow {
21        #[template_child]
22        pub avatar: TemplateChild<Avatar>,
23        #[template_child]
24        pub display_name: TemplateChild<gtk::Label>,
25        #[template_child]
26        pub user_id: TemplateChild<gtk::Label>,
27        #[template_child]
28        pub state_stack: TemplateChild<gtk::Stack>,
29        #[template_child]
30        pub error_image: TemplateChild<gtk::Image>,
31        /// The session this item represents.
32        #[property(get, set = Self::set_session, explicit_notify)]
33        pub session: glib::WeakRef<SessionInfo>,
34        pub user_bindings: RefCell<Vec<glib::Binding>>,
35    }
36
37    #[glib::object_subclass]
38    impl ObjectSubclass for AccountRow {
39        const NAME: &'static str = "AccountChooserDialogRow";
40        type Type = super::AccountRow;
41        type ParentType = gtk::ListBoxRow;
42
43        fn class_init(klass: &mut Self::Class) {
44            Self::bind_template(klass);
45        }
46
47        fn instance_init(obj: &InitializingObject<Self>) {
48            obj.init_template();
49        }
50    }
51
52    #[glib::derived_properties]
53    impl ObjectImpl for AccountRow {
54        fn dispose(&self) {
55            for binding in self.user_bindings.take() {
56                binding.unbind();
57            }
58        }
59    }
60
61    impl WidgetImpl for AccountRow {}
62    impl ListBoxRowImpl for AccountRow {}
63
64    impl AccountRow {
65        /// Set the session this item represents.
66        fn set_session(&self, session: Option<&SessionInfo>) {
67            if self.session.upgrade().as_ref() == session {
68                return;
69            }
70
71            for binding in self.user_bindings.take() {
72                binding.unbind();
73            }
74
75            if let Some(session) = session {
76                if let Some(session) = session.downcast_ref::<Session>() {
77                    let user = session.user();
78
79                    let avatar_data_handler = user
80                        .bind_property("avatar-data", &*self.avatar, "data")
81                        .sync_create()
82                        .build();
83                    let display_name_handler = user
84                        .bind_property("display-name", &*self.display_name, "label")
85                        .sync_create()
86                        .build();
87                    self.user_bindings
88                        .borrow_mut()
89                        .extend([avatar_data_handler, display_name_handler]);
90
91                    self.user_id.set_label(session.user_id().as_str());
92                    self.user_id.set_visible(true);
93
94                    self.state_stack.set_visible(false);
95                } else {
96                    let user_id = session.user_id().to_string();
97
98                    let avatar_data = AvatarData::new();
99                    avatar_data.set_display_name(user_id.clone());
100                    self.avatar.set_data(Some(avatar_data));
101
102                    self.display_name.set_label(&user_id);
103                    self.user_id.set_visible(false);
104
105                    if let Some(failed) = session.downcast_ref::<FailedSession>() {
106                        self.error_image
107                            .set_tooltip_text(Some(&failed.error().to_user_facing()));
108                        self.state_stack.set_visible_child_name("error");
109                    } else {
110                        self.state_stack.set_visible_child_name("loading");
111                    }
112                    self.state_stack.set_visible(true);
113                }
114            }
115
116            self.session.set(session);
117            self.obj().notify_session();
118        }
119    }
120}
121
122glib::wrapper! {
123    /// A `GtkListBoxRow` representing a logged-in session in the `AccountChooserDialog`.
124    pub struct AccountRow(ObjectSubclass<imp::AccountRow>)
125        @extends gtk::Widget, gtk::ListBoxRow, @implements gtk::Accessible;
126}
127
128impl AccountRow {
129    pub fn new(session: &SessionInfo) -> Self {
130        glib::Object::builder().property("session", session).build()
131    }
132}