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
use adw::subclass::prelude::*;
use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};

use crate::{components::Spinner, utils::bool_to_accessible_tristate};

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

    use glib::subclass::InitializingObject;

    use super::*;

    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
    #[template(resource = "/org/gnome/Fractal/ui/components/rows/switch_loading_row.ui")]
    #[properties(wrapper_type = super::SwitchLoadingRow)]
    pub struct SwitchLoadingRow {
        #[template_child]
        pub spinner: TemplateChild<Spinner>,
        #[template_child]
        pub switch: TemplateChild<gtk::Switch>,
        /// Whether the switch is active.
        #[property(get = Self::is_active, set = Self::set_is_active)]
        pub is_active: PhantomData<bool>,
        /// Whether the row is loading.
        #[property(get = Self::is_loading, set = Self::set_is_loading)]
        pub is_loading: PhantomData<bool>,
        /// Whether the row is read-only.
        #[property(get, set = Self::set_read_only, explicit_notify)]
        pub read_only: Cell<bool>,
    }

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

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

            klass.set_accessible_role(gtk::AccessibleRole::Switch);
        }

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

    #[glib::derived_properties]
    impl ObjectImpl for SwitchLoadingRow {
        fn constructed(&self) {
            self.parent_constructed();
            let obj = self.obj();

            self.switch.connect_active_notify(clone!(
                #[weak]
                obj,
                move |switch| {
                    obj.update_state(&[gtk::accessible::State::Checked(
                        bool_to_accessible_tristate(switch.is_active()),
                    )]);
                    obj.notify_is_active();
                }
            ));
            obj.update_state(&[gtk::accessible::State::Checked(
                bool_to_accessible_tristate(self.switch.is_active()),
            )]);
        }
    }

    impl WidgetImpl for SwitchLoadingRow {}
    impl ListBoxRowImpl for SwitchLoadingRow {}
    impl PreferencesRowImpl for SwitchLoadingRow {}
    impl ActionRowImpl for SwitchLoadingRow {}

    impl SwitchLoadingRow {
        /// Whether the switch is active.
        fn is_active(&self) -> bool {
            self.switch.is_active()
        }

        /// Set whether the switch is active.
        fn set_is_active(&self, active: bool) {
            if self.is_active() == active {
                return;
            }

            self.switch.set_active(active);
            self.obj().notify_is_active();
        }

        /// Whether the row is loading.
        fn is_loading(&self) -> bool {
            self.spinner.is_visible()
        }

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

            self.spinner.set_visible(loading);
            self.obj().notify_is_loading();
        }

        /// Set whether the row is read-only.
        fn set_read_only(&self, read_only: bool) {
            if self.read_only.get() == read_only {
                return;
            }
            let obj = self.obj();

            self.read_only.set(read_only);

            obj.update_property(&[gtk::accessible::Property::ReadOnly(read_only)]);
            obj.notify_read_only();
        }
    }
}

glib::wrapper! {
    /// An `AdwActionRow` with a switch and a loading state.
    pub struct SwitchLoadingRow(ObjectSubclass<imp::SwitchLoadingRow>)
        @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow, @implements gtk::Actionable, gtk::Accessible;
}

impl SwitchLoadingRow {
    pub fn new() -> Self {
        glib::Object::new()
    }
}