fractal/utils/
expression_list_model.rs1use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
2use indexmap::IndexMap;
3use tracing::error;
4
5use crate::utils::BoundObject;
6
7mod imp {
8 use std::cell::RefCell;
9
10 use super::*;
11
12 #[derive(Debug, Default, glib::Properties)]
13 #[properties(wrapper_type = super::ExpressionListModel)]
14 pub struct ExpressionListModel {
15 #[property(get, set = Self::set_model, explicit_notify, nullable)]
16 model: BoundObject<gio::ListModel>,
17 expressions: RefCell<Vec<gtk::Expression>>,
18 watches: RefCell<IndexMap<glib::Object, Vec<gtk::ExpressionWatch>>>,
21 }
22
23 #[glib::object_subclass]
24 impl ObjectSubclass for ExpressionListModel {
25 const NAME: &'static str = "ExpressionListModel";
26 type Type = super::ExpressionListModel;
27 type Interfaces = (gio::ListModel,);
28 }
29
30 #[glib::derived_properties]
31 impl ObjectImpl for ExpressionListModel {
32 fn dispose(&self) {
33 for watch in self.watches.take().into_values().flatten() {
34 watch.unwatch();
35 }
36 }
37 }
38
39 impl ListModelImpl for ExpressionListModel {
40 fn item_type(&self) -> glib::Type {
41 self.model
42 .obj()
43 .map_or_else(glib::Object::static_type, |m| m.item_type())
44 }
45
46 fn n_items(&self) -> u32 {
47 self.model.obj().map(|m| m.n_items()).unwrap_or_default()
48 }
49
50 fn item(&self, position: u32) -> Option<glib::Object> {
51 self.model.obj().and_then(|m| m.item(position))
52 }
53 }
54
55 impl ExpressionListModel {
56 fn set_model(&self, model: Option<gio::ListModel>) {
58 if self.model.obj() == model {
59 return;
60 }
61
62 let obj = self.obj();
63 let removed = self.n_items();
64
65 self.model.disconnect_signals();
66 for watch in self.watches.take().into_values().flatten() {
67 watch.unwatch();
68 }
69
70 let added = if let Some(model) = model {
71 let items_changed_handler = model.connect_items_changed(clone!(
72 #[strong]
73 obj,
74 move |_, pos, removed, added| {
75 obj.imp().watch_items(pos, removed, added);
76 obj.items_changed(pos, removed, added);
77 }
78 ));
79
80 let added = model.n_items();
81 self.model.set(model, vec![items_changed_handler]);
82
83 self.watch_items(0, removed, added);
84 added
85 } else {
86 0
87 };
88
89 let obj = self.obj();
90 obj.items_changed(0, removed, added);
91 obj.notify_model();
92 }
93
94 pub(super) fn set_expressions(&self, expressions: Vec<gtk::Expression>) {
96 for watch in self.watches.take().into_values().flatten() {
97 watch.unwatch();
98 }
99
100 self.expressions.replace(expressions);
101
102 let n_items = self.n_items();
103 self.watch_items(0, n_items, n_items);
104 }
105
106 fn watch_items(&self, pos: u32, removed: u32, added: u32) {
109 let Some(model) = self.model.obj() else {
110 return;
111 };
112
113 let expressions = self.expressions.borrow().clone();
114 if expressions.is_empty() {
115 return;
116 }
117
118 let mut new_entries = Vec::with_capacity(added as usize);
119 for item_pos in pos..pos + added {
120 let Some(item) = model.item(item_pos) else {
121 error!("Out of bounds item");
122 break;
123 };
124
125 let obj = self.obj();
126 let mut item_watches = Vec::with_capacity(expressions.len());
127 for expression in &expressions {
128 item_watches.push(expression.watch(
129 Some(&item),
130 clone!(
131 #[strong]
132 obj,
133 #[weak]
134 item,
135 move || {
136 obj.imp().item_expr_changed(&item);
137 }
138 ),
139 ));
140 }
141
142 new_entries.push((item, item_watches));
143 }
144
145 let mut watches = self.watches.borrow_mut();
146 let removed_range = (pos as usize)..((pos + removed) as usize);
147 for watch in watches
148 .splice(removed_range, new_entries)
149 .flat_map(|(_, w)| w)
150 {
151 watch.unwatch();
152 }
153 }
154
155 fn item_expr_changed(&self, item: &glib::Object) {
156 let pos = self.watches.borrow().get_index_of(item);
158 if let Some(pos) = pos {
159 self.obj().items_changed(pos as u32, 1, 1);
160 }
161 }
162 }
163}
164
165glib::wrapper! {
166 pub struct ExpressionListModel(ObjectSubclass<imp::ExpressionListModel>)
168 @implements gio::ListModel;
169}
170
171impl ExpressionListModel {
172 pub fn new() -> Self {
173 glib::Object::new()
174 }
175
176 pub(crate) fn set_expressions(&self, expressions: Vec<gtk::Expression>) {
178 self.imp().set_expressions(expressions);
179 }
180}
181
182impl Default for ExpressionListModel {
183 fn default() -> Self {
184 Self::new()
185 }
186}