Skip to main content

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