fractal/components/power_level_selection/
popover.rs
1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{glib, glib::clone, CompositeTemplate};
3
4use crate::{
5 session::model::{Permissions, PowerLevel, POWER_LEVEL_ADMIN, POWER_LEVEL_MOD},
6 utils::BoundObject,
7};
8
9mod imp {
10 use std::cell::Cell;
11
12 use glib::subclass::InitializingObject;
13
14 use super::*;
15
16 #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
17 #[template(resource = "/org/gnome/Fractal/ui/components/power_level_selection/popover.ui")]
18 #[properties(wrapper_type = super::PowerLevelSelectionPopover)]
19 pub struct PowerLevelSelectionPopover {
20 #[template_child]
21 admin_row: TemplateChild<gtk::ListBoxRow>,
22 #[template_child]
23 admin_selected: TemplateChild<gtk::Image>,
24 #[template_child]
25 mod_row: TemplateChild<gtk::ListBoxRow>,
26 #[template_child]
27 mod_selected: TemplateChild<gtk::Image>,
28 #[template_child]
29 default_row: TemplateChild<gtk::ListBoxRow>,
30 #[template_child]
31 default_pl_label: TemplateChild<gtk::Label>,
32 #[template_child]
33 default_selected: TemplateChild<gtk::Image>,
34 #[template_child]
35 muted_row: TemplateChild<gtk::ListBoxRow>,
36 #[template_child]
37 muted_pl_label: TemplateChild<gtk::Label>,
38 #[template_child]
39 muted_selected: TemplateChild<gtk::Image>,
40 #[template_child]
41 custom_row: TemplateChild<adw::SpinRow>,
42 #[template_child]
43 custom_adjustment: TemplateChild<gtk::Adjustment>,
44 #[template_child]
45 custom_confirm: TemplateChild<gtk::Button>,
46 #[property(get, set = Self::set_permissions, explicit_notify, nullable)]
48 permissions: BoundObject<Permissions>,
49 #[property(get, set = Self::set_selected_power_level, explicit_notify)]
51 selected_power_level: Cell<PowerLevel>,
52 }
53
54 #[glib::object_subclass]
55 impl ObjectSubclass for PowerLevelSelectionPopover {
56 const NAME: &'static str = "PowerLevelSelectionPopover";
57 type Type = super::PowerLevelSelectionPopover;
58 type ParentType = gtk::Popover;
59
60 fn class_init(klass: &mut Self::Class) {
61 Self::bind_template(klass);
62 Self::bind_template_callbacks(klass);
63 }
64
65 fn instance_init(obj: &InitializingObject<Self>) {
66 obj.init_template();
67 }
68 }
69
70 #[glib::derived_properties]
71 impl ObjectImpl for PowerLevelSelectionPopover {}
72
73 impl WidgetImpl for PowerLevelSelectionPopover {}
74 impl PopoverImpl for PowerLevelSelectionPopover {}
75
76 #[gtk::template_callbacks]
77 impl PowerLevelSelectionPopover {
78 fn set_permissions(&self, permissions: Option<Permissions>) {
80 if self.permissions.obj() == permissions {
81 return;
82 }
83
84 self.permissions.disconnect_signals();
85
86 if let Some(permissions) = permissions {
87 let own_pl_handler = permissions.connect_own_power_level_notify(clone!(
88 #[weak(rename_to = imp)]
89 self,
90 move |_| {
91 imp.update();
92 }
93 ));
94 let default_pl_handler = permissions.connect_default_power_level_notify(clone!(
95 #[weak(rename_to = imp)]
96 self,
97 move |_| {
98 imp.update_default();
99 imp.update_muted();
100 imp.update_selection();
101 }
102 ));
103 let muted_pl_handler = permissions.connect_mute_power_level_notify(clone!(
104 #[weak(rename_to = imp)]
105 self,
106 move |_| {
107 imp.update_muted();
108 imp.update_selection();
109 }
110 ));
111
112 self.permissions.set(
113 permissions,
114 vec![own_pl_handler, default_pl_handler, muted_pl_handler],
115 );
116 }
117
118 self.update();
119 self.obj().notify_permissions();
120 }
121
122 fn set_selected_power_level(&self, power_level: PowerLevel) {
124 if self.selected_power_level.get() == power_level {
125 return;
126 }
127
128 self.selected_power_level.set(power_level);
129
130 self.update_selection();
131 self.update_custom();
132 self.obj().notify_selected_power_level();
133 }
134
135 fn update(&self) {
137 self.update_admin();
138 self.update_mod();
139 self.update_default();
140 self.update_muted();
141 self.update_custom();
142 self.update_selection();
143 }
144
145 fn update_admin(&self) {
147 let Some(permissions) = self.permissions.obj() else {
148 return;
149 };
150
151 let can_change_to_admin = permissions.own_power_level() >= POWER_LEVEL_ADMIN;
152
153 if can_change_to_admin {
154 self.admin_row.set_sensitive(true);
155 self.admin_row.set_activatable(true);
156 } else {
157 self.admin_row.set_sensitive(false);
158 self.admin_row.set_activatable(false);
159 }
160 }
161
162 fn update_mod(&self) {
164 let Some(permissions) = self.permissions.obj() else {
165 return;
166 };
167
168 let can_change_to_mod = permissions.own_power_level() >= POWER_LEVEL_MOD;
169
170 if can_change_to_mod {
171 self.mod_row.set_sensitive(true);
172 self.mod_row.set_activatable(true);
173 } else {
174 self.mod_row.set_sensitive(false);
175 self.mod_row.set_activatable(false);
176 }
177 }
178
179 fn update_default(&self) {
181 let Some(permissions) = self.permissions.obj() else {
182 return;
183 };
184
185 let default = permissions.default_power_level();
186 self.default_pl_label.set_label(&default.to_string());
187
188 let can_change_to_default = permissions.own_power_level() >= default;
189
190 if can_change_to_default {
191 self.default_row.set_sensitive(true);
192 self.default_row.set_activatable(true);
193 } else {
194 self.default_row.set_sensitive(false);
195 self.default_row.set_activatable(false);
196 }
197 }
198
199 fn update_muted(&self) {
201 let Some(permissions) = self.permissions.obj() else {
202 return;
203 };
204
205 let mute = permissions.mute_power_level();
206 let default = permissions.default_power_level();
207
208 if mute >= default {
209 self.muted_row.set_visible(false);
212 return;
213 }
214
215 self.muted_pl_label.set_label(&mute.to_string());
216
217 let can_change_to_muted = permissions.own_power_level() >= mute;
218
219 if can_change_to_muted {
220 self.muted_row.set_sensitive(true);
221 self.muted_row.set_activatable(true);
222 } else {
223 self.muted_row.set_sensitive(false);
224 self.muted_row.set_activatable(false);
225 }
226
227 self.muted_row.set_visible(true);
228 }
229
230 fn update_custom(&self) {
232 let Some(permissions) = self.permissions.obj() else {
233 return;
234 };
235
236 self.custom_adjustment
237 .set_upper(permissions.own_power_level() as f64);
238 self.custom_adjustment
239 .set_value(self.selected_power_level.get() as f64);
240 }
241
242 fn update_selection(&self) {
244 let Some(permissions) = self.permissions.obj() else {
245 return;
246 };
247
248 let power_level = self.selected_power_level.get();
249
250 self.admin_selected
251 .set_opacity((power_level == POWER_LEVEL_ADMIN).into());
252 self.mod_selected
253 .set_opacity((power_level == POWER_LEVEL_MOD).into());
254 self.default_selected
255 .set_opacity((power_level == permissions.default_power_level()).into());
256 self.muted_selected
257 .set_opacity((power_level == permissions.mute_power_level()).into());
258 }
259
260 #[template_callback]
262 fn custom_value_changed(&self) {
263 let power_level = self.custom_adjustment.value() as PowerLevel;
264 let can_confirm = power_level != self.selected_power_level.get();
265
266 self.custom_confirm.set_sensitive(can_confirm);
267 }
268
269 #[template_callback]
271 fn custom_value_confirmed(&self) {
272 let power_level = self.custom_adjustment.value() as PowerLevel;
273
274 self.obj().popdown();
275 self.set_selected_power_level(power_level);
276 }
277
278 #[template_callback]
280 fn row_activated(&self, row: >k::ListBoxRow) {
281 let Some(permissions) = self.permissions.obj() else {
282 return;
283 };
284
285 let power_level = match row.index() {
286 0 => POWER_LEVEL_ADMIN,
287 1 => POWER_LEVEL_MOD,
288 2 => permissions.default_power_level(),
289 3 => permissions.mute_power_level(),
290 _ => return,
291 };
292
293 self.obj().popdown();
294 self.set_selected_power_level(power_level);
295 }
296 }
297}
298
299glib::wrapper! {
300 pub struct PowerLevelSelectionPopover(ObjectSubclass<imp::PowerLevelSelectionPopover>)
302 @extends gtk::Widget, gtk::Popover, @implements gtk::Accessible;
303}
304
305impl PowerLevelSelectionPopover {
306 pub fn new() -> Self {
307 glib::Object::new()
308 }
309}