euv_ui/hook/debounced_value/impl.rs
1use super::*;
2
3/// Inherent implementation of [`DebouncedValue`].
4impl<T: Clone + PartialEq + Default + 'static> DebouncedValue<T> {
5 /// Schedules `next` to become the emitted value. The
6 /// commit happens on the next `tick` call at or after
7 /// `now_ms + delay_ms`.
8 ///
9 /// Calling `set` repeatedly within `delay_ms` of each
10 /// other means only the last value wins — that's the
11 /// "debounce" contract.
12 ///
13 /// # Arguments
14 ///
15 /// - `T: Clone + PartialEq + Default + 'static` - A generic type parameter.
16 /// - `u64` - The current time in milliseconds (any monotonic
17 /// source; on the web use `performance.now()`).
18 pub fn set(&self, next: T, now_ms: u64) {
19 self.get_state().set(DebounceState::Pending(now_ms, next));
20 }
21
22 /// Drives the state machine forward.
23 ///
24 /// - If the state is `Idle`, this is a no-op.
25 /// - If the state is `Pending(set_at, value)`, this
26 /// emits `value` (writing it to the `value` signal)
27 /// iff `now_ms - set_at >= delay_ms`. Otherwise the
28 /// pending value is preserved and the call returns
29 /// `false`.
30 ///
31 /// Returns `true` when a pending value was emitted,
32 /// `false` otherwise.
33 ///
34 /// # Arguments
35 ///
36 /// - `u64` - The current time in milliseconds.
37 ///
38 /// # Returns
39 ///
40 /// - `bool` - A boolean.
41 pub fn tick(&self, now_ms: u64) -> bool {
42 match self.get_state().get() {
43 DebounceState::Idle => false,
44 DebounceState::Pending(set_at, _) => {
45 if now_ms.saturating_sub(set_at) >= u64::from(self.delay_ms) {
46 let pending: T = match self.get_state().get() {
47 DebounceState::Pending(_, value) => value,
48 DebounceState::Idle => unreachable!(),
49 };
50 self.get_value().set(pending);
51 self.get_state().set(DebounceState::Idle);
52 true
53 } else {
54 false
55 }
56 }
57 }
58 }
59
60 /// Cancels any pending value without emitting it.
61 /// The emitted value is left untouched.
62 pub fn cancel(&self) {
63 self.get_state().set(DebounceState::Idle);
64 }
65
66 /// Returns the currently emitted value as a snapshot.
67 ///
68 /// # Returns
69 ///
70 /// - `T` - The current value (or a snapshot thereof).
71 pub fn get(&self) -> T {
72 self.get_value().get()
73 }
74
75 /// Returns `true` when a value is waiting to be
76 /// emitted.
77 ///
78 /// # Returns
79 ///
80 /// - `bool` - `true` when a value is waiting to be emitted.
81 pub fn is_pending(&self) -> bool {
82 matches!(self.get_state().get(), DebounceState::Pending(_, _))
83 }
84}
85
86/// Debug formatting for [`DebouncedValue`].
87impl<T: Clone + PartialEq + Debug + Default + 'static> Display for DebouncedValue<T> {
88 /// Formats the [`DebouncedValue`] via the supplied formatter.
89 ///
90 /// # Arguments
91 ///
92 /// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
93 ///
94 /// # Returns
95 ///
96 /// - `FmtResult` - Result of the formatting operation.
97 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
98 let pending: &DebounceState<T> = &self.get_state().get();
99 match pending {
100 DebounceState::Idle => {
101 write!(formatter, "DebouncedValue({:?})", self.get_value().get())
102 }
103 DebounceState::Pending(_, value) => {
104 write!(formatter, "DebouncedValue(pending={value:?})")
105 }
106 }
107 }
108}