fractal/account_switcher/
avatar_with_selection.rs

1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{glib, CompositeTemplate};
3
4use crate::components::{Avatar, AvatarData};
5
6mod imp {
7    use std::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/account_switcher/avatar_with_selection.ui")]
15    #[properties(wrapper_type = super::AvatarWithSelection)]
16    pub struct AvatarWithSelection {
17        #[template_child]
18        child_avatar: TemplateChild<Avatar>,
19        #[template_child]
20        checkmark: TemplateChild<gtk::Image>,
21        /// The [`AvatarData`] displayed by this widget.
22        #[property(get = Self::data, set = Self::set_data, explicit_notify, nullable)]
23        data: PhantomData<Option<AvatarData>>,
24        /// The size of the Avatar.
25        #[property(get = Self::size, set = Self::set_size, minimum = -1, default = -1)]
26        size: PhantomData<i32>,
27        /// Whether this avatar is selected.
28        #[property(get = Self::is_selected, set = Self::set_selected, explicit_notify)]
29        selected: PhantomData<bool>,
30    }
31
32    #[glib::object_subclass]
33    impl ObjectSubclass for AvatarWithSelection {
34        const NAME: &'static str = "AvatarWithSelection";
35        type Type = super::AvatarWithSelection;
36        type ParentType = adw::Bin;
37
38        fn class_init(klass: &mut Self::Class) {
39            Self::bind_template(klass);
40        }
41
42        fn instance_init(obj: &InitializingObject<Self>) {
43            obj.init_template();
44        }
45    }
46
47    #[glib::derived_properties]
48    impl ObjectImpl for AvatarWithSelection {}
49
50    impl WidgetImpl for AvatarWithSelection {}
51    impl BinImpl for AvatarWithSelection {}
52
53    impl AccessibleImpl for AvatarWithSelection {
54        fn first_accessible_child(&self) -> Option<gtk::Accessible> {
55            // Hide the children in the a11y tree.
56            None
57        }
58    }
59
60    impl AvatarWithSelection {
61        /// Whether this avatar is selected.
62        fn is_selected(&self) -> bool {
63            self.checkmark.get_visible()
64        }
65
66        /// Set whether this avatar is selected.
67        fn set_selected(&self, selected: bool) {
68            if self.is_selected() == selected {
69                return;
70            }
71
72            self.checkmark.set_visible(selected);
73
74            if selected {
75                self.child_avatar.add_css_class("selected-avatar");
76            } else {
77                self.child_avatar.remove_css_class("selected-avatar");
78            }
79
80            self.obj().notify_selected();
81        }
82
83        /// The [`AvatarData`] displayed by this widget.
84        fn data(&self) -> Option<AvatarData> {
85            self.child_avatar.data()
86        }
87
88        /// Set the [`AvatarData`] displayed by this widget.
89        fn set_data(&self, data: Option<AvatarData>) {
90            self.child_avatar.set_data(data);
91        }
92
93        /// The size of the Avatar.
94        fn size(&self) -> i32 {
95            self.child_avatar.size()
96        }
97
98        /// Set the size of the Avatar.
99        fn set_size(&self, size: i32) {
100            self.child_avatar.set_size(size);
101        }
102    }
103}
104
105glib::wrapper! {
106    /// A widget displaying an [`Avatar`] and an optional selected effect.
107    pub struct AvatarWithSelection(ObjectSubclass<imp::AvatarWithSelection>)
108        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
109}
110
111impl AvatarWithSelection {
112    pub fn new() -> Self {
113        glib::Object::new()
114    }
115}