Created
April 28, 2024 10:13
-
-
Save saivert/f74c784dfc8fd8c17b1b251bacf64d10 to your computer and use it in GitHub Desktop.
Filter list model with type known at construction time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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