From b38aa791241232cee3c3c1bd9a4ab161ce3428a7 Mon Sep 17 00:00:00 2001 From: Samuel Guerra Date: Tue, 20 Jun 2023 00:21:59 -0300 Subject: [PATCH] Refactored explicit var update requests to propagate across bindings. --- TODO/_current.md | 3 -- zero-ui-core/src/var.rs | 66 +++++++++++++++++++++++++------- zero-ui-core/src/var/cow.rs | 14 +++---- zero-ui-core/src/var/flat_map.rs | 2 +- zero-ui-core/src/var/map_ref.rs | 12 +++--- zero-ui-core/src/var/util.rs | 4 +- zero-ui-core/src/var/when.rs | 8 ++-- 7 files changed, 74 insertions(+), 35 deletions(-) diff --git a/TODO/_current.md b/TODO/_current.md index c06250666..cec2e6e56 100644 --- a/TODO/_current.md +++ b/TODO/_current.md @@ -64,9 +64,6 @@ # Vars -* Previous value in hook? - - We are cloning anyway, could have the prev-value in a Cow. - - Owned if actually changed. * Merge. * Config example and tests with errors. diff --git a/zero-ui-core/src/var.rs b/zero-ui-core/src/var.rs index 31fc60e91..ba91990ff 100644 --- a/zero-ui-core/src/var.rs +++ b/zero-ui-core/src/var.rs @@ -969,21 +969,21 @@ impl<'a, T: VarValue> VarModify<'a, T> { } } - /// Returns `(notify, new_value, tags)`. - pub fn finish(self) -> (bool, Option, Vec>) { + /// Returns `(notify, new_value, update, tags)`. + pub fn finish(self) -> (bool, Option, bool, Vec>) { match self.value { Cow::Borrowed(_) => { if self.update { - return (true, None, self.tags); + return (true, None, true, self.tags); } } Cow::Owned(v) => { if self.update || self.current_value != &v { - return (true, Some(v), self.tags); + return (true, Some(v), self.update, self.tags); } } } - (false, None, vec![]) + (false, None, false, vec![]) } } impl<'a, T: VarValue> ops::Deref for VarModify<'a, T> { @@ -1002,19 +1002,27 @@ impl<'a, T: VarValue> std::convert::AsRef for VarModify<'a, T> { /// Arguments for [`AnyVar::hook`]. pub struct VarHookArgs<'a> { value: &'a dyn AnyVarValue, + update: bool, tags: &'a [Box], } impl<'a> VarHookArgs<'a> { /// New from updated value and custom tag. - pub fn new(value: &'a dyn AnyVarValue, tags: &'a [Box]) -> Self { - Self { value, tags } + pub fn new(value: &'a dyn AnyVarValue, update: bool, tags: &'a [Box]) -> Self { + Self { value, update, tags } } /// Reference the updated value. - pub fn value(&self) -> &dyn AnyVarValue { + pub fn value(&self) -> &'a dyn AnyVarValue { self.value } + /// If update was explicitly requested. + /// + /// Note that bindings/mappings propagate this update request. + pub fn update(&self) -> bool { + self.update + } + /// Value type ID. pub fn value_type(&self) -> TypeId { self.value.as_any().type_id() @@ -1604,8 +1612,15 @@ pub trait Var: IntoVar + AnyVar + Clone { #[cfg(not(dyn_closure))] let mut map = map; - var_bind(self, other, move |value, _, other| { - let _ = other.set(map(value)); + var_bind(self, other, move |value, args, other| { + let value = map(value); + let update = args.update; + let _ = other.modify(move |vm| { + vm.set(value); + if update { + vm.update(); + } + }); }) } @@ -1627,9 +1642,15 @@ pub trait Var: IntoVar + AnyVar + Clone { #[cfg(not(dyn_closure))] let mut map = map; - var_bind(self, other, move |value, _, other| { + var_bind(self, other, move |value, args, other| { if let Some(value) = map(value) { - let _ = other.set(value); + let update = args.update; + let _ = other.modify(move |vm| { + vm.set(value); + if update { + vm.update(); + } + }); } }) } @@ -1655,9 +1676,13 @@ pub trait Var: IntoVar + AnyVar + Clone { let is_from_other = args.downcast_tags::().any(|&b| b == binding_tag); if !is_from_other { let value = map(value); + let update = args.update; let _ = other.modify(move |vm| { vm.set(value); vm.push_tag(binding_tag); + if update { + vm.update(); + } }); } }); @@ -1666,9 +1691,13 @@ pub trait Var: IntoVar + AnyVar + Clone { let is_from_self = args.downcast_tags::().any(|&b| b == binding_tag); if !is_from_self { let value = map_back(value); + let update = args.update; let _ = self_.modify(move |vm| { vm.set(value); vm.push_tag(binding_tag); + if update { + vm.update(); + } }); } }); @@ -1696,9 +1725,13 @@ pub trait Var: IntoVar + AnyVar + Clone { let is_from_other = args.downcast_tags::().any(|&b| b == binding_tag); if !is_from_other { if let Some(value) = map(value) { + let update = args.update; let _ = other.modify(move |vm| { vm.set(value); vm.push_tag(binding_tag); + if update { + vm.update(); + } }); } } @@ -1708,9 +1741,13 @@ pub trait Var: IntoVar + AnyVar + Clone { let is_from_self = args.downcast_tags::().any(|&b| b == binding_tag); if !is_from_self { if let Some(value) = map_back(value) { + let update = args.update; let _ = self_.modify(move |vm| { vm.set(value); vm.push_tag(binding_tag); + if update { + vm.update(); + } }); } } @@ -2039,9 +2076,12 @@ pub trait Var: IntoVar + AnyVar + Clone { let easing_fn = easing_fn.clone(); let mut _anim_handle = animation::AnimationHandle::dummy(); - var_bind(&source, &easing_var, move |value, _, easing_var| { + var_bind(&source, &easing_var, move |value, args, easing_var| { let easing_fn = easing_fn.clone(); _anim_handle = easing_var.ease(value.clone(), duration, move |t| easing_fn(t)); + if args.update { + easing_var.update(); + } }) .perm(); easing_var.read_only() diff --git a/zero-ui-core/src/var/cow.rs b/zero-ui-core/src/var/cow.rs index f0a68951b..91f12a874 100644 --- a/zero-ui-core/src/var/cow.rs +++ b/zero-ui-core/src/var/cow.rs @@ -66,20 +66,20 @@ impl> ArcCowVar { match data { Data::Source { source, hooks, .. } => { - let (update, new_value, tags) = source.with(|val| { + let (notify, new_value, update, tags) = source.with(|val| { let mut vm = VarModify::new(val); modify(&mut vm); vm.finish() }); let value = new_value.unwrap_or_else(|| source.get()); - if update { - let hook_args = VarHookArgs::new(&value, &tags); + if notify { + let hook_args = VarHookArgs::new(&value, update, &tags); hooks.retain(|h| h.call(&hook_args)); UPDATES.update(None); } *data = Data::Owned { value, - last_update: if update { VARS.update_id() } else { source.last_update() }, + last_update: if notify { VARS.update_id() } else { source.last_update() }, hooks: mem::take(hooks), animation: VARS.current_modify(), }; @@ -98,18 +98,18 @@ impl> ArcCowVar { *animation = curr_anim; } - let (update, new_value, tags) = { + let (notify, new_value, update, tags) = { let mut vm = VarModify::new(value); modify(&mut vm); vm.finish() }; - if update { + if notify { if let Some(nv) = new_value { *value = nv; } *last_update = VARS.update_id(); - let hook_args = VarHookArgs::new(value, &tags); + let hook_args = VarHookArgs::new(value, update, &tags); hooks.retain(|h| h.call(&hook_args)); UPDATES.update(None); } diff --git a/zero-ui-core/src/var/flat_map.rs b/zero-ui-core/src/var/flat_map.rs index b762ea3da..2722f6605 100644 --- a/zero-ui-core/src/var/flat_map.rs +++ b/zero-ui-core/src/var/flat_map.rs @@ -52,7 +52,7 @@ where data.var_handle = data.var.hook(ArcFlatMapVar::on_var_hook(weak_flat.clone())); data.last_update = VARS.update_id(); data.var.with(|value| { - let args = VarHookArgs::new(value, args.tags()); + let args = VarHookArgs::new(value, args.update(), args.tags()); data.hooks.retain(|h| h.call(&args)); }); } diff --git a/zero-ui-core/src/var/map_ref.rs b/zero-ui-core/src/var/map_ref.rs index 48647316a..17cde63ea 100644 --- a/zero-ui-core/src/var/map_ref.rs +++ b/zero-ui-core/src/var/map_ref.rs @@ -79,7 +79,7 @@ impl> AnyVar for MapRef { self.source.hook(Box::new(move |args| { if let Some(value) = args.downcast_value() { let value = map(value); - pos_modify_action(&VarHookArgs::new(value, args.tags())) + pos_modify_action(&VarHookArgs::new(value, args.update(), args.tags())) } else { true } @@ -301,7 +301,7 @@ impl> AnyVar for MapRefBidi { self.source.hook(Box::new(move |args| { if let Some(value) = args.downcast_value() { let value = map(value); - pos_modify_action(&VarHookArgs::new(value, args.tags())) + pos_modify_action(&VarHookArgs::new(value, args.update(), args.tags())) } else { true } @@ -406,13 +406,15 @@ impl> Var for MapRefBidi { let map = self.map.clone(); let map_mut = self.map_mut.clone(); self.source.modify(move |vm| { - let (update, new_value, tags) = { + let (notify, new_value, update, tags) = { let mut vm = VarModify::new(map(vm.as_ref())); modify(&mut vm); vm.finish() }; - if update { - vm.update(); + if notify { + if update { + vm.update(); + } if let Some(nv) = new_value { *map_mut(vm.to_mut()) = nv; } diff --git a/zero-ui-core/src/var/util.rs b/zero-ui-core/src/var/util.rs index 863f93630..52cdb11c1 100644 --- a/zero-ui-core/src/var/util.rs +++ b/zero-ui-core/src/var/util.rs @@ -320,7 +320,7 @@ impl VarData { meta.animation = curr_anim; } - let (notify, new_value, tags) = self.with(|value| { + let (notify, new_value, update, tags) = self.with(|value| { let mut value = VarModify::new(value); modify(&mut value); value.finish() @@ -334,7 +334,7 @@ impl VarData { meta.last_update = VARS.update_id(); self.with(|val| { - let args = VarHookArgs::new(val, &tags); + let args = VarHookArgs::new(val, update, &tags); meta.hooks.retain(|h| h.call(&args)); }); UPDATES.update(None); diff --git a/zero-ui-core/src/var/when.rs b/zero-ui-core/src/var/when.rs index 5e7fc1496..0a94986bc 100644 --- a/zero-ui-core/src/var/when.rs +++ b/zero-ui-core/src/var/when.rs @@ -317,7 +317,7 @@ impl ArcWhenVar { if update { drop(data); - VARS.schedule_update(ArcWhenVar::apply_update(rc_when, args.tags_vec())); + VARS.schedule_update(ArcWhenVar::apply_update(rc_when, false, args.tags_vec())); } true @@ -333,7 +333,7 @@ impl ArcWhenVar { let data = rc_when.w.lock(); if data.active == i { drop(data); - VARS.schedule_update(ArcWhenVar::apply_update(rc_when, args.tags_vec())); + VARS.schedule_update(ArcWhenVar::apply_update(rc_when, args.update(), args.tags_vec())); } true } else { @@ -342,7 +342,7 @@ impl ArcWhenVar { }) } - fn apply_update(rc_merge: Arc>, tags: Vec>) -> VarUpdateFn { + fn apply_update(rc_merge: Arc>, update: bool, tags: Vec>) -> VarUpdateFn { Box::new(move || { let mut data = rc_merge.w.lock(); let data = &mut *data; @@ -363,7 +363,7 @@ impl ArcWhenVar { }; active.with(|value| { - let args = VarHookArgs::new(value, &tags); + let args = VarHookArgs::new(value, update, &tags); data.hooks.retain(|h| h.call(&args)); }); UPDATES.update(None);