[fractal/fractal-next] sidebar: Hide empty categories
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] sidebar: Hide empty categories
- Date: Fri, 19 Nov 2021 17:10:29 +0000 (UTC)
commit f13e6b3a22a682801a496576ba4828f8c5e9546d
Author: Julian Sparber <julian sparber net>
Date: Fri Nov 19 17:54:51 2021 +0100
sidebar: Hide empty categories
This also adds a property to the `ItemList` called `show-all` to show
all categories (expect invites). Once we implement dnd of rooms between
categories we can use this property to display all categories.
src/session/sidebar/category.rs | 51 ++++++++++++-----
src/session/sidebar/item_list.rs | 120 +++++++++++++++++++++++++++++++++++++--
src/session/sidebar/mod.rs | 1 -
3 files changed, 152 insertions(+), 20 deletions(-)
---
diff --git a/src/session/sidebar/category.rs b/src/session/sidebar/category.rs
index 41ad6334..b604107d 100644
--- a/src/session/sidebar/category.rs
+++ b/src/session/sidebar/category.rs
@@ -13,6 +13,7 @@ mod imp {
pub struct Category {
pub model: OnceCell<gio::ListModel>,
pub type_: Cell<CategoryType>,
+ pub is_empty: Cell<bool>,
}
#[glib::object_subclass]
@@ -50,6 +51,13 @@ mod imp {
gio::ListModel::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
),
+ glib::ParamSpec::new_boolean(
+ "empty",
+ "Empty",
+ "Whether this category is empty",
+ false,
+ glib::ParamFlags::READABLE,
+ ),
]
});
@@ -81,6 +89,7 @@ mod imp {
"type" => obj.type_().to_value(),
"display-name" => obj.type_().to_string().to_value(),
"model" => self.model.get().to_value(),
+ "empty" => obj.is_empty().to_value(),
_ => unimplemented!(),
}
}
@@ -122,7 +131,7 @@ impl Category {
let type_ = self.type_();
// Special case room lists so that they are sorted and in the right category
- if model.is::<RoomList>() {
+ let model = if model.is::<RoomList>() {
let filter = gtk::CustomFilter::new(move |o| {
o.downcast_ref::<Room>()
.filter(|r| CategoryType::from(r.category()) == type_)
@@ -136,20 +145,34 @@ impl Category {
b.latest_change().cmp(&a.latest_change()).into()
});
let sort_model = gtk::SortListModel::new(Some(&filter_model), Some(&sorter));
-
- sort_model.connect_items_changed(
- clone!(@weak self as obj => move |_, pos, removed, added| {
- obj.items_changed(pos, removed, added);
- }),
- );
- priv_.model.set(sort_model.upcast()).unwrap();
+ sort_model.upcast()
} else {
- model.connect_items_changed(
- clone!(@weak self as obj => move |_, pos, removed, added| {
- obj.items_changed(pos, removed, added);
- }),
- );
- priv_.model.set(model).unwrap();
+ model
+ };
+
+ model.connect_items_changed(
+ clone!(@weak self as obj => move |model, pos, removed, added| {
+ obj.items_changed(pos, removed, added);
+ obj.set_is_empty(model.n_items() == 0);
+ }),
+ );
+
+ self.set_is_empty(model.n_items() == 0);
+ priv_.model.set(model).unwrap();
+ }
+
+ fn set_is_empty(&self, is_empty: bool) {
+ let priv_ = imp::Category::from_instance(self);
+ if is_empty == self.is_empty() {
+ return;
}
+
+ priv_.is_empty.set(is_empty);
+ self.notify("empty");
+ }
+
+ pub fn is_empty(&self) -> bool {
+ let priv_ = imp::Category::from_instance(self);
+ priv_.is_empty.get()
}
}
diff --git a/src/session/sidebar/item_list.rs b/src/session/sidebar/item_list.rs
index 7fa61cfd..5a583aba 100644
--- a/src/session/sidebar/item_list.rs
+++ b/src/session/sidebar/item_list.rs
@@ -1,4 +1,4 @@
-use gtk::{gio, glib, prelude::*, subclass::prelude::*};
+use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use crate::session::{
room_list::RoomList,
@@ -11,14 +11,16 @@ use crate::session::{
mod imp {
use once_cell::sync::Lazy;
use once_cell::unsync::OnceCell;
+ use std::cell::Cell;
use super::*;
#[derive(Debug, Default)]
pub struct ItemList {
- pub list: OnceCell<[glib::Object; 7]>,
+ pub list: OnceCell<[(glib::Object, Cell<bool>); 7]>,
pub room_list: OnceCell<RoomList>,
pub verification_list: OnceCell<VerificationList>,
+ pub show_all: Cell<bool>,
}
#[glib::object_subclass]
@@ -47,6 +49,13 @@ mod imp {
VerificationList::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
),
+ glib::ParamSpec::new_boolean(
+ "show-all",
+ "Show All",
+ "Whether all room categories should be shown",
+ false,
+ glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+ ),
]
});
@@ -63,6 +72,7 @@ mod imp {
match pspec.name() {
"room-list" => obj.set_room_list(value.get().unwrap()),
"verification-list" => obj.set_verification_list(value.get().unwrap()),
+ "show-all" => obj.set_show_all(value.get().unwrap()),
_ => unimplemented!(),
}
}
@@ -71,6 +81,7 @@ mod imp {
match pspec.name() {
"room-list" => obj.room_list().to_value(),
"verification-list" => obj.verification_list().to_value(),
+ "show-all" => obj.show_all().to_value(),
_ => unimplemented!(),
}
}
@@ -92,6 +103,26 @@ mod imp {
Category::new(CategoryType::Left, room_list).upcast::<glib::Object>(),
];
+ for (index, item) in list.iter().enumerate() {
+ if let Some(category) = item.downcast_ref::<Category>() {
+ category.connect_notify_local(
+ Some("empty"),
+ clone!(@weak obj => move |_, _| {
+ obj.update_category(index);
+ }),
+ );
+ }
+ }
+
+ let list = list.map(|item| {
+ let visible = if let Some(category) = item.downcast_ref::<Category>() {
+ !category.is_empty()
+ } else {
+ true
+ };
+ (item, Cell::new(visible))
+ });
+
self.list.set(list).unwrap();
}
}
@@ -101,13 +132,28 @@ mod imp {
glib::Object::static_type()
}
fn n_items(&self, _list_model: &Self::Type) -> u32 {
- self.list.get().map(|l| l.len()).unwrap_or(0) as u32
+ self.list
+ .get()
+ .unwrap()
+ .iter()
+ .filter(|(_, visible)| visible.get())
+ .count() as u32
}
fn item(&self, _list_model: &Self::Type, position: u32) -> Option<glib::Object> {
self.list
.get()
- .and_then(|l| l.get(position as usize))
- .map(glib::object::Cast::upcast_ref::<glib::Object>)
+ .unwrap()
+ .iter()
+ .filter_map(
+ |(item, visible)| {
+ if visible.get() {
+ Some(item)
+ } else {
+ None
+ }
+ },
+ )
+ .nth(position as usize)
.cloned()
}
}
@@ -131,6 +177,58 @@ impl ItemList {
.expect("Failed to create ItemList")
}
+ fn update_category(&self, position: usize) {
+ let priv_ = imp::ItemList::from_instance(self);
+ let (item, old_visible) = priv_.list.get().unwrap().get(position).unwrap();
+ let category = item.downcast_ref::<Category>().unwrap();
+
+ let visible = !category.is_empty() || (self.show_all() && is_show_all_category(category));
+ if visible != old_visible.get() {
+ old_visible.set(visible);
+ let hidden_before_position = priv_
+ .list
+ .get()
+ .unwrap()
+ .iter()
+ .take(position)
+ .filter(|(_, visible)| !visible.get())
+ .count();
+ let real_position = position - hidden_before_position;
+
+ let (removed, added) = if visible { (0, 1) } else { (1, 0) };
+
+ self.items_changed(real_position as u32, removed, added);
+ }
+ }
+
+ // Whether all room categories are shown
+ // This doesn't include `CategoryType::Invite` since the user can't move rooms to it.
+ pub fn show_all(&self) -> bool {
+ let priv_ = imp::ItemList::from_instance(self);
+ priv_.show_all.get()
+ }
+
+ // Set whether all room categories should be shown
+ // This doesn't include `CategoryType::Invite` since the user can't move rooms to it.
+ pub fn set_show_all(&self, show_all: bool) {
+ let priv_ = imp::ItemList::from_instance(self);
+ if show_all == self.show_all() {
+ return;
+ }
+
+ priv_.show_all.set(show_all);
+
+ for (index, (item, _)) in priv_.list.get().unwrap().iter().enumerate() {
+ if let Some(category) = item.downcast_ref::<Category>() {
+ if is_show_all_category(category) {
+ self.update_category(index);
+ }
+ }
+ }
+
+ self.notify("show-all");
+ }
+
fn set_room_list(&self, room_list: RoomList) {
let priv_ = imp::ItemList::from_instance(self);
priv_.room_list.set(room_list).unwrap();
@@ -151,3 +249,15 @@ impl ItemList {
priv_.verification_list.get().unwrap()
}
}
+
+// Wheter this category should be shown when `show-all` is `true`
+// This doesn't include `CategoryType::Invite` since the user can't move rooms to it.
+fn is_show_all_category(category: &Category) -> bool {
+ matches!(
+ category.type_(),
+ CategoryType::Favorite
+ | CategoryType::Normal
+ | CategoryType::LowPriority
+ | CategoryType::Left
+ )
+}
diff --git a/src/session/sidebar/mod.rs b/src/session/sidebar/mod.rs
index 606f33fc..fe3bd4b2 100644
--- a/src/session/sidebar/mod.rs
+++ b/src/session/sidebar/mod.rs
@@ -208,7 +208,6 @@ impl Sidebar {
}
};
- // TODO: hide empty categories
let tree_model = gtk::TreeListModel::new(&item_list, false, true, |item| {
item.clone().downcast::<gio::ListModel>().ok()
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]