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