fractal/session/view/content/room_history/
title.rs

1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{glib, glib::clone, CompositeTemplate};
3
4use crate::{prelude::*, session::model::Room, utils::BoundObjectWeakRef};
5
6mod imp {
7    use std::cell::RefCell;
8
9    use glib::subclass::InitializingObject;
10
11    use super::*;
12
13    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
14    #[template(resource = "/org/gnome/Fractal/ui/session/view/content/room_history/title.ui")]
15    #[properties(wrapper_type = super::RoomHistoryTitle)]
16    pub struct RoomHistoryTitle {
17        #[template_child]
18        button: TemplateChild<gtk::Button>,
19        #[template_child]
20        subtitle_label: TemplateChild<gtk::Label>,
21        // The room to present the title of.
22        #[property(get, set = Self::set_room, explicit_notify, nullable)]
23        room: BoundObjectWeakRef<Room>,
24        // The title of the room that can be presented on a single line.
25        #[property(get)]
26        title: RefCell<String>,
27        // The subtitle of the room that can be presented on a single line.
28        #[property(get)]
29        subtitle: RefCell<Option<String>>,
30    }
31
32    #[glib::object_subclass]
33    impl ObjectSubclass for RoomHistoryTitle {
34        const NAME: &'static str = "RoomHistoryTitle";
35        type Type = super::RoomHistoryTitle;
36        type ParentType = adw::Bin;
37
38        fn class_init(klass: &mut Self::Class) {
39            Self::bind_template(klass);
40
41            klass.set_css_name("room-title");
42        }
43
44        fn instance_init(obj: &InitializingObject<Self>) {
45            obj.init_template();
46        }
47    }
48
49    #[glib::derived_properties]
50    impl ObjectImpl for RoomHistoryTitle {}
51
52    impl WidgetImpl for RoomHistoryTitle {}
53    impl BinImpl for RoomHistoryTitle {}
54
55    impl RoomHistoryTitle {
56        /// Set the room to present the title of.
57        fn set_room(&self, room: Option<Room>) {
58            if self.room.obj() == room {
59                return;
60            }
61
62            self.room.disconnect_signals();
63
64            if let Some(room) = room {
65                let display_name_handler = room.connect_display_name_notify(clone!(
66                    #[weak(rename_to = imp)]
67                    self,
68                    move |_| {
69                        imp.update_title();
70                    }
71                ));
72                let topic_handler = room.connect_topic_notify(clone!(
73                    #[weak(rename_to = imp)]
74                    self,
75                    move |_| {
76                        imp.update_subtitle();
77                    }
78                ));
79
80                self.room
81                    .set(&room, vec![display_name_handler, topic_handler]);
82            }
83
84            self.obj().notify_room();
85            self.update_title();
86            self.update_subtitle();
87        }
88
89        /// Update the title of the room.
90        fn update_title(&self) {
91            let Some(room) = self.room.obj() else {
92                return;
93            };
94
95            // Remove newlines.
96            let mut title = room.display_name().replace('\n', "");
97            // Remove trailing spaces.
98            title.truncate_end_whitespaces();
99
100            if *self.title.borrow() == title {
101                return;
102            }
103
104            self.title.replace(title);
105            self.obj().notify_title();
106        }
107
108        /// Update the subtitle of the room.
109        fn update_subtitle(&self) {
110            let Some(room) = self.room.obj() else {
111                return;
112            };
113
114            let subtitle = room
115                .topic()
116                .map(|s| {
117                    // Remove newlines.
118                    let mut s = s.replace('\n', "");
119                    // Remove trailing spaces.
120                    s.truncate_end_whitespaces();
121                    s
122                })
123                .filter(|s| !s.is_empty());
124
125            if *self.subtitle.borrow() == subtitle {
126                return;
127            }
128
129            let has_subtitle = subtitle.is_some();
130
131            self.subtitle.replace(subtitle);
132
133            let obj = self.obj();
134            obj.notify_subtitle();
135
136            let button_valign = if has_subtitle {
137                obj.add_css_class("with-subtitle");
138                gtk::Align::Fill
139            } else {
140                obj.remove_css_class("with-subtitle");
141                gtk::Align::Center
142            };
143
144            self.button.set_valign(button_valign);
145            self.subtitle_label.set_visible(has_subtitle);
146        }
147    }
148}
149
150glib::wrapper! {
151    /// A widget to show a room's title and topic in a header bar.
152    pub struct RoomHistoryTitle(ObjectSubclass<imp::RoomHistoryTitle>)
153        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
154}
155
156impl RoomHistoryTitle {
157    /// Construct a new empty `RoomHistoryTitle`.
158    pub fn new() -> Self {
159        glib::Object::new()
160    }
161}
162
163impl Default for RoomHistoryTitle {
164    fn default() -> Self {
165        Self::new()
166    }
167}