-
Notifications
You must be signed in to change notification settings - Fork 567
/
Copy pathcommon_util.rs
154 lines (135 loc) · 4.76 KB
/
common_util.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2019 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Common functions used by the backends
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;
// This is the default timing on windows.
const MULTI_CLICK_INTERVAL: Duration = Duration::from_millis(500);
// the max distance between two clicks for them to count as a multi-click
const MULTI_CLICK_MAX_DISTANCE: f64 = 5.0;
/// Strip the access keys from the menu string.
///
/// Changes "E&xit" to "Exit". Actual ampersands are escaped as "&&".
#[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
}
/// A trait for implementing the boxed callback hack.
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)
}
}
/// An incrementing counter for generating unique ids.
///
/// This can be used safely from multiple threads.
///
/// The counter will overflow if `next()` is called 2^64 - 2 times.
/// If this is possible for your application, and reuse would be undesirable,
/// use something else.
pub struct Counter(AtomicU64);
impl Counter {
/// Create a new counter.
pub const fn new() -> Counter {
Counter(AtomicU64::new(1))
}
/// Creates a new counter with a given starting value.
///
/// # Safety
///
/// The value must not be zero.
pub const unsafe fn new_unchecked(init: u64) -> Counter {
Counter(AtomicU64::new(init))
}
/// Return the next value.
pub fn next(&self) -> u64 {
self.0.fetch_add(1, Ordering::Relaxed)
}
/// Return the next value, as a `NonZeroU64`.
pub fn next_nonzero(&self) -> NonZeroU64 {
// safe because our initial value is 1 and can only be incremented.
unsafe { NonZeroU64::new_unchecked(self.0.fetch_add(1, Ordering::Relaxed)) }
}
}
/// A small helper for determining the click-count of a mouse-down event.
///
/// Click-count is incremented if both the duration and distance between a pair
/// of clicks are below some threshold.
#[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 {
/// Create a new ClickCounter with the given interval and distance.
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)
}
/// Return the click count for a click occurring now, at the provided position.
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)
}
}