fractal/components/rows/
entry_add_row.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use adw::{prelude::*, subclass::prelude::*};
use gtk::{glib, glib::closure_local, CompositeTemplate};

use crate::components::LoadingButton;

mod imp {
    use std::{cell::Cell, marker::PhantomData, sync::LazyLock};

    use glib::subclass::{InitializingObject, Signal};

    use super::*;

    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
    #[template(resource = "/org/gnome/Fractal/ui/components/rows/entry_add_row.ui")]
    #[properties(wrapper_type = super::EntryAddRow)]
    pub struct EntryAddRow {
        #[template_child]
        pub add_button: TemplateChild<LoadingButton>,
        /// The tooltip text of the add button.
        #[property(get = Self::add_button_tooltip_text, set = Self::set_add_button_tooltip_text, explicit_notify, nullable)]
        pub add_button_tooltip_text: PhantomData<Option<glib::GString>>,
        /// Whether to prevent the add button from being activated.
        #[property(get, set = Self::set_inhibit_add, explicit_notify)]
        pub inhibit_add: Cell<bool>,
        /// Whether this row is loading.
        #[property(get = Self::is_loading, set = Self::set_is_loading, explicit_notify)]
        pub is_loading: PhantomData<bool>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for EntryAddRow {
        const NAME: &'static str = "EntryAddRow";
        type Type = super::EntryAddRow;
        type ParentType = adw::EntryRow;

        fn class_init(klass: &mut Self::Class) {
            Self::bind_template(klass);
            Self::Type::bind_template_callbacks(klass);
        }

        fn instance_init(obj: &InitializingObject<Self>) {
            obj.init_template();
        }
    }

    #[glib::derived_properties]
    impl ObjectImpl for EntryAddRow {
        fn signals() -> &'static [Signal] {
            static SIGNALS: LazyLock<Vec<Signal>> =
                LazyLock::new(|| vec![Signal::builder("add").build()]);
            SIGNALS.as_ref()
        }
    }

    impl WidgetImpl for EntryAddRow {}
    impl ListBoxRowImpl for EntryAddRow {}
    impl PreferencesRowImpl for EntryAddRow {}
    impl ActionRowImpl for EntryAddRow {}
    impl EntryRowImpl for EntryAddRow {}

    impl EntryAddRow {
        /// The tooltip text of the add button.
        fn add_button_tooltip_text(&self) -> Option<glib::GString> {
            self.add_button.tooltip_text()
        }

        /// Set the tooltip text of the add button.
        fn set_add_button_tooltip_text(&self, tooltip_text: Option<&str>) {
            if self.add_button_tooltip_text().as_deref() == tooltip_text {
                return;
            }

            self.add_button.set_tooltip_text(tooltip_text);
            self.obj().notify_add_button_tooltip_text();
        }

        /// Set whether to prevent the add button from being activated.
        fn set_inhibit_add(&self, inhibit: bool) {
            if self.inhibit_add.get() == inhibit {
                return;
            }

            self.inhibit_add.set(inhibit);

            let obj = self.obj();
            obj.update_add_button();
            obj.notify_inhibit_add();
        }

        /// Whether this row is loading.
        fn is_loading(&self) -> bool {
            self.add_button.is_loading()
        }

        /// Set whether this row is loading.
        fn set_is_loading(&self, is_loading: bool) {
            if self.is_loading() == is_loading {
                return;
            }

            self.add_button.set_is_loading(is_loading);

            let obj = self.obj();
            obj.set_sensitive(!is_loading);
            obj.notify_is_loading();
        }
    }
}

glib::wrapper! {
    /// An `AdwEntryRow` with an "add" button.
    pub struct EntryAddRow(ObjectSubclass<imp::EntryAddRow>)
        @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow, adw::EntryRow,
        @implements gtk::Actionable, gtk::Editable, gtk::Accessible;
}

#[gtk::template_callbacks]
impl EntryAddRow {
    pub fn new() -> Self {
        glib::Object::new()
    }

    /// Whether the add button can be activated.
    fn can_add(&self) -> bool {
        !self.inhibit_add() && !self.text().is_empty()
    }

    /// Update the state of the add button.
    #[template_callback]
    fn update_add_button(&self) {
        self.imp().add_button.set_sensitive(self.can_add());
    }

    /// Emit the `add` signal.
    #[template_callback]
    fn add(&self) {
        if !self.can_add() {
            return;
        }

        self.emit_by_name::<()>("add", &[]);
    }

    /// Connect to the `add` signal.
    pub fn connect_add<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
        self.connect_closure(
            "add",
            true,
            closure_local!(move |obj: Self| {
                f(&obj);
            }),
        )
    }
}