mirror of
https://github.com/danbulant/cushy
synced 2026-06-24 17:12:11 +00:00
Debouncing
This commit is contained in:
parent
aeb55e0b94
commit
3f2aace55e
1 changed files with 82 additions and 7 deletions
89
src/value.rs
89
src/value.rs
|
|
@ -9,10 +9,11 @@ use std::sync::atomic::{self, AtomicBool};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard, TryLockError};
|
use std::sync::{Arc, Mutex, MutexGuard, TryLockError};
|
||||||
use std::task::{Poll, Waker};
|
use std::task::{Poll, Waker};
|
||||||
use std::thread::ThreadId;
|
use std::thread::ThreadId;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use ahash::AHashSet;
|
use ahash::AHashSet;
|
||||||
|
|
||||||
use crate::animation::{DynamicTransition, LinearInterpolate};
|
use crate::animation::{AnimationHandle, DynamicTransition, IntoAnimate, LinearInterpolate, Spawn};
|
||||||
use crate::context::sealed::WindowHandle;
|
use crate::context::sealed::WindowHandle;
|
||||||
use crate::context::{self, WidgetContext};
|
use crate::context::{self, WidgetContext};
|
||||||
use crate::utils::{run_in_bg, IgnorePoison, UnwindsafeCondvar, WithClone};
|
use crate::utils::{run_in_bg, IgnorePoison, UnwindsafeCondvar, WithClone};
|
||||||
|
|
@ -159,6 +160,33 @@ impl<T> Dynamic<T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new dynamic that contains the updated contents of this dynamic
|
||||||
|
/// at most once every `period`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn debounced_every(&self, period: Duration) -> Self
|
||||||
|
where
|
||||||
|
T: PartialEq + Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let debounced = Dynamic::new(self.get());
|
||||||
|
let mut debounce = Debounce::new(debounced.clone(), period);
|
||||||
|
self.for_each_cloned(move |value| debounce.update(value));
|
||||||
|
debounced
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new dynamic that contains the updated contents of this dynamic
|
||||||
|
/// delayed by `period`. Each time this value is updated, the delay is
|
||||||
|
/// reset.
|
||||||
|
#[must_use]
|
||||||
|
pub fn debounced_with_delay(&self, period: Duration) -> Self
|
||||||
|
where
|
||||||
|
T: PartialEq + Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let debounced = Dynamic::new(self.get());
|
||||||
|
let mut debounce = Debounce::new(debounced.clone(), period).extending();
|
||||||
|
self.for_each_cloned(move |value| debounce.update(value));
|
||||||
|
debounced
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a new dynamic that is updated using `U::from(T.clone())` each
|
/// Returns a new dynamic that is updated using `U::from(T.clone())` each
|
||||||
/// time `self` is updated.
|
/// time `self` is updated.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
@ -1858,7 +1886,6 @@ impl Validations {
|
||||||
self.invalid.map_mut(|invalid| *invalid += 1);
|
self.invalid.map_mut(|invalid| *invalid += 1);
|
||||||
|
|
||||||
let invalid_count = self.invalid.clone();
|
let invalid_count = self.invalid.clone();
|
||||||
let state = self.state.clone();
|
|
||||||
let dynamic = dynamic.clone();
|
let dynamic = dynamic.clone();
|
||||||
let mut initial_generation = dynamic.generation();
|
let mut initial_generation = dynamic.generation();
|
||||||
let mut invalid = true;
|
let mut invalid = true;
|
||||||
|
|
@ -1884,11 +1911,6 @@ impl Validations {
|
||||||
match current_state {
|
match current_state {
|
||||||
ValidationsState::Resetting => {
|
ValidationsState::Resetting => {
|
||||||
initial_generation = dynamic.generation();
|
initial_generation = dynamic.generation();
|
||||||
let state = state.clone();
|
|
||||||
|
|
||||||
run_in_bg(move || {
|
|
||||||
state.set(ValidationsState::Initial);
|
|
||||||
});
|
|
||||||
Validation::None
|
Validation::None
|
||||||
}
|
}
|
||||||
ValidationsState::Initial if initial_generation == dynamic.generation() => {
|
ValidationsState::Initial if initial_generation == dynamic.generation() => {
|
||||||
|
|
@ -1963,6 +1985,7 @@ impl Validations {
|
||||||
/// Resets the validation status for all related validations.
|
/// Resets the validation status for all related validations.
|
||||||
pub fn reset(&self) {
|
pub fn reset(&self) {
|
||||||
self.state.set(ValidationsState::Resetting);
|
self.state.set(ValidationsState::Resetting);
|
||||||
|
self.state.set(ValidationsState::Initial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2043,3 +2066,55 @@ impl WhenValidation<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Debounce<T> {
|
||||||
|
destination: Dynamic<T>,
|
||||||
|
period: Duration,
|
||||||
|
delay: Option<AnimationHandle>,
|
||||||
|
buffer: Dynamic<T>,
|
||||||
|
extend: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Debounce<T>
|
||||||
|
where
|
||||||
|
T: Clone + PartialEq + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
pub fn new(destination: Dynamic<T>, period: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: Dynamic::new(destination.get()),
|
||||||
|
destination,
|
||||||
|
period,
|
||||||
|
delay: None,
|
||||||
|
extend: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extending(mut self) -> Self {
|
||||||
|
self.extend = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, value: T) {
|
||||||
|
if self.buffer.replace(value).is_some() {
|
||||||
|
let create_delay = if self.extend {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
self.delay
|
||||||
|
.as_ref()
|
||||||
|
.map_or(true, AnimationHandle::is_complete)
|
||||||
|
};
|
||||||
|
|
||||||
|
if create_delay {
|
||||||
|
let destination = self.destination.clone();
|
||||||
|
let buffer = self.buffer.clone();
|
||||||
|
self.delay = Some(
|
||||||
|
self.period
|
||||||
|
.on_complete(move || {
|
||||||
|
destination.set(buffer.get());
|
||||||
|
})
|
||||||
|
.spawn(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue