use std::cell::Cell;
use std::num::NonZeroU64;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use instant::Instant;
use crate::kurbo::Point;
use crate::WinHandler;
const MULTI_CLICK_INTERVAL: Duration = Duration::from_millis(500);
const MULTI_CLICK_MAX_DISTANCE: f64 = 5.0;
#[allow(dead_code)]
pub fn strip_access_key(raw_menu_text: &str) -> String {
let mut saw_ampersand = false;
let mut result = String::new();
for c in raw_menu_text.chars() {
if c == '&' {
if saw_ampersand {
result.push(c);
}
saw_ampersand = !saw_ampersand;
} else {
result.push(c);
saw_ampersand = false;
}
}
result
}
pub(crate) trait IdleCallback: Send {
fn call(self: Box<Self>, a: &mut dyn WinHandler);
}
impl<F: FnOnce(&mut dyn WinHandler) + Send> IdleCallback for F {
fn call(self: Box<F>, a: &mut dyn WinHandler) {
(*self)(a)
}
}
pub struct Counter(AtomicU64);
impl Counter {
pub const fn new() -> Counter {
Counter(AtomicU64::new(1))
}
pub const unsafe fn new_unchecked(init: u64) -> Counter {
Counter(AtomicU64::new(init))
}
pub fn next(&self) -> u64 {
self.0.fetch_add(1, Ordering::Relaxed)
}
pub fn next_nonzero(&self) -> NonZeroU64 {
unsafe { NonZeroU64::new_unchecked(self.0.fetch_add(1, Ordering::Relaxed)) }
}
}
#[derive(Debug, Clone)]
pub struct ClickCounter {
max_interval: Cell<Duration>,
max_distance: Cell<f64>,
last_click: Cell<Instant>,
last_pos: Cell<Point>,
click_count: Cell<u8>,
}
#[allow(dead_code)]
impl ClickCounter {
pub fn new(max_interval: Duration, max_distance: f64) -> ClickCounter {
ClickCounter {
max_interval: Cell::new(max_interval),
max_distance: Cell::new(max_distance),
last_click: Cell::new(Instant::now()),
click_count: Cell::new(0),
last_pos: Cell::new(Point::new(f64::MAX, 0.0)),
}
}
pub fn set_interval_ms(&self, millis: u64) {
self.max_interval.set(Duration::from_millis(millis))
}
pub fn set_distance(&self, distance: f64) {
self.max_distance.set(distance)
}
pub fn count_for_click(&self, click_pos: Point) -> u8 {
let click_time = Instant::now();
let last_time = self.last_click.replace(click_time);
let last_pos = self.last_pos.replace(click_pos);
let elapsed = click_time - last_time;
let distance = last_pos.distance(click_pos);
if elapsed > self.max_interval.get() || distance > self.max_distance.get() {
self.click_count.set(0);
}
let click_count = self.click_count.get().saturating_add(1);
self.click_count.set(click_count);
click_count
}
}
impl Default for ClickCounter {
fn default() -> Self {
ClickCounter::new(MULTI_CLICK_INTERVAL, MULTI_CLICK_MAX_DISTANCE)
}
}