fractal/components/rows/
loading_row.rs1use glib::subclass::Signal;
2use gtk::{
3 glib,
4 glib::{clone, closure_local},
5 prelude::*,
6 subclass::prelude::*,
7 CompositeTemplate,
8};
9
10use crate::components::LoadingBin;
11
12mod imp {
13 use std::{marker::PhantomData, sync::LazyLock};
14
15 use glib::subclass::InitializingObject;
16
17 use super::*;
18
19 #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
20 #[template(resource = "/org/gnome/Fractal/ui/components/rows/loading_row.ui")]
21 #[properties(wrapper_type = super::LoadingRow)]
22 pub struct LoadingRow {
23 #[template_child]
24 loading_bin: TemplateChild<LoadingBin>,
25 #[template_child]
26 error_label: TemplateChild<gtk::Label>,
27 #[template_child]
28 retry_button: TemplateChild<gtk::Button>,
29 #[property(get = Self::error, set = Self::set_error, explicit_notify, nullable)]
31 error: PhantomData<Option<glib::GString>>,
32 }
33
34 #[glib::object_subclass]
35 impl ObjectSubclass for LoadingRow {
36 const NAME: &'static str = "LoadingRow";
37 type Type = super::LoadingRow;
38 type ParentType = gtk::ListBoxRow;
39
40 fn class_init(klass: &mut Self::Class) {
41 Self::bind_template(klass);
42 }
43
44 fn instance_init(obj: &InitializingObject<Self>) {
45 obj.init_template();
46 }
47 }
48
49 #[glib::derived_properties]
50 impl ObjectImpl for LoadingRow {
51 fn signals() -> &'static [Signal] {
52 static SIGNALS: LazyLock<Vec<Signal>> =
53 LazyLock::new(|| vec![Signal::builder("retry").build()]);
54 SIGNALS.as_ref()
55 }
56
57 fn constructed(&self) {
58 self.parent_constructed();
59 let obj = self.obj();
60
61 self.retry_button.connect_clicked(clone!(
62 #[weak]
63 obj,
64 move |_| {
65 obj.emit_by_name::<()>("retry", &[]);
66 }
67 ));
68 }
69 }
70
71 impl WidgetImpl for LoadingRow {}
72 impl ListBoxRowImpl for LoadingRow {}
73
74 impl LoadingRow {
75 fn error(&self) -> Option<glib::GString> {
77 let message = self.error_label.text();
78 if message.is_empty() {
79 None
80 } else {
81 Some(message)
82 }
83 }
84
85 fn set_error(&self, message: Option<&str>) {
90 if let Some(message) = message {
91 self.error_label.set_text(message);
92 self.loading_bin.set_is_loading(false);
93 } else {
94 self.loading_bin.set_is_loading(true);
95 }
96
97 self.obj().notify_error();
98 }
99 }
100}
101
102glib::wrapper! {
103 pub struct LoadingRow(ObjectSubclass<imp::LoadingRow>)
107 @extends gtk::Widget, gtk::ListBoxRow, @implements gtk::Accessible;
108}
109
110impl LoadingRow {
111 pub fn new() -> Self {
112 glib::Object::new()
113 }
114
115 pub fn connect_retry<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
117 self.connect_closure(
118 "retry",
119 true,
120 closure_local!(move |obj: Self| {
121 f(&obj);
122 }),
123 )
124 }
125}
126
127impl Default for LoadingRow {
128 fn default() -> Self {
129 Self::new()
130 }
131}