fractal/components/avatar/
data.rs1use gtk::{gdk, glib, prelude::*, subclass::prelude::*};
2use tracing::warn;
3
4use super::AvatarImage;
5use crate::{
6 application::Application,
7 utils::notifications::{paintable_as_notification_icon, string_as_notification_icon},
8};
9
10mod imp {
11 use std::cell::RefCell;
12
13 use super::*;
14
15 #[derive(Debug, Default, glib::Properties)]
16 #[properties(wrapper_type = super::AvatarData)]
17 pub struct AvatarData {
18 #[property(get, set = Self::set_image, explicit_notify, nullable)]
20 image: RefCell<Option<AvatarImage>>,
21 #[property(get, set = Self::set_display_name, explicit_notify)]
23 display_name: RefCell<String>,
24 }
25
26 #[glib::object_subclass]
27 impl ObjectSubclass for AvatarData {
28 const NAME: &'static str = "AvatarData";
29 type Type = super::AvatarData;
30 }
31
32 #[glib::derived_properties]
33 impl ObjectImpl for AvatarData {}
34
35 impl AvatarData {
36 fn set_image(&self, image: Option<AvatarImage>) {
38 if *self.image.borrow() == image {
39 return;
40 }
41
42 self.image.replace(image);
43 self.obj().notify_image();
44 }
45
46 fn set_display_name(&self, display_name: String) {
48 if *self.display_name.borrow() == display_name {
49 return;
50 }
51
52 self.display_name.replace(display_name);
53 self.obj().notify_display_name();
54 }
55 }
56}
57
58glib::wrapper! {
59 pub struct AvatarData(ObjectSubclass<imp::AvatarData>);
61}
62
63impl AvatarData {
64 pub fn new() -> Self {
66 glib::Object::new()
67 }
68
69 pub(crate) async fn as_notification_icon(&self, inhibit_image: bool) -> Option<gdk::Texture> {
75 let Some(window) = Application::default().active_window() else {
76 warn!("Could not generate icon for notification: no active window");
77 return None;
78 };
79 let Some(renderer) = window.renderer() else {
80 warn!("Could not generate icon for notification: no renderer");
81 return None;
82 };
83 let scale_factor = window.scale_factor();
84
85 if !inhibit_image && let Some(image) = self.image() {
86 match image.load_small_paintable().await {
87 Ok(Some(paintable)) => {
88 let texture = paintable_as_notification_icon(
89 paintable.upcast_ref(),
90 scale_factor,
91 &renderer,
92 );
93 return Some(texture);
94 }
95 Ok(None) => {}
97 Err(error) => {
99 warn!("Could not generate icon for notification: {error}");
100 }
101 }
102 }
103
104 let texture = string_as_notification_icon(
105 &self.display_name(),
106 scale_factor,
107 &window.create_pango_layout(None),
108 &renderer,
109 );
110 Some(texture)
111 }
112}
113
114impl Default for AvatarData {
115 fn default() -> Self {
116 Self::new()
117 }
118}