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