fractal/session/model/sidebar_data/
list_model.rsuse gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
use super::{Selection, SidebarItemList};
use crate::{
session::model::{IdentityVerification, Room},
utils::{expression, BoundObjectWeakRef},
};
mod imp {
use std::cell::{Cell, OnceCell};
use super::*;
#[derive(Debug, Default, glib::Properties)]
#[properties(wrapper_type = super::SidebarListModel)]
pub struct SidebarListModel {
#[property(get, set = Self::set_item_list, construct_only)]
pub item_list: OnceCell<SidebarItemList>,
#[property(get)]
pub string_filter: gtk::StringFilter,
#[property(get)]
pub is_filtered: Cell<bool>,
#[property(get)]
pub selection_model: Selection,
pub selected_item: BoundObjectWeakRef<glib::Object>,
item_type_filter: gtk::CustomFilter,
}
#[glib::object_subclass]
impl ObjectSubclass for SidebarListModel {
const NAME: &'static str = "SidebarListModel";
type Type = super::SidebarListModel;
}
#[glib::derived_properties]
impl ObjectImpl for SidebarListModel {
fn constructed(&self) {
self.parent_constructed();
self.selection_model.connect_selected_item_notify(clone!(
#[weak(rename_to = imp)]
self,
move |selection_model| {
imp.selected_item.disconnect_signals();
if let Some(item) = &selection_model.selected_item() {
if let Some(verification) = item.downcast_ref::<IdentityVerification>() {
let verification_handler = verification.connect_replaced(clone!(
#[weak]
selection_model,
move |_, new_verification| {
selection_model
.set_selected_item(Some(new_verification.clone()));
}
));
imp.selected_item.set(item, vec![verification_handler]);
}
}
}
));
self.string_filter.connect_search_notify(clone!(
#[weak(rename_to = imp)]
self,
move |string_filter| {
imp.set_is_filtered(string_filter.search().is_some_and(|s| !s.is_empty()));
}
));
}
}
impl SidebarListModel {
fn item_list(&self) -> &SidebarItemList {
self.item_list.get().unwrap()
}
fn set_item_list(&self, item_list: SidebarItemList) {
let item_list = self.item_list.get_or_init(|| item_list);
let flattened_model = gtk::FlattenListModel::new(Some(item_list.clone()));
self.item_type_filter.set_filter_func(clone!(
#[weak(rename_to = imp)]
self,
#[upgrade_or]
false,
move |item| !imp.is_filtered.get() || item.is::<Room>()
));
let room_name_expression = Room::this_expression("display-name");
self.string_filter
.set_match_mode(gtk::StringFilterMatchMode::Substring);
self.string_filter
.set_expression(Some(expression::normalize_string(room_name_expression)));
self.string_filter.set_ignore_case(true);
self.string_filter.set_search(Some(""));
let multi_filter = gtk::EveryFilter::new();
multi_filter.append(self.item_type_filter.clone());
multi_filter.append(self.string_filter.clone());
let filter_model = gtk::FilterListModel::new(Some(flattened_model), Some(multi_filter));
self.selection_model.set_model(Some(filter_model));
}
fn set_is_filtered(&self, is_filtered: bool) {
if self.is_filtered.get() == is_filtered {
return;
}
self.is_filtered.set(is_filtered);
self.obj().notify_is_filtered();
self.item_list().inhibit_expanded(is_filtered);
self.item_type_filter.changed(gtk::FilterChange::Different);
}
}
}
glib::wrapper! {
pub struct SidebarListModel(ObjectSubclass<imp::SidebarListModel>);
}
impl SidebarListModel {
pub fn new(item_list: &SidebarItemList) -> Self {
glib::Object::builder()
.property("item-list", item_list)
.build()
}
}