fractal/components/pill/
source.rs1use 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 #[property(get = Self::identifier)]
31 identifier: PhantomData<String>,
32 #[property(get = Self::display_name, set = Self::set_display_name, explicit_notify)]
34 display_name: PhantomData<String>,
35 #[property(get, set = Self::set_name_ambiguous, explicit_notify)]
37 is_name_ambiguous: Cell<bool>,
38 #[property(get = Self::disambiguated_name)]
43 disambiguated_name: PhantomData<String>,
44 #[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 fn identifier(&self) -> String {
63 imp::pill_source_identifier(&self.obj())
64 }
65
66 fn display_name(&self) -> String {
68 self.avatar_data.display_name()
69 }
70
71 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 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 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 pub struct PillSource(ObjectSubclass<imp::PillSource>);
113}
114
115pub trait PillSourceExt: 'static {
121 #[allow(dead_code)]
123 fn identifier(&self) -> String;
124
125 fn display_name(&self) -> String;
127
128 fn set_display_name(&self, display_name: String);
130
131 #[allow(dead_code)]
133 fn is_name_ambiguous(&self) -> bool;
134
135 fn set_is_name_ambiguous(&self, is_ambiguous: bool);
137
138 fn disambiguated_name(&self) -> String;
143
144 fn avatar_data(&self) -> AvatarData;
146
147 fn connect_display_name_notify<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId;
149
150 fn connect_disambiguated_name_notify<F: Fn(&Self) + 'static>(
152 &self,
153 f: F,
154 ) -> glib::SignalHandlerId;
155
156 fn to_pill(&self) -> Pill;
158}
159
160impl<O: IsA<PillSource>> PillSourceExt for O {
161 fn identifier(&self) -> String {
163 self.upcast_ref().identifier()
164 }
165
166 fn display_name(&self) -> String {
168 self.upcast_ref().display_name()
169 }
170
171 fn set_display_name(&self, display_name: String) {
173 self.upcast_ref().set_display_name(display_name);
174 }
175
176 fn is_name_ambiguous(&self) -> bool {
178 self.upcast_ref().is_name_ambiguous()
179 }
180
181 fn set_is_name_ambiguous(&self, is_ambiguous: bool) {
183 self.upcast_ref().set_is_name_ambiguous(is_ambiguous);
184 }
185
186 fn disambiguated_name(&self) -> String {
188 self.upcast_ref().disambiguated_name()
189 }
190
191 fn avatar_data(&self) -> AvatarData {
193 self.upcast_ref().avatar_data()
194 }
195
196 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 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 fn to_pill(&self) -> Pill {
213 Pill::new(self)
214 }
215}
216
217pub trait PillSourceImpl: ObjectImpl {
223 fn identifier(&self) -> String;
225}
226
227unsafe 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
242fn 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}