druid_shell/
common_util.rs1use std::cell::Cell;
18use std::num::NonZeroU64;
19use std::sync::atomic::{AtomicU64, Ordering};
20use std::time::Duration;
21
22use instant::Instant;
23
24use crate::kurbo::Point;
25use crate::WinHandler;
26
27const MULTI_CLICK_INTERVAL: Duration = Duration::from_millis(500);
29const MULTI_CLICK_MAX_DISTANCE: f64 = 5.0;
31
32#[allow(dead_code)]
36pub fn strip_access_key(raw_menu_text: &str) -> String {
37 let mut saw_ampersand = false;
38 let mut result = String::new();
39 for c in raw_menu_text.chars() {
40 if c == '&' {
41 if saw_ampersand {
42 result.push(c);
43 }
44 saw_ampersand = !saw_ampersand;
45 } else {
46 result.push(c);
47 saw_ampersand = false;
48 }
49 }
50 result
51}
52
53pub(crate) trait IdleCallback: Send {
55 fn call(self: Box<Self>, a: &mut dyn WinHandler);
56}
57
58impl<F: FnOnce(&mut dyn WinHandler) + Send> IdleCallback for F {
59 fn call(self: Box<F>, a: &mut dyn WinHandler) {
60 (*self)(a)
61 }
62}
63
64pub struct Counter(AtomicU64);
72
73impl Counter {
74 pub const fn new() -> Counter {
76 Counter(AtomicU64::new(1))
77 }
78
79 pub const unsafe fn new_unchecked(init: u64) -> Counter {
85 Counter(AtomicU64::new(init))
86 }
87
88 pub fn next(&self) -> u64 {
90 self.0.fetch_add(1, Ordering::Relaxed)
91 }
92
93 pub fn next_nonzero(&self) -> NonZeroU64 {
95 unsafe { NonZeroU64::new_unchecked(self.0.fetch_add(1, Ordering::Relaxed)) }
97 }
98}
99
100#[derive(Debug, Clone)]
105pub struct ClickCounter {
106 max_interval: Cell<Duration>,
107 max_distance: Cell<f64>,
108 last_click: Cell<Instant>,
109 last_pos: Cell<Point>,
110 click_count: Cell<u8>,
111}
112
113#[allow(dead_code)]
114impl ClickCounter {
115 pub fn new(max_interval: Duration, max_distance: f64) -> ClickCounter {
117 ClickCounter {
118 max_interval: Cell::new(max_interval),
119 max_distance: Cell::new(max_distance),
120 last_click: Cell::new(Instant::now()),
121 click_count: Cell::new(0),
122 last_pos: Cell::new(Point::new(f64::MAX, 0.0)),
123 }
124 }
125
126 pub fn set_interval_ms(&self, millis: u64) {
127 self.max_interval.set(Duration::from_millis(millis))
128 }
129
130 pub fn set_distance(&self, distance: f64) {
131 self.max_distance.set(distance)
132 }
133
134 pub fn count_for_click(&self, click_pos: Point) -> u8 {
136 let click_time = Instant::now();
137 let last_time = self.last_click.replace(click_time);
138 let last_pos = self.last_pos.replace(click_pos);
139 let elapsed = click_time - last_time;
140 let distance = last_pos.distance(click_pos);
141 if elapsed > self.max_interval.get() || distance > self.max_distance.get() {
142 self.click_count.set(0);
143 }
144 let click_count = self.click_count.get().saturating_add(1);
145 self.click_count.set(click_count);
146 click_count
147 }
148}
149
150impl Default for ClickCounter {
151 fn default() -> Self {
152 ClickCounter::new(MULTI_CLICK_INTERVAL, MULTI_CLICK_MAX_DISTANCE)
153 }
154}