1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use adw::{prelude::*, subclass::prelude::*};
use gtk::{glib, glib::clone, CompositeTemplate};

use crate::{prelude::*, session::model::Room, utils::BoundObjectWeakRef};

mod imp {
    use std::cell::RefCell;

    use glib::subclass::InitializingObject;

    use super::*;

    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
    #[template(resource = "/org/gnome/Fractal/ui/session/view/content/room_history/title.ui")]
    #[properties(wrapper_type = super::RoomHistoryTitle)]
    pub struct RoomHistoryTitle {
        // The room to present the title of.
        #[property(get, set = Self::set_room, explicit_notify, nullable)]
        pub room: BoundObjectWeakRef<Room>,
        // The title of the room that can be presented on a single line.
        #[property(get)]
        pub title: RefCell<String>,
        // The subtitle of the room that can be presented on a single line.
        #[property(get)]
        pub subtitle: RefCell<Option<String>>,
        #[template_child]
        pub subtitle_label: TemplateChild<gtk::Label>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for RoomHistoryTitle {
        const NAME: &'static str = "RoomHistoryTitle";
        type Type = super::RoomHistoryTitle;
        type ParentType = adw::Bin;

        fn class_init(klass: &mut Self::Class) {
            Self::bind_template(klass);

            klass.set_css_name("room-title");
        }

        fn instance_init(obj: &InitializingObject<Self>) {
            obj.init_template();
        }
    }

    #[glib::derived_properties]
    impl ObjectImpl for RoomHistoryTitle {}

    impl WidgetImpl for RoomHistoryTitle {}
    impl BinImpl for RoomHistoryTitle {}

    impl RoomHistoryTitle {
        /// Set the room to present the title of.
        fn set_room(&self, room: Option<Room>) {
            if self.room.obj() == room {
                return;
            }

            self.room.disconnect_signals();

            if let Some(room) = room {
                let display_name_handler = room.connect_display_name_notify(clone!(
                    #[weak(rename_to = imp)]
                    self,
                    move |_| {
                        imp.update_title();
                    }
                ));
                let topic_handler = room.connect_topic_notify(clone!(
                    #[weak(rename_to = imp)]
                    self,
                    move |_| {
                        imp.update_subtitle();
                    }
                ));

                self.room
                    .set(&room, vec![display_name_handler, topic_handler]);
            }

            self.obj().notify_room();
            self.update_title();
            self.update_subtitle();
        }

        /// Update the title of the room.
        fn update_title(&self) {
            let Some(room) = self.room.obj() else {
                return;
            };

            // Remove newlines.
            let mut title = room.display_name().replace('\n', "");
            // Remove trailing spaces.
            title.truncate_end_whitespaces();

            if *self.title.borrow() == title {
                return;
            }

            self.title.replace(title);
            self.obj().notify_title();
        }

        /// Update the subtitle of the room.
        fn update_subtitle(&self) {
            let Some(room) = self.room.obj() else {
                return;
            };

            let subtitle = room
                .topic()
                .map(|s| {
                    // Remove newlines.
                    let mut s = s.replace('\n', "");
                    // Remove trailing spaces.
                    s.truncate_end_whitespaces();
                    s
                })
                .filter(|s| !s.is_empty());

            if *self.subtitle.borrow() == subtitle {
                return;
            }

            let has_subtitle = subtitle.is_some();

            self.subtitle.replace(subtitle);
            self.obj().notify_subtitle();
            self.subtitle_label.set_visible(has_subtitle);
        }
    }
}

glib::wrapper! {
    /// A widget to show a room's title and topic in a header bar.
    pub struct RoomHistoryTitle(ObjectSubclass<imp::RoomHistoryTitle>)
        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
}

impl RoomHistoryTitle {
    /// Construct a new empty `RoomHistoryTitle`.
    pub fn new() -> Self {
        glib::Object::new()
    }
}

impl Default for RoomHistoryTitle {
    fn default() -> Self {
        Self::new()
    }
}