Skip to main content

euv_ui/hook/debounced_value/
impl.rs

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