1#[macro_export]
58macro_rules! impl_from_and_into_var {
59 ($($tt:tt)+) => {
60 $crate::__impl_from_and_into_var! { $($tt)* }
61 };
62}
63
64use parking_lot::RwLock;
65
66use crate::{AnyVarHookArgs, AnyVarValue};
67
68use super::{VARS, VarHandle, VarHook, VarModify, VarUpdateId, VarValue, animation::ModifyInfo};
69
70#[doc(hidden)]
71#[macro_export]
72macro_rules! __impl_from_and_into_var {
73 (
75 $(#[$docs:meta])*
76 fn from ( $($input:tt)+ )
77 $($rest:tt)+
78 ) => {
79 $crate::__impl_from_and_into_var! {
80 =input=>
81 [
82 input { $($input)+ }
83 generics { }
84 docs { $(#[$docs])* }
85 ]
86 ( $($input)+ ) $($rest)+
87 }
88 };
89 (
91 $(#[$docs:meta])*
92 fn from <
93 $($rest:tt)+
94 ) => {
95 $crate::__impl_from_and_into_var! {
96 =generics=>
97 [
98 generics { < }
99 docs { $(#[$docs])* }
100 ]
101 $($rest)+
102 }
103 };
104 (
106 =generics=>
107 [
108 generics { $($generics:tt)+ }
109 $($config:tt)*
110 ]
111
112 >( $($input:tt)+ ) $($rest:tt)+
113 ) => {
114 $crate::__impl_from_and_into_var! {
115 =input=>
116 [
117 input { $($input)+ }
118 generics { $($generics)+ > }
119 $($config)*
120 ]
121 ( $($input)+ ) $($rest)+
122 }
123 };
124 (
126 =generics=>
127 [
128 generics { $($generics:tt)+ }
129 $($config:tt)*
130 ]
131
132 >>( $($input:tt)+ ) $($rest:tt)+
133 ) => {
134 $crate::__impl_from_and_into_var! {
135 =input=>
136 [
137 input { $($input)+ }
138 generics { $($generics)+ >> }
139 $($config)*
140 ]
141 ( $($input)+ ) $($rest)+
142 }
143 };
144 (
146 =generics=>
147 [
148 generics { $($generics:tt)+ }
149 $($config:tt)*
150 ]
151
152 $tt:tt $($rest:tt)+
153 ) => {
154 $crate::__impl_from_and_into_var! {
156 =generics=>
157 [
158 generics { $($generics)+ $tt }
159 $($config)*
160 ]
161 $($rest)*
162 }
163 };
165 (
167 =input=>
168 [$($config:tt)*]
169 ($ident:ident : $Input:ty $(,)?) $($rest:tt)+
170 ) => {
171 $crate::__impl_from_and_into_var! {
172 =output=>
173 [
174 input_type { $Input }
175 $($config)*
176 ]
177 $($rest)+
178 }
179 };
180 (
182 =input=>
183 [$($config:tt)*]
184 (( $($destructure:tt)+ ) : $Input:ty $(,)?) $($rest:tt)+
185 ) => {
186 $crate::__impl_from_and_into_var! {
187 =output=>
188 [
189 input_type { $Input }
190 $($config)*
191 ]
192 $($rest)+
193 }
194 };
195 (
197 =input=>
198 [$($config:tt)*]
199 ([ $($destructure:tt)+ ] : $Input:ty $(,)?) $($rest:tt)+
200 ) => {
201 $crate::__impl_from_and_into_var! {
202 =output=>
203 [
204 input_type { $Input }
205 $($config)*
206 ]
207 $($rest)+
208 }
209 };
210
211 (
213 =output=>
214 [
215 input_type { $Input:ty }
216 input { $($input:tt)+ }
217 generics { $($generics:tt)* }
218 docs { $($docs:tt)* }
219 ]
220 -> $Output:ty
221 ;
222
223 $($rest:tt)*
224 ) => {
225 impl $($generics)* $crate::IntoVar<$Output> for $Input {
226 type Var = $crate::LocalVar<$Output>;
227
228 $($docs)*
229
230 fn into_var(self) -> Self::Var {
231 $crate::LocalVar(self.into())
232 }
233 }
234
235 impl $($generics)* $crate::IntoValue<$Output> for $Input { }
236
237 $crate::__impl_from_and_into_var! {
239 $($rest)*
240 }
241 };
242
243 (
245 =output=>
246 [
247 input_type { $Input:ty }
248 input { $($input:tt)+ }
249 generics { $($generics:tt)* }
250 docs { $($docs:tt)* }
251 ]
252 -> $Output:ty
253 $convert:block
254
255 $($rest:tt)*
256 ) => {
257 impl $($generics)* From<$Input> for $Output {
258 $($docs)*
259
260 fn from($($input)+) -> Self
261 $convert
262 }
263
264 impl $($generics)* $crate::IntoVar<$Output> for $Input {
265 type Var = $crate::LocalVar<$Output>;
266
267 $($docs)*
268
269 fn into_var(self) -> Self::Var {
270 $crate::LocalVar(self.into())
271 }
272 }
273
274 impl $($generics)* $crate::IntoValue<$Output> for $Input { }
275
276 $crate::__impl_from_and_into_var! {
278 $($rest)*
279 }
280 };
281
282 () => {
283 };
285}
286
287struct VarMeta {
288 last_update: VarUpdateId,
289 hooks: Vec<VarHook>,
290 animation: ModifyInfo,
291}
292impl VarMeta {
293 fn push_hook(&mut self, pos_modify_action: Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync>) -> VarHandle {
294 let (hook, weak) = VarHandle::new(pos_modify_action);
295 self.hooks.push(weak);
296 hook
297 }
298
299 fn skip_modify(&mut self) -> bool {
300 let cur_anim = VARS.current_modify();
301 if cur_anim.importance() < self.animation.importance() {
302 return true;
303 }
304 self.animation = cur_anim;
305 false
306 }
307}
308
309struct VarDataInner {
310 value: Box<dyn AnyVarValue>,
311 meta: VarMeta,
312}
313
314pub(super) struct VarData(RwLock<VarDataInner>);
315
316impl VarData {
317 pub fn new(value: impl VarValue) -> Self {
318 Self::new_impl(Box::new(value))
319 }
320 fn new_impl(value: Box<dyn AnyVarValue>) -> Self {
321 Self(RwLock::new(VarDataInner {
322 value,
323 meta: VarMeta {
324 last_update: VarUpdateId::never(),
325 hooks: vec![],
326 animation: ModifyInfo::never(),
327 },
328 }))
329 }
330
331 pub fn into_value<T: VarValue>(self) -> T {
332 *self.0.into_inner().value.into_any().downcast::<T>().unwrap()
333 }
334
335 fn read<T: VarValue>(&self) -> parking_lot::MappedRwLockReadGuard<T> {
336 let read = self.0.read();
337 parking_lot::RwLockReadGuard::map(read, |r| r.value.as_any().downcast_ref::<T>().unwrap())
338 }
339
340 pub fn with<T: VarValue, R>(&self, f: impl FnOnce(&T) -> R) -> R {
342 f(&*self.read())
343 }
344
345 pub fn last_update(&self) -> VarUpdateId {
346 self.0.read().meta.last_update
347 }
348
349 pub fn is_animating(&self) -> bool {
350 self.0.read().meta.animation.is_animating()
351 }
352
353 pub fn modify_importance(&self) -> usize {
354 self.0.read().meta.animation.importance()
355 }
356
357 pub fn push_hook(&self, pos_modify_action: Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync>) -> VarHandle {
358 self.0.write().meta.push_hook(pos_modify_action)
359 }
360
361 pub fn push_animation_hook(&self, handler: Box<dyn FnOnce() + Send>) -> Result<(), Box<dyn FnOnce() + Send>> {
362 self.0.write().meta.animation.hook_animation_stop(handler)
363 }
364
365 #[cfg(feature = "dyn_closure")]
366 pub fn apply_modify<T: VarValue>(&self, modify: Box<dyn FnOnce(&mut VarModify<T>) + 'static>) {
367 apply_modify(
368 &self.0,
369 Box::new(move |v| {
370 let mut value = VarModify::new(v.as_any().downcast_ref::<T>().unwrap());
371 modify(&mut value);
372 let (notify, new_value, update, tags, importance) = value.finish();
373 (
374 notify,
375 match new_value {
376 Some(v) => Some(Box::new(v)),
377 None => None,
378 },
379 update,
380 tags,
381 importance,
382 )
383 }),
384 )
385 }
386
387 #[cfg(not(feature = "dyn_closure"))]
388 pub fn apply_modify<T: VarValue>(&self, modify: impl FnOnce(&mut VarModify<T>) + 'static) {
389 apply_modify(
390 &self.0,
391 Box::new(move |v| {
392 let mut value = VarModify::new(v.as_any().downcast_ref::<T>().unwrap());
393 modify(&mut value);
394 let (notify, new_value, update, tags, importance) = value.finish();
395 (
396 notify,
397 match new_value {
398 Some(v) => Some(Box::new(v)),
399 None => None,
400 },
401 update,
402 tags,
403 importance,
404 )
405 }),
406 )
407 }
408}
409
410fn apply_modify(
411 inner: &RwLock<VarDataInner>,
412 modify: Box<dyn FnOnce(&dyn AnyVarValue) -> (bool, Option<Box<dyn AnyVarValue>>, bool, Vec<Box<dyn AnyVarValue>>, Option<usize>)>,
413) {
414 let mut data = inner.write();
415 if data.meta.skip_modify() {
416 return;
417 }
418
419 let data = parking_lot::RwLockWriteGuard::downgrade(data);
420
421 let (notify, new_value, update, tags, custom_importance) = modify(&*data.value);
422
423 if notify {
424 drop(data);
425 let mut data = inner.write();
426 if let Some(nv) = new_value {
427 data.value = nv;
428 }
429 data.meta.last_update = VARS.update_id();
430
431 if let Some(i) = custom_importance {
432 data.meta.animation.importance = i;
433 }
434
435 if !data.meta.hooks.is_empty() {
436 let mut hooks = std::mem::take(&mut data.meta.hooks);
437
438 let meta = parking_lot::RwLockWriteGuard::downgrade(data);
439
440 let args = AnyVarHookArgs::new(&*meta.value, update, &tags);
441 hooks.retain(|h| h.call(&args));
442 drop(meta);
443
444 let mut data = inner.write();
445 hooks.append(&mut data.meta.hooks);
446 data.meta.hooks = hooks;
447 }
448
449 VARS.wake_app();
450 } else if let Some(i) = custom_importance {
451 drop(data);
452 let mut data = inner.write();
453 data.meta.animation.importance = i;
454 }
455}