fret_ui_headless/
tooltip_delay_group.rs1#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
8pub struct TooltipDelayGroupConfig {
9 pub delay_ticks: u64,
10 pub skip_delay_ticks: u64,
11}
12
13impl TooltipDelayGroupConfig {
14 pub fn new(delay_ticks: u64, skip_delay_ticks: u64) -> Self {
15 Self {
16 delay_ticks,
17 skip_delay_ticks,
18 }
19 }
20}
21
22#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
23pub struct TooltipDelayGroupState {
24 pub last_closed_at: Option<u64>,
25}
26
27impl TooltipDelayGroupState {
28 pub fn open_delay_ticks(&self, now: u64, cfg: TooltipDelayGroupConfig) -> u64 {
29 if cfg.delay_ticks == 0 {
30 return 0;
31 }
32 let Some(last) = self.last_closed_at else {
33 return cfg.delay_ticks;
34 };
35 if cfg.skip_delay_ticks == 0 {
36 return cfg.delay_ticks;
37 }
38 let elapsed = now.saturating_sub(last);
39 if elapsed <= cfg.skip_delay_ticks {
40 0
41 } else {
42 cfg.delay_ticks
43 }
44 }
45
46 pub fn note_closed(&mut self, now: u64) {
47 self.last_closed_at = Some(now);
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn open_delay_is_skipped_within_skip_window() {
57 let mut st = TooltipDelayGroupState::default();
58 let cfg = TooltipDelayGroupConfig::new(10, 30);
59
60 assert_eq!(st.open_delay_ticks(100, cfg), 10);
61
62 st.note_closed(120);
63 assert_eq!(st.open_delay_ticks(121, cfg), 0);
64 assert_eq!(st.open_delay_ticks(150, cfg), 0);
65 assert_eq!(st.open_delay_ticks(151, cfg), 10);
66 }
67
68 #[test]
69 fn zero_delay_always_opens_immediately() {
70 let mut st = TooltipDelayGroupState::default();
71 let cfg = TooltipDelayGroupConfig::new(0, 30);
72
73 assert_eq!(st.open_delay_ticks(10, cfg), 0);
74 st.note_closed(20);
75 assert_eq!(st.open_delay_ticks(21, cfg), 0);
76 }
77}