Added Watcher type

This commit is contained in:
Jonathan Johnson 2024-09-02 09:23:52 -07:00
parent 0dd18826c9
commit 6a346ea3ef
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
2 changed files with 49 additions and 0 deletions

View file

@ -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

View file

@ -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<usize>);
impl Watcher {
/// Ensures all callbacks attached to this watcher are invoked when `other`
/// is changed.
pub fn watch<T>(&self, other: &impl Source<T>)
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<F, T>(&self, mut when_changed: F) -> Dynamic<T>
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<F>(&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();