Skip to content

Instantly share code, notes, and snippets.

@saivert
Created April 28, 2024 10:13
Show Gist options
  • Save saivert/f74c784dfc8fd8c17b1b251bacf64d10 to your computer and use it in GitHub Desktop.
Save saivert/f74c784dfc8fd8c17b1b251bacf64d10 to your computer and use it in GitHub Desktop.
Filter list model with type known at construction time.
// SPDX-License-Identifier: GPL-3.0-or-later
use glib::{Properties, closure_local};
use glib::subclass::prelude::*;
use gtk::{gio, prelude::*, subclass::prelude::*};
use std::cell::RefCell;
use std::cell::OnceCell;
mod imp {
use super::*;
#[derive(Debug, Properties)]
#[properties(wrapper_type = super::PwFilterListModel)]
pub struct PwFilterListModel {
/// Contains the items that matches the filter predicate.
#[property(get, set, construct_only)]
pub(super) filtered_model: OnceCell<gio::ListStore>,
/// The model we are filtering.
#[property(get, set = Self::set_model, nullable )]
pub(super) model: RefCell<Option<gio::ListModel>>,
/// The CustomFilter object.
#[property(get, set, nullable)]
pub(super) filter: RefCell<Option<gtk::CustomFilter>>,
}
impl Default for PwFilterListModel {
fn default() -> Self {
Self {
filtered_model: Default::default(),
model: Default::default(),
filter: Default::default(),
}
}
}
#[glib::object_subclass]
impl ObjectSubclass for PwFilterListModel {
const NAME: &'static str = "PwFilterListModel";
type Type = super::PwFilterListModel;
type Interfaces = (gio::ListModel,);
}
#[glib::derived_properties]
impl ObjectImpl for PwFilterListModel {
}
impl ListModelImpl for PwFilterListModel {
fn item_type(&self) -> glib::Type {
self.filtered_model.get().expect("filtered model set at construction time").item_type()
}
fn n_items(&self) -> u32 {
if let Some(model) = self.filtered_model.get() {
model.n_items()
} else {
0
}
}
fn item(&self, position: u32) -> Option<glib::Object> {
if let Some(model) = self.filtered_model.get() {
model.item(position)
} else {
None
}
}
}
impl PwFilterListModel {
pub fn set_model(&self, new_model: Option<gio::ListModel>) {
let filtered_model = self.filtered_model.get().expect("filtered model set at construction time");
let removed = filtered_model.n_items();
if let Some(new_model) = new_model {
let widget = self.obj();
let handler = closure_local!(@watch widget => move |listmodel: &gio::ListModel, _position: u32, _removed: u32, _added: u32| {
let filter = match widget.filter() {
Some(x) => x,
None => gtk::CustomFilter::new(|_|true)
};
let filtered_model = widget.filtered_model();
let k = listmodel.iter::<glib::Object>().map_while(Result::ok);
let y = k.filter(|x| filter.match_(x));
let u: Vec<glib::Object> = y.collect();
let removed = filtered_model.n_items();
filtered_model.splice(0, filtered_model.n_items(), &u);
widget.items_changed(0, removed, filtered_model.n_items())
});
handler.invoke::<()>(&[&new_model, &0u32, &0u32, &0u32]);
new_model.connect_closure("items-changed", true, handler);
self.model.replace(Some(new_model.clone().upcast()));
} else {
self.obj().items_changed(0, removed, 0);
}
}
}
}
glib::wrapper! {
pub struct PwFilterListModel(ObjectSubclass<imp::PwFilterListModel>) @implements gio::ListModel;
}
impl PwFilterListModel {
pub(crate) fn new<T>(model: Option<&impl glib::IsA<gio::ListModel>>) -> Self
where T: glib::IsA<glib::Object>,
{
glib::Object::builder()
.property("model", model)
.property("filtered-model", gio::ListStore::new::<T>())
.build()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment