From 6a346ea3ef1e6b6b37767d8c61e0d6a9f8581422 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Mon, 2 Sep 2024 09:23:52 -0700 Subject: [PATCH] Added Watcher type --- CHANGELOG.md | 3 +++ src/value.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c2f9db..7d4a5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MakeWidget::to_button()` - `MakeWidget::to_checkbox()` - `WidgetInstance::to_window()` +- `Watcher` is a new type that executes callbacks when one or more sources + changes. This can simplify some data flows such as regenerating a value when + many one of many UI elements are changed. [139]: https://github.com/khonsulabs/cushy/issues/139 diff --git a/src/value.rs b/src/value.rs index ee98b22..8f3a7a5 100644 --- a/src/value.rs +++ b/src/value.rs @@ -3794,6 +3794,52 @@ impl InvalidationBatch<'_> { } } +/// Watches one or more [`Source`]s and invokes associated callbacks when +/// changed. +/// +/// This type is useful when needing to ensure logic is executed or a value is +/// regenerated each time one of many sources are changed. +#[derive(Debug, Clone, Default)] +pub struct Watcher(Dynamic); + +impl Watcher { + /// Ensures all callbacks attached to this watcher are invoked when `other` + /// is changed. + pub fn watch(&self, other: &impl Source) + where + T: Send + 'static, + { + let counter = self.0.clone(); + self.0 + .set_source(other.for_each_subsequent_generational(move |guard| { + // We want to drop our guard before changing the counter to + // ensure all callbacks associated with our counter are executed + // without this type holding any source locks. + drop(guard); + let mut counter = counter.lock(); + *counter = counter.wrapping_add(1); + })); + } + + /// Returns a new dynamic populated by invoking `when_changed` each time any + /// watched source is updated. + pub fn map_changed(&self, mut when_changed: F) -> Dynamic + where + F: FnMut() -> T + Send + 'static, + T: PartialEq + Send + 'static, + { + self.0.map_each(move |_| when_changed()) + } + + /// Invokes `when_changed` each time any watched source is updated. + pub fn when_changed(&self, mut when_changed: F) -> CallbackHandle + where + F: FnMut() + Send + 'static, + { + self.0.for_each(move |_| when_changed()) + } +} + #[test] fn map_cycle_is_finite() { crate::initialize_tracing();