1use std::any::Any;
2
3#[cfg(feature = "state-query")]
4use fret_query::{QueryHandle, QueryState};
5use fret_runtime::{Model, ModelUpdateError};
6use fret_ui::{ElementContext, Invalidation, UiHost};
7
8pub trait ModelWatchExt {
13 type WatchedModel<'cx, 'm, T: Any>
14 where
15 Self: 'cx;
16
17 fn watch_model<'cx, 'm, T: Any>(
18 &'cx mut self,
19 model: &'m Model<T>,
20 ) -> Self::WatchedModel<'cx, 'm, T>;
21}
22
23impl<'a, H: UiHost> ModelWatchExt for ElementContext<'a, H> {
24 type WatchedModel<'cx, 'm, T: Any>
25 = WatchedModel<'cx, 'm, 'a, H, T>
26 where
27 Self: 'cx;
28
29 fn watch_model<'cx, 'm, T: Any>(
30 &'cx mut self,
31 model: &'m Model<T>,
32 ) -> Self::WatchedModel<'cx, 'm, T> {
33 WatchedModel {
34 cx: self,
35 model,
36 invalidation: Invalidation::Paint,
37 }
38 }
39}
40
41pub trait TrackedModelExt<T: Any> {
47 fn watch_in<'cx, 'a, H: UiHost>(
48 &self,
49 cx: &'cx mut ElementContext<'a, H>,
50 ) -> WatchedModel<'cx, '_, 'a, H, T>;
51
52 fn paint_in<'cx, 'a, H: UiHost>(
53 &self,
54 cx: &'cx mut ElementContext<'a, H>,
55 ) -> WatchedModel<'cx, '_, 'a, H, T> {
56 self.watch_in(cx).paint()
57 }
58
59 fn layout_in<'cx, 'a, H: UiHost>(
60 &self,
61 cx: &'cx mut ElementContext<'a, H>,
62 ) -> WatchedModel<'cx, '_, 'a, H, T> {
63 self.watch_in(cx).layout()
64 }
65
66 fn hit_test_in<'cx, 'a, H: UiHost>(
67 &self,
68 cx: &'cx mut ElementContext<'a, H>,
69 ) -> WatchedModel<'cx, '_, 'a, H, T> {
70 self.watch_in(cx).hit_test()
71 }
72}
73
74impl<T: Any> TrackedModelExt<T> for Model<T> {
75 fn watch_in<'cx, 'a, H: UiHost>(
76 &self,
77 cx: &'cx mut ElementContext<'a, H>,
78 ) -> WatchedModel<'cx, '_, 'a, H, T> {
79 WatchedModel {
80 cx,
81 model: self,
82 invalidation: Invalidation::Paint,
83 }
84 }
85}
86
87#[must_use]
88pub struct WatchedModel<'cx, 'm, 'a, H: UiHost, T: Any> {
89 cx: &'cx mut ElementContext<'a, H>,
90 model: &'m Model<T>,
91 invalidation: Invalidation,
92}
93
94impl<'cx, 'm, 'a, H: UiHost, T: Any> WatchedModel<'cx, 'm, 'a, H, T> {
95 pub fn invalidation(mut self, invalidation: Invalidation) -> Self {
96 self.invalidation = invalidation;
97 self
98 }
99
100 pub fn paint(self) -> Self {
101 self.invalidation(Invalidation::Paint)
102 }
103
104 pub fn layout(self) -> Self {
105 self.invalidation(Invalidation::Layout)
106 }
107
108 pub fn hit_test(self) -> Self {
109 self.invalidation(Invalidation::HitTest)
110 }
111
112 pub fn observe(self) {
113 self.cx.observe_model(self.model, self.invalidation);
114 }
115
116 pub fn revision(self) -> Option<u64> {
117 self.cx.observe_model(self.model, self.invalidation);
118 self.cx.app.models().revision(self.model)
119 }
120
121 pub fn copied(self) -> Option<T>
122 where
123 T: Copy,
124 {
125 self.cx.get_model_copied(self.model, self.invalidation)
126 }
127
128 pub fn copied_or(self, default: T) -> T
129 where
130 T: Copy,
131 {
132 self.copied().unwrap_or(default)
133 }
134
135 pub fn copied_or_default(self) -> T
136 where
137 T: Copy + Default,
138 {
139 self.copied().unwrap_or_default()
140 }
141
142 pub fn cloned(self) -> Option<T>
143 where
144 T: Clone,
145 {
146 self.cx.get_model_cloned(self.model, self.invalidation)
147 }
148
149 pub fn cloned_or(self, default: T) -> T
150 where
151 T: Clone,
152 {
153 self.cloned().unwrap_or(default)
154 }
155
156 pub fn cloned_or_else(self, f: impl FnOnce() -> T) -> T
157 where
158 T: Clone,
159 {
160 self.cloned().unwrap_or_else(f)
161 }
162
163 pub fn cloned_or_default(self) -> T
164 where
165 T: Clone + Default,
166 {
167 self.cloned().unwrap_or_default()
168 }
169
170 pub fn value(self) -> Option<T>
173 where
174 T: Clone,
175 {
176 self.cloned()
177 }
178
179 pub fn value_or(self, default: T) -> T
180 where
181 T: Clone,
182 {
183 self.value().unwrap_or(default)
184 }
185
186 pub fn value_or_else(self, f: impl FnOnce() -> T) -> T
187 where
188 T: Clone,
189 {
190 self.value().unwrap_or_else(f)
191 }
192
193 pub fn value_or_default(self) -> T
194 where
195 T: Clone + Default,
196 {
197 self.value().unwrap_or_default()
198 }
199
200 pub fn read_ref<R>(self, f: impl FnOnce(&T) -> R) -> Result<R, ModelUpdateError> {
201 self.cx.read_model_ref(self.model, self.invalidation, f)
202 }
203
204 pub fn read<R>(self, f: impl FnOnce(&mut H, &T) -> R) -> Result<R, ModelUpdateError> {
205 self.cx.read_model(self.model, self.invalidation, f)
206 }
207}
208
209#[cfg(feature = "state-query")]
210pub trait QueryHandleWatchExt<T: 'static> {
211 fn watch_query<'cx, 'a, H: UiHost>(
212 &self,
213 cx: &'cx mut ElementContext<'a, H>,
214 ) -> WatchedModel<'cx, '_, 'a, H, QueryState<T>>;
215
216 fn paint_query<'cx, 'a, H: UiHost>(
217 &self,
218 cx: &'cx mut ElementContext<'a, H>,
219 ) -> WatchedModel<'cx, '_, 'a, H, QueryState<T>> {
220 self.watch_query(cx).paint()
221 }
222
223 fn layout_query<'cx, 'a, H: UiHost>(
224 &self,
225 cx: &'cx mut ElementContext<'a, H>,
226 ) -> WatchedModel<'cx, '_, 'a, H, QueryState<T>> {
227 self.watch_query(cx).layout()
228 }
229
230 fn hit_test_query<'cx, 'a, H: UiHost>(
231 &self,
232 cx: &'cx mut ElementContext<'a, H>,
233 ) -> WatchedModel<'cx, '_, 'a, H, QueryState<T>> {
234 self.watch_query(cx).hit_test()
235 }
236}
237
238#[cfg(feature = "state-query")]
239impl<T: 'static> QueryHandleWatchExt<T> for QueryHandle<T> {
240 fn watch_query<'cx, 'a, H: UiHost>(
241 &self,
242 cx: &'cx mut ElementContext<'a, H>,
243 ) -> WatchedModel<'cx, '_, 'a, H, QueryState<T>> {
244 WatchedModel {
245 cx,
246 model: self.model(),
247 invalidation: Invalidation::Paint,
248 }
249 }
250}