fractal/components/pill/
source.rs1use gtk::{glib, prelude::*, subclass::prelude::*};
2
3use super::Pill;
4use crate::{
5 components::{AvatarData, AvatarImageSafetySetting},
6 session::Room,
7};
8
9mod imp {
10 use std::{cell::Cell, marker::PhantomData};
11
12 use super::*;
13
14 #[repr(C)]
15 pub struct PillSourceClass {
16 parent_class: glib::object::ObjectClass,
17 pub(super) identifier: fn(&super::PillSource) -> String,
18 }
19
20 unsafe impl ClassStruct for PillSourceClass {
21 type Type = PillSource;
22 }
23
24 pub(super) fn pill_source_identifier(this: &super::PillSource) -> String {
25 let klass = this.class();
26 (klass.as_ref().identifier)(this)
27 }
28
29 #[derive(Debug, Default, glib::Properties)]
30 #[properties(wrapper_type = super::PillSource)]
31 pub struct PillSource {
32 #[property(get = Self::identifier)]
34 identifier: PhantomData<String>,
35 #[property(get = Self::display_name, set = Self::set_display_name, explicit_notify)]
37 display_name: PhantomData<String>,
38 #[property(get, set = Self::set_name_ambiguous, explicit_notify)]
40 is_name_ambiguous: Cell<bool>,
41 #[property(get = Self::disambiguated_name)]
46 disambiguated_name: PhantomData<String>,
47 #[property(get)]
49 avatar_data: AvatarData,
50 }
51
52 #[glib::object_subclass]
53 impl ObjectSubclass for PillSource {
54 const NAME: &'static str = "PillSource";
55 const ABSTRACT: bool = true;
56 type Type = super::PillSource;
57 type Class = PillSourceClass;
58 }
59
60 #[glib::derived_properties]
61 impl ObjectImpl for PillSource {}
62
63 impl PillSource {
64 fn identifier(&self) -> String {
66 imp::pill_source_identifier(&self.obj())
67 }
68
69 fn display_name(&self) -> String {
71 self.avatar_data.display_name()
72 }
73
74 fn set_display_name(&self, display_name: String) {
76 if self.display_name() == display_name {
77 return;
78 }
79
80 self.avatar_data.set_display_name(display_name);
81
82 let obj = self.obj();
83 obj.notify_display_name();
84 obj.notify_disambiguated_name();
85 }
86
87 fn set_name_ambiguous(&self, is_ambiguous: bool) {
89 if self.is_name_ambiguous.get() == is_ambiguous {
90 return;
91 }
92
93 self.is_name_ambiguous.set(is_ambiguous);
94
95 let obj = self.obj();
96 obj.notify_is_name_ambiguous();
97 obj.notify_disambiguated_name();
98 }
99
100 fn disambiguated_name(&self) -> String {
102 let display_name = self.display_name();
103
104 if self.is_name_ambiguous.get() {
105 format!("{display_name} ({})", self.identifier())
106 } else {
107 display_name
108 }
109 }
110 }
111}
112
113glib::wrapper! {
114 pub struct PillSource(ObjectSubclass<imp::PillSource>);
116}
117
118pub trait PillSourceExt: 'static {
124 #[allow(dead_code)]
126 fn identifier(&self) -> String;
127
128 fn display_name(&self) -> String;
130
131 fn set_display_name(&self, display_name: String);
133
134 #[allow(dead_code)]
136 fn is_name_ambiguous(&self) -> bool;
137
138 fn set_is_name_ambiguous(&self, is_ambiguous: bool);
140
141 fn disambiguated_name(&self) -> String;
146
147 fn avatar_data(&self) -> AvatarData;
149
150 fn connect_display_name_notify<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId;
152
153 fn connect_disambiguated_name_notify<F: Fn(&Self) + 'static>(
155 &self,
156 f: F,
157 ) -> glib::SignalHandlerId;
158
159 fn to_pill(
162 &self,
163 watched_safety_setting: AvatarImageSafetySetting,
164 watched_room: Option<Room>,
165 ) -> Pill;
166}
167
168impl<O: IsA<PillSource>> PillSourceExt for O {
169 fn identifier(&self) -> String {
171 self.upcast_ref().identifier()
172 }
173
174 fn display_name(&self) -> String {
176 self.upcast_ref().display_name()
177 }
178
179 fn set_display_name(&self, display_name: String) {
181 self.upcast_ref().set_display_name(display_name);
182 }
183
184 fn is_name_ambiguous(&self) -> bool {
186 self.upcast_ref().is_name_ambiguous()
187 }
188
189 fn set_is_name_ambiguous(&self, is_ambiguous: bool) {
191 self.upcast_ref().set_is_name_ambiguous(is_ambiguous);
192 }
193
194 fn disambiguated_name(&self) -> String {
196 self.upcast_ref().disambiguated_name()
197 }
198
199 fn avatar_data(&self) -> AvatarData {
201 self.upcast_ref().avatar_data()
202 }
203
204 fn connect_display_name_notify<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
206 self.upcast_ref()
207 .connect_display_name_notify(move |source| f(source.downcast_ref().unwrap()))
208 }
209
210 fn connect_disambiguated_name_notify<F: Fn(&Self) + 'static>(
212 &self,
213 f: F,
214 ) -> glib::SignalHandlerId {
215 self.upcast_ref()
216 .connect_disambiguated_name_notify(move |source| f(source.downcast_ref().unwrap()))
217 }
218
219 fn to_pill(
221 &self,
222 watched_safety_setting: AvatarImageSafetySetting,
223 watched_room: Option<Room>,
224 ) -> Pill {
225 Pill::new(self, watched_safety_setting, watched_room)
226 }
227}
228
229pub trait PillSourceImpl: ObjectImpl {
235 fn identifier(&self) -> String;
237}
238
239unsafe impl<T> IsSubclassable<T> for PillSource
241where
242 T: PillSourceImpl,
243 T::Type: IsA<PillSource>,
244{
245 fn class_init(class: &mut glib::Class<Self>) {
246 Self::parent_class_init::<T>(class.upcast_ref_mut());
247
248 let klass = class.as_mut();
249
250 klass.identifier = identifier_trampoline::<T>;
251 }
252}
253
254fn identifier_trampoline<T>(this: &PillSource) -> String
256where
257 T: ObjectSubclass + PillSourceImpl,
258 T::Type: IsA<PillSource>,
259{
260 let this = this.downcast_ref::<T::Type>().unwrap();
261 this.imp().identifier()
262}