fractal/components/rows/
switch_loading_row.rs

1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{glib, glib::clone, CompositeTemplate};
3
4use crate::utils::bool_to_accessible_tristate;
5
6mod imp {
7    use std::{cell::Cell, marker::PhantomData};
8
9    use glib::subclass::InitializingObject;
10
11    use super::*;
12
13    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
14    #[template(resource = "/org/gnome/Fractal/ui/components/rows/switch_loading_row.ui")]
15    #[properties(wrapper_type = super::SwitchLoadingRow)]
16    pub struct SwitchLoadingRow {
17        #[template_child]
18        spinner: TemplateChild<adw::Spinner>,
19        #[template_child]
20        switch: TemplateChild<gtk::Switch>,
21        /// Whether the switch is active.
22        #[property(get = Self::is_active, set = Self::set_is_active)]
23        is_active: PhantomData<bool>,
24        /// Whether the row is loading.
25        #[property(get = Self::is_loading, set = Self::set_is_loading)]
26        is_loading: PhantomData<bool>,
27        /// Whether the row is read-only.
28        #[property(get, set = Self::set_read_only, explicit_notify)]
29        read_only: Cell<bool>,
30    }
31
32    #[glib::object_subclass]
33    impl ObjectSubclass for SwitchLoadingRow {
34        const NAME: &'static str = "SwitchLoadingRow";
35        type Type = super::SwitchLoadingRow;
36        type ParentType = adw::ActionRow;
37
38        fn class_init(klass: &mut Self::Class) {
39            Self::bind_template(klass);
40
41            klass.set_accessible_role(gtk::AccessibleRole::Switch);
42        }
43
44        fn instance_init(obj: &InitializingObject<Self>) {
45            obj.init_template();
46        }
47    }
48
49    #[glib::derived_properties]
50    impl ObjectImpl for SwitchLoadingRow {
51        fn constructed(&self) {
52            self.parent_constructed();
53            let obj = self.obj();
54
55            self.switch.connect_active_notify(clone!(
56                #[weak]
57                obj,
58                move |switch| {
59                    obj.update_state(&[gtk::accessible::State::Checked(
60                        bool_to_accessible_tristate(switch.is_active()),
61                    )]);
62                    obj.notify_is_active();
63                }
64            ));
65            obj.update_state(&[gtk::accessible::State::Checked(
66                bool_to_accessible_tristate(self.switch.is_active()),
67            )]);
68        }
69    }
70
71    impl WidgetImpl for SwitchLoadingRow {}
72    impl ListBoxRowImpl for SwitchLoadingRow {}
73    impl PreferencesRowImpl for SwitchLoadingRow {}
74    impl ActionRowImpl for SwitchLoadingRow {}
75
76    impl SwitchLoadingRow {
77        /// Whether the switch is active.
78        fn is_active(&self) -> bool {
79            self.switch.is_active()
80        }
81
82        /// Set whether the switch is active.
83        fn set_is_active(&self, active: bool) {
84            if self.is_active() == active {
85                return;
86            }
87
88            self.switch.set_active(active);
89            self.obj().notify_is_active();
90        }
91
92        /// Whether the row is loading.
93        fn is_loading(&self) -> bool {
94            self.spinner.is_visible()
95        }
96
97        /// Set whether the row is loading.
98        fn set_is_loading(&self, loading: bool) {
99            if self.is_loading() == loading {
100                return;
101            }
102
103            self.spinner.set_visible(loading);
104            self.obj().notify_is_loading();
105        }
106
107        /// Set whether the row is read-only.
108        fn set_read_only(&self, read_only: bool) {
109            if self.read_only.get() == read_only {
110                return;
111            }
112            let obj = self.obj();
113
114            self.read_only.set(read_only);
115
116            obj.update_property(&[gtk::accessible::Property::ReadOnly(read_only)]);
117            obj.notify_read_only();
118        }
119    }
120}
121
122glib::wrapper! {
123    /// An `AdwActionRow` with a switch and a loading state.
124    pub struct SwitchLoadingRow(ObjectSubclass<imp::SwitchLoadingRow>)
125        @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow,
126        @implements gtk::Actionable, gtk::Accessible;
127}
128
129impl SwitchLoadingRow {
130    pub fn new() -> Self {
131        glib::Object::new()
132    }
133}