Skip to main content

fractal/session/sidebar_data/
item_list.rs

1use std::cell::Cell;
2
3use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
4
5use super::{
6    SidebarIconItem, SidebarIconItemType, SidebarItem, SidebarSection, SidebarSectionName,
7};
8use crate::session::{RoomCategory, RoomList, VerificationList};
9
10/// The number of top-level items in the sidebar.
11const TOP_LEVEL_ITEMS_COUNT: usize = 9;
12
13mod imp {
14    use std::cell::OnceCell;
15
16    use super::*;
17
18    #[derive(Debug, Default, glib::Properties)]
19    #[properties(wrapper_type = super::SidebarItemList)]
20    pub struct SidebarItemList {
21        /// The list of top-level items.
22        list: OnceCell<[SidebarItem; TOP_LEVEL_ITEMS_COUNT]>,
23        /// The list of rooms.
24        #[property(get, construct_only)]
25        room_list: OnceCell<RoomList>,
26        /// The list of verification requests.
27        #[property(get, construct_only)]
28        verification_list: OnceCell<VerificationList>,
29        /// The room category to show all compatible sections and icon items
30        /// for.
31        ///
32        /// The UI is updated to show possible drop actions for a room with the
33        /// given category.
34        show_all_for_room_category: Cell<Option<RoomCategory>>,
35    }
36
37    #[glib::object_subclass]
38    impl ObjectSubclass for SidebarItemList {
39        const NAME: &'static str = "SidebarItemList";
40        type Type = super::SidebarItemList;
41        type Interfaces = (gio::ListModel,);
42    }
43
44    #[glib::derived_properties]
45    impl ObjectImpl for SidebarItemList {
46        fn constructed(&self) {
47            self.parent_constructed();
48            let obj = self.obj();
49
50            let room_list = obj.room_list();
51            let verification_list = obj.verification_list();
52
53            let list = self.list.get_or_init(|| {
54                [
55                    SidebarItem::new(SidebarIconItem::new(SidebarIconItemType::Explore)),
56                    SidebarItem::new(SidebarSection::new(
57                        SidebarSectionName::VerificationRequest,
58                        &verification_list,
59                    )),
60                    SidebarItem::new(SidebarSection::new(
61                        SidebarSectionName::InviteRequest,
62                        &room_list,
63                    )),
64                    SidebarItem::new(SidebarSection::new(SidebarSectionName::Invited, &room_list)),
65                    SidebarItem::new(SidebarSection::new(
66                        SidebarSectionName::Favorite,
67                        &room_list,
68                    )),
69                    SidebarItem::new(SidebarSection::new(SidebarSectionName::Normal, &room_list)),
70                    SidebarItem::new(SidebarSection::new(
71                        SidebarSectionName::LowPriority,
72                        &room_list,
73                    )),
74                    SidebarItem::new(SidebarSection::new(SidebarSectionName::Left, &room_list)),
75                    SidebarItem::new(SidebarIconItem::new(SidebarIconItemType::Forget)),
76                ]
77            });
78
79            for item in list {
80                if let Some(section) = item.inner_item().downcast_ref::<SidebarSection>() {
81                    section.connect_is_empty_notify(clone!(
82                        #[weak(rename_to = imp)]
83                        self,
84                        #[weak]
85                        item,
86                        move |_| {
87                            imp.update_item_visibility(&item);
88                        }
89                    ));
90                }
91                self.update_item_visibility(item);
92            }
93        }
94    }
95
96    impl ListModelImpl for SidebarItemList {
97        fn item_type(&self) -> glib::Type {
98            SidebarItem::static_type()
99        }
100
101        fn n_items(&self) -> u32 {
102            TOP_LEVEL_ITEMS_COUNT as u32
103        }
104
105        fn item(&self, position: u32) -> Option<glib::Object> {
106            self.list().get(position as usize).cloned().and_upcast()
107        }
108    }
109
110    impl SidebarItemList {
111        /// The list of top-level items.
112        pub(super) fn list(&self) -> &[SidebarItem; TOP_LEVEL_ITEMS_COUNT] {
113            self.list.get().unwrap()
114        }
115
116        /// Set the room category to show all compatible sections and icon items
117        /// for.
118        pub(super) fn set_show_all_for_room_category(&self, category: Option<RoomCategory>) {
119            if self.show_all_for_room_category.get() == category {
120                return;
121            }
122
123            self.show_all_for_room_category.set(category);
124            for item in self.list() {
125                self.update_item_visibility(item);
126            }
127        }
128
129        /// Update the visibility of the given item.
130        fn update_item_visibility(&self, item: &SidebarItem) {
131            item.update_visibility_for_room_category(self.show_all_for_room_category.get());
132        }
133
134        /// Set whether to inhibit the expanded state of the sections.
135        ///
136        /// It means that all the sections will be expanded regardless of
137        /// their "is-expanded" property.
138        pub(super) fn inhibit_expanded(&self, inhibit: bool) {
139            for item in self.list() {
140                item.set_inhibit_expanded(inhibit);
141            }
142        }
143    }
144}
145
146glib::wrapper! {
147    /// Fixed list of all subcomponents in the sidebar.
148    ///
149    /// Implements the `gio::ListModel` interface and yields the top-level
150    /// items of the sidebar.
151    pub struct SidebarItemList(ObjectSubclass<imp::SidebarItemList>)
152        @implements gio::ListModel;
153}
154
155impl SidebarItemList {
156    /// Construct a new `SidebarItemList` with the given room list and
157    /// verification list.
158    pub fn new(room_list: &RoomList, verification_list: &VerificationList) -> Self {
159        glib::Object::builder()
160            .property("room-list", room_list)
161            .property("verification-list", verification_list)
162            .build()
163    }
164
165    /// Set the room category to show all compatible sections and icon items
166    /// for.
167    pub(crate) fn set_show_all_for_room_category(&self, category: Option<RoomCategory>) {
168        self.imp().set_show_all_for_room_category(category);
169    }
170
171    /// Set whether to inhibit the expanded state of the sections.
172    ///
173    /// It means that all the sections will be expanded regardless of their
174    /// "is-expanded" property.
175    pub(crate) fn inhibit_expanded(&self, inhibit: bool) {
176        self.imp().inhibit_expanded(inhibit);
177    }
178
179    /// Returns the [`SidebarSection`] for the given room category.
180    pub(crate) fn section_from_room_category(
181        &self,
182        category: RoomCategory,
183    ) -> Option<SidebarSection> {
184        const FIRST_ROOM_SECTION_INDEX: usize = 2;
185
186        let index = match category {
187            RoomCategory::Knocked => FIRST_ROOM_SECTION_INDEX,
188            RoomCategory::Invited => FIRST_ROOM_SECTION_INDEX + 1,
189            RoomCategory::Favorite => FIRST_ROOM_SECTION_INDEX + 2,
190            RoomCategory::Normal => FIRST_ROOM_SECTION_INDEX + 3,
191            RoomCategory::LowPriority => FIRST_ROOM_SECTION_INDEX + 4,
192            RoomCategory::Left => FIRST_ROOM_SECTION_INDEX + 5,
193            _ => return None,
194        };
195
196        self.imp()
197            .list()
198            .get(index)
199            .map(SidebarItem::inner_item)
200            .and_downcast()
201    }
202}