fractal/session/sidebar_data/
item.rs1use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
2
3use super::{SidebarIconItem, SidebarSection};
4use crate::{
5 session::RoomCategory,
6 utils::{BoundConstructOnlyObject, SingleItemListModel},
7};
8
9mod imp {
10 use std::cell::{Cell, OnceCell};
11
12 use super::*;
13
14 #[derive(Debug, glib::Properties)]
15 #[properties(wrapper_type = super::SidebarItem)]
16 pub struct SidebarItem {
17 #[property(get, set = Self::set_inner_item, construct_only)]
19 inner_item: BoundConstructOnlyObject<glib::Object>,
20 #[property(get)]
22 is_visible: Cell<bool>,
23 #[property(get, set = Self::set_inhibit_expanded, explicit_notify)]
28 inhibit_expanded: Cell<bool>,
29 is_visible_filter: gtk::CustomFilter,
30 is_expanded_filter: gtk::CustomFilter,
31 model: OnceCell<gtk::FilterListModel>,
33 }
34
35 impl Default for SidebarItem {
36 fn default() -> Self {
37 Self {
38 inner_item: Default::default(),
39 is_visible: Cell::new(true),
40 inhibit_expanded: Default::default(),
41 is_visible_filter: Default::default(),
42 is_expanded_filter: Default::default(),
43 model: Default::default(),
44 }
45 }
46 }
47
48 #[glib::object_subclass]
49 impl ObjectSubclass for SidebarItem {
50 const NAME: &'static str = "SidebarItem";
51 type Type = super::SidebarItem;
52 type Interfaces = (gio::ListModel,);
53 }
54
55 #[glib::derived_properties]
56 impl ObjectImpl for SidebarItem {}
57
58 impl ListModelImpl for SidebarItem {
59 fn item_type(&self) -> glib::Type {
60 glib::Object::static_type()
61 }
62
63 fn n_items(&self) -> u32 {
64 self.model.get().unwrap().n_items()
65 }
66
67 fn item(&self, position: u32) -> Option<glib::Object> {
68 self.model.get().unwrap().item(position)
69 }
70 }
71
72 impl SidebarItem {
73 fn set_inner_item(&self, item: glib::Object) {
75 let mut handlers = Vec::new();
76
77 let inner_model = if let Some(section) = item.downcast_ref::<SidebarSection>() {
78 let section_model = SingleItemListModel::new(Some(section));
80
81 self.is_expanded_filter.set_filter_func(clone!(
83 #[weak(rename_to = imp)]
84 self,
85 #[weak]
86 section,
87 #[upgrade_or]
88 false,
89 move |_| imp.inhibit_expanded.get() || section.is_expanded()
90 ));
91 let children_model = gtk::FilterListModel::new(
92 Some(section.clone()),
93 Some(self.is_expanded_filter.clone()),
94 );
95
96 let is_expanded_handler = section.connect_is_expanded_notify(clone!(
97 #[weak(rename_to = imp)]
98 self,
99 move |_| {
100 imp.is_expanded_filter.changed(gtk::FilterChange::Different);
101 }
102 ));
103 handlers.push(is_expanded_handler);
104
105 let wrapper_model = gio::ListStore::new::<glib::Object>();
107 wrapper_model.append(§ion_model);
108 wrapper_model.append(&children_model);
109
110 gtk::FlattenListModel::new(Some(wrapper_model)).upcast::<gio::ListModel>()
111 } else {
112 SingleItemListModel::new(Some(&item)).upcast()
114 };
115
116 self.inner_item.set(item, handlers);
117
118 self.is_visible_filter.set_filter_func(clone!(
119 #[weak(rename_to = imp)]
120 self,
121 #[upgrade_or]
122 false,
123 move |_| imp.is_visible.get()
124 ));
125 let model =
126 gtk::FilterListModel::new(Some(inner_model), Some(self.is_visible_filter.clone()));
127
128 let obj = self.obj();
129 model.connect_items_changed(clone!(
130 #[weak]
131 obj,
132 move |_model, pos, removed, added| {
133 obj.items_changed(pos, removed, added);
134 }
135 ));
136
137 self.model.set(model).unwrap();
138 }
139
140 pub(super) fn set_visible(&self, visible: bool) {
142 if self.is_visible.get() == visible {
143 return;
144 }
145
146 self.is_visible.set(visible);
147
148 self.obj().notify_is_visible();
149 self.is_visible_filter.changed(gtk::FilterChange::Different);
150 }
151
152 fn set_inhibit_expanded(&self, inhibit: bool) {
154 if self.inhibit_expanded.get() == inhibit {
155 return;
156 }
157
158 self.inhibit_expanded.set(inhibit);
159
160 self.obj().notify_inhibit_expanded();
161 self.is_expanded_filter
162 .changed(gtk::FilterChange::Different);
163 }
164 }
165}
166
167glib::wrapper! {
168 pub struct SidebarItem(ObjectSubclass<imp::SidebarItem>)
173 @implements gio::ListModel;
174}
175
176impl SidebarItem {
177 pub fn new(item: impl IsA<glib::Object>) -> Self {
179 glib::Object::builder()
180 .property("inner-item", &item)
181 .build()
182 }
183
184 pub(crate) fn update_visibility_for_room_category(
187 &self,
188 source_category: Option<RoomCategory>,
189 ) {
190 let inner_item = self.inner_item();
191 let visible = if let Some(section) = inner_item.downcast_ref::<SidebarSection>() {
192 section.visible_for_room_category(source_category)
193 } else if let Some(icon_item) = inner_item.downcast_ref::<SidebarIconItem>() {
194 icon_item.visible_for_room_category(source_category)
195 } else {
196 true
197 };
198
199 self.imp().set_visible(visible);
200 }
201}