fractal/components/rows/
entry_add_row.rs1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{glib, glib::closure_local};
3
4use crate::components::LoadingButton;
5
6mod imp {
7 use std::{cell::Cell, marker::PhantomData, sync::LazyLock};
8
9 use glib::subclass::{InitializingObject, Signal};
10
11 use super::*;
12
13 #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
14 #[template(resource = "/org/gnome/Fractal/ui/components/rows/entry_add_row.ui")]
15 #[properties(wrapper_type = super::EntryAddRow)]
16 pub struct EntryAddRow {
17 #[template_child]
18 add_button: TemplateChild<LoadingButton>,
19 #[property(get = Self::add_button_tooltip_text, set = Self::set_add_button_tooltip_text, explicit_notify, nullable)]
21 add_button_tooltip_text: PhantomData<Option<glib::GString>>,
22 #[property(get, set = Self::set_inhibit_add, explicit_notify)]
24 inhibit_add: Cell<bool>,
25 #[property(get = Self::is_loading, set = Self::set_is_loading, explicit_notify)]
27 is_loading: PhantomData<bool>,
28 }
29
30 #[glib::object_subclass]
31 impl ObjectSubclass for EntryAddRow {
32 const NAME: &'static str = "EntryAddRow";
33 type Type = super::EntryAddRow;
34 type ParentType = adw::EntryRow;
35
36 fn class_init(klass: &mut Self::Class) {
37 Self::bind_template(klass);
38 Self::bind_template_callbacks(klass);
39 }
40
41 fn instance_init(obj: &InitializingObject<Self>) {
42 obj.init_template();
43 }
44 }
45
46 #[glib::derived_properties]
47 impl ObjectImpl for EntryAddRow {
48 fn signals() -> &'static [Signal] {
49 static SIGNALS: LazyLock<Vec<Signal>> =
50 LazyLock::new(|| vec![Signal::builder("add").build()]);
51 SIGNALS.as_ref()
52 }
53 }
54
55 impl WidgetImpl for EntryAddRow {}
56 impl ListBoxRowImpl for EntryAddRow {}
57 impl PreferencesRowImpl for EntryAddRow {}
58 impl ActionRowImpl for EntryAddRow {}
59 impl EntryRowImpl for EntryAddRow {}
60
61 #[gtk::template_callbacks]
62 impl EntryAddRow {
63 fn add_button_tooltip_text(&self) -> Option<glib::GString> {
65 self.add_button.tooltip_text()
66 }
67
68 fn set_add_button_tooltip_text(&self, tooltip_text: Option<&str>) {
70 if self.add_button_tooltip_text().as_deref() == tooltip_text {
71 return;
72 }
73
74 self.add_button.set_tooltip_text(tooltip_text);
75 self.obj().notify_add_button_tooltip_text();
76 }
77
78 fn set_inhibit_add(&self, inhibit: bool) {
80 if self.inhibit_add.get() == inhibit {
81 return;
82 }
83
84 self.inhibit_add.set(inhibit);
85
86 self.update_add_button();
87 self.obj().notify_inhibit_add();
88 }
89
90 fn is_loading(&self) -> bool {
92 self.add_button.is_loading()
93 }
94
95 fn set_is_loading(&self, is_loading: bool) {
97 if self.is_loading() == is_loading {
98 return;
99 }
100
101 self.add_button.set_is_loading(is_loading);
102
103 let obj = self.obj();
104 obj.set_sensitive(!is_loading);
105 obj.notify_is_loading();
106 }
107
108 fn can_add(&self) -> bool {
110 !self.inhibit_add.get() && !self.obj().text().is_empty()
111 }
112
113 #[template_callback]
115 fn update_add_button(&self) {
116 self.add_button.set_sensitive(self.can_add());
117 }
118
119 #[template_callback]
121 fn add(&self) {
122 if !self.can_add() {
123 return;
124 }
125
126 self.obj().emit_by_name::<()>("add", &[]);
127 }
128 }
129}
130
131glib::wrapper! {
132 pub struct EntryAddRow(ObjectSubclass<imp::EntryAddRow>)
134 @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow, adw::EntryRow,
135 @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Actionable, gtk::Editable;
136}
137
138impl EntryAddRow {
139 pub fn new() -> Self {
140 glib::Object::new()
141 }
142
143 pub fn connect_add<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
145 self.connect_closure(
146 "add",
147 true,
148 closure_local!(move |obj: Self| {
149 f(&obj);
150 }),
151 )
152 }
153}