fractal/account_chooser_dialog/
account_row.rs1use 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 #[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 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 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}