fractal/components/pill/
source.rs

1use gtk::{glib, prelude::*, subclass::prelude::*};
2
3use super::Pill;
4use crate::components::AvatarData;
5
6mod imp {
7    use std::{cell::Cell, marker::PhantomData};
8
9    use super::*;
10
11    #[repr(C)]
12    pub struct PillSourceClass {
13        parent_class: glib::object::ObjectClass,
14        pub(super) identifier: fn(&super::PillSource) -> String,
15    }
16
17    unsafe impl ClassStruct for PillSourceClass {
18        type Type = PillSource;
19    }
20
21    pub(super) fn pill_source_identifier(this: &super::PillSource) -> String {
22        let klass = this.class();
23        (klass.as_ref().identifier)(this)
24    }
25
26    #[derive(Debug, Default, glib::Properties)]
27    #[properties(wrapper_type = super::PillSource)]
28    pub struct PillSource {
29        /// A unique identifier for this source.
30        #[property(get = Self::identifier)]
31        identifier: PhantomData<String>,
32        /// The display name of this source.
33        #[property(get = Self::display_name, set = Self::set_display_name, explicit_notify)]
34        display_name: PhantomData<String>,
35        /// Whether the display name of this source is ambiguous.
36        #[property(get, set = Self::set_name_ambiguous, explicit_notify)]
37        is_name_ambiguous: Cell<bool>,
38        /// The disambiguated display name of this source.
39        ///
40        /// This is the name to display in case the identifier does not appear
41        /// next to it.
42        #[property(get = Self::disambiguated_name)]
43        disambiguated_name: PhantomData<String>,
44        /// The avatar data of this source.
45        #[property(get)]
46        avatar_data: AvatarData,
47    }
48
49    #[glib::object_subclass]
50    impl ObjectSubclass for PillSource {
51        const NAME: &'static str = "PillSource";
52        const ABSTRACT: bool = true;
53        type Type = super::PillSource;
54        type Class = PillSourceClass;
55    }
56
57    #[glib::derived_properties]
58    impl ObjectImpl for PillSource {}
59
60    impl PillSource {
61        /// A unique identifier for this source.
62        fn identifier(&self) -> String {
63            imp::pill_source_identifier(&self.obj())
64        }
65
66        /// The display name of this source.
67        fn display_name(&self) -> String {
68            self.avatar_data.display_name()
69        }
70
71        /// Set the display name of this source.
72        fn set_display_name(&self, display_name: String) {
73            if self.display_name() == display_name {
74                return;
75            }
76
77            self.avatar_data.set_display_name(display_name);
78
79            let obj = self.obj();
80            obj.notify_display_name();
81            obj.notify_disambiguated_name();
82        }
83
84        /// Set whether the display name of this source is ambiguous.
85        fn set_name_ambiguous(&self, is_ambiguous: bool) {
86            if self.is_name_ambiguous.get() == is_ambiguous {
87                return;
88            }
89
90            self.is_name_ambiguous.set(is_ambiguous);
91
92            let obj = self.obj();
93            obj.notify_is_name_ambiguous();
94            obj.notify_disambiguated_name();
95        }
96
97        /// The disambiguated display name of this source.
98        fn disambiguated_name(&self) -> String {
99            let display_name = self.display_name();
100
101            if self.is_name_ambiguous.get() {
102                format!("{display_name} ({})", self.identifier())
103            } else {
104                display_name
105            }
106        }
107    }
108}
109
110glib::wrapper! {
111    /// Parent class of objects that can be represented as a `Pill`.
112    pub struct PillSource(ObjectSubclass<imp::PillSource>);
113}
114
115/// Public trait containing implemented methods for everything that derives from
116/// `PillSource`.
117///
118/// To override the behavior of these methods, override the corresponding method
119/// of `PillSourceImpl`.
120pub trait PillSourceExt: 'static {
121    /// A unique identifier for this source.
122    #[allow(dead_code)]
123    fn identifier(&self) -> String;
124
125    /// The display name of this source.
126    fn display_name(&self) -> String;
127
128    /// Set the display name of this source.
129    fn set_display_name(&self, display_name: String);
130
131    /// Whether the display name of this source is ambiguous.
132    #[allow(dead_code)]
133    fn is_name_ambiguous(&self) -> bool;
134
135    /// Set whether the display name of this source is ambiguous.
136    fn set_is_name_ambiguous(&self, is_ambiguous: bool);
137
138    /// The disambiguated display name of this source.
139    ///
140    /// This is the name to display in case the identifier does not appear next
141    /// to it.
142    fn disambiguated_name(&self) -> String;
143
144    /// The avatar data of this source.
145    fn avatar_data(&self) -> AvatarData;
146
147    /// Connect to the signal emitted when the display name changes.
148    fn connect_display_name_notify<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId;
149
150    /// Connect to the signal emitted when the disambiguated name changes.
151    fn connect_disambiguated_name_notify<F: Fn(&Self) + 'static>(
152        &self,
153        f: F,
154    ) -> glib::SignalHandlerId;
155
156    /// Get a `Pill` representing this source.
157    fn to_pill(&self) -> Pill;
158}
159
160impl<O: IsA<PillSource>> PillSourceExt for O {
161    /// A unique identifier for this source.
162    fn identifier(&self) -> String {
163        self.upcast_ref().identifier()
164    }
165
166    /// The display name of this source.
167    fn display_name(&self) -> String {
168        self.upcast_ref().display_name()
169    }
170
171    /// Set the display name of this source.
172    fn set_display_name(&self, display_name: String) {
173        self.upcast_ref().set_display_name(display_name);
174    }
175
176    /// Whether the display name of this source is ambiguous.
177    fn is_name_ambiguous(&self) -> bool {
178        self.upcast_ref().is_name_ambiguous()
179    }
180
181    /// Set whether the display name of this source is ambiguous.
182    fn set_is_name_ambiguous(&self, is_ambiguous: bool) {
183        self.upcast_ref().set_is_name_ambiguous(is_ambiguous);
184    }
185
186    /// The disambiguated display name of this source.
187    fn disambiguated_name(&self) -> String {
188        self.upcast_ref().disambiguated_name()
189    }
190
191    /// The avatar data of this source.
192    fn avatar_data(&self) -> AvatarData {
193        self.upcast_ref().avatar_data()
194    }
195
196    /// Connect to the signal emitted when the display name changes.
197    fn connect_display_name_notify<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
198        self.upcast_ref()
199            .connect_display_name_notify(move |source| f(source.downcast_ref().unwrap()))
200    }
201
202    /// Connect to the signal emitted when the disambiguated name changes.
203    fn connect_disambiguated_name_notify<F: Fn(&Self) + 'static>(
204        &self,
205        f: F,
206    ) -> glib::SignalHandlerId {
207        self.upcast_ref()
208            .connect_disambiguated_name_notify(move |source| f(source.downcast_ref().unwrap()))
209    }
210
211    /// Get a `Pill` representing this source.
212    fn to_pill(&self) -> Pill {
213        Pill::new(self)
214    }
215}
216
217/// Public trait that must be implemented for everything that derives from
218/// `PillSource`.
219///
220/// Overriding a method from this Trait overrides also its behavior in
221/// `PillSourceExt`.
222pub trait PillSourceImpl: ObjectImpl {
223    /// A unique identifier for this source.
224    fn identifier(&self) -> String;
225}
226
227// Make `PillSource` subclassable.
228unsafe impl<T> IsSubclassable<T> for PillSource
229where
230    T: PillSourceImpl,
231    T::Type: IsA<PillSource>,
232{
233    fn class_init(class: &mut glib::Class<Self>) {
234        Self::parent_class_init::<T>(class.upcast_ref_mut());
235
236        let klass = class.as_mut();
237
238        klass.identifier = identifier_trampoline::<T>;
239    }
240}
241
242// Virtual method implementation trampolines.
243fn identifier_trampoline<T>(this: &PillSource) -> String
244where
245    T: ObjectSubclass + PillSourceImpl,
246    T::Type: IsA<PillSource>,
247{
248    let this = this.downcast_ref::<T::Type>().unwrap();
249    this.imp().identifier()
250}