fractal/components/rows/
removable_row.rs1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{glib, glib::closure_local, CompositeTemplate};
3
4use crate::components::LoadingButton;
5
6mod imp {
7 use std::{cell::RefCell, marker::PhantomData, sync::LazyLock};
8
9 use glib::subclass::{InitializingObject, Signal};
10
11 use super::*;
12
13 #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
14 #[template(resource = "/org/gnome/Fractal/ui/components/rows/removable_row.ui")]
15 #[properties(wrapper_type = super::RemovableRow)]
16 pub struct RemovableRow {
17 #[template_child]
18 remove_button: TemplateChild<LoadingButton>,
19 #[template_child]
20 extra_suffix_bin: TemplateChild<adw::Bin>,
21 #[property(get = Self::remove_button_tooltip_text, set = Self::set_remove_button_tooltip_text, explicit_notify, nullable)]
23 remove_button_tooltip_text: PhantomData<Option<glib::GString>>,
24 #[property(get, set = Self::set_remove_button_accessible_label, explicit_notify, nullable)]
26 remove_button_accessible_label: RefCell<Option<String>>,
27 #[property(get = Self::is_loading, set = Self::set_is_loading, explicit_notify)]
29 is_loading: PhantomData<bool>,
30 #[property(get = Self::extra_suffix, set = Self::set_extra_suffix, explicit_notify, nullable)]
34 extra_suffix: PhantomData<Option<gtk::Widget>>,
35 }
36
37 #[glib::object_subclass]
38 impl ObjectSubclass for RemovableRow {
39 const NAME: &'static str = "RemovableRow";
40 type Type = super::RemovableRow;
41 type ParentType = adw::ActionRow;
42
43 fn class_init(klass: &mut Self::Class) {
44 Self::bind_template(klass);
45 Self::bind_template_callbacks(klass);
46 }
47
48 fn instance_init(obj: &InitializingObject<Self>) {
49 obj.init_template();
50 }
51 }
52
53 #[glib::derived_properties]
54 impl ObjectImpl for RemovableRow {
55 fn signals() -> &'static [Signal] {
56 static SIGNALS: LazyLock<Vec<Signal>> =
57 LazyLock::new(|| vec![Signal::builder("remove").build()]);
58 SIGNALS.as_ref()
59 }
60 }
61
62 impl WidgetImpl for RemovableRow {}
63 impl ListBoxRowImpl for RemovableRow {}
64 impl PreferencesRowImpl for RemovableRow {}
65 impl ActionRowImpl for RemovableRow {}
66
67 #[gtk::template_callbacks]
68 impl RemovableRow {
69 fn remove_button_tooltip_text(&self) -> Option<glib::GString> {
71 self.remove_button.tooltip_text()
72 }
73
74 fn set_remove_button_tooltip_text(&self, tooltip_text: Option<&str>) {
76 if self.remove_button_tooltip_text().as_deref() == tooltip_text {
77 return;
78 }
79
80 self.remove_button.set_tooltip_text(tooltip_text);
81 self.obj().notify_remove_button_tooltip_text();
82 }
83
84 fn set_remove_button_accessible_label(&self, label: Option<String>) {
86 if *self.remove_button_accessible_label.borrow() == label {
87 return;
88 }
89
90 if let Some(label) = &label {
91 self.remove_button
92 .update_property(&[gtk::accessible::Property::Label(label)]);
93 } else {
94 self.remove_button
95 .reset_property(gtk::AccessibleProperty::Label);
96 }
97
98 self.remove_button_accessible_label.replace(label);
99 self.obj().notify_remove_button_accessible_label();
100 }
101
102 fn is_loading(&self) -> bool {
104 self.remove_button.is_loading()
105 }
106
107 fn set_is_loading(&self, is_loading: bool) {
109 if self.is_loading() == is_loading {
110 return;
111 }
112
113 self.remove_button.set_is_loading(is_loading);
114
115 let obj = self.obj();
116 obj.set_sensitive(!is_loading);
117 obj.notify_is_loading();
118 }
119
120 fn extra_suffix(&self) -> Option<gtk::Widget> {
122 self.extra_suffix_bin.child()
123 }
124
125 fn set_extra_suffix(&self, widget: Option<>k::Widget>) {
127 if self.extra_suffix().as_ref() == widget {
128 return;
129 }
130
131 self.extra_suffix_bin.set_child(widget);
132 self.obj().notify_extra_suffix();
133 }
134
135 #[template_callback]
137 fn remove(&self) {
138 self.obj().emit_by_name::<()>("remove", &[]);
139 }
140 }
141}
142
143glib::wrapper! {
144 pub struct RemovableRow(ObjectSubclass<imp::RemovableRow>)
146 @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow,
147 @implements gtk::Actionable, gtk::Accessible;
148}
149
150impl RemovableRow {
151 pub fn new() -> Self {
152 glib::Object::new()
153 }
154
155 pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
157 self.connect_closure(
158 "remove",
159 true,
160 closure_local!(move |obj: Self| {
161 f(&obj);
162 }),
163 )
164 }
165}