1use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
7#[serde(rename_all = "snake_case")]
8pub enum Button {
9 Primary,
10 Secondary,
11 Middle,
12 Extra1,
13 Extra2,
14}
15
16impl Button {
17 pub fn egui(self) -> egui::PointerButton {
18 match self {
19 Button::Primary => egui::PointerButton::Primary,
20 Button::Secondary => egui::PointerButton::Secondary,
21 Button::Middle => egui::PointerButton::Middle,
22 Button::Extra1 => egui::PointerButton::Extra1,
23 Button::Extra2 => egui::PointerButton::Extra2,
24 }
25 }
26 fn index(self) -> usize {
27 self as usize
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
32#[serde(rename_all = "snake_case")]
33pub enum WheelUnit {
34 Point,
35 Line,
36 Page,
37}
38
39#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
40#[serde(deny_unknown_fields)]
41pub struct Modifiers {
42 #[serde(default)]
43 pub ctrl: bool,
44 #[serde(default)]
45 pub shift: bool,
46 #[serde(default)]
47 pub alt: bool,
48 #[serde(default)]
50 pub command: bool,
51}
52
53impl Modifiers {
54 pub fn egui(self) -> egui::Modifiers {
55 egui::Modifiers {
56 alt: self.alt,
57 ctrl: self.ctrl || self.command,
58 shift: self.shift,
59 mac_cmd: self.command,
60 command: self.ctrl || self.command,
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, schemars::JsonSchema)]
67pub struct PointerState {
68 pub pos: Option<[f32; 2]>,
70 pub buttons: Vec<Button>,
71 pub modifiers: Modifiers,
72}
73
74pub const MULTI_CLICK_WINDOW: f64 = 0.3;
80
81pub const CLICK_DIST: f32 = 6.0;
84pub const CLICK_DURATION: f64 = 0.8;
86
87#[derive(Debug, Clone, Default)]
88pub struct Pointer {
89 pub pos: Option<egui::Pos2>,
90 pub buttons: [bool; 5],
91 pub modifiers: Modifiers,
92 pub now: f64,
96 pub reset_clicks: bool,
99 released: Option<f64>,
102 press: Option<(egui::Pos2, f64)>,
105 moved_far: bool,
106}
107
108const ALL_BUTTONS: [Button; 5] = [Button::Primary, Button::Secondary, Button::Middle, Button::Extra1, Button::Extra2];
109
110impl Pointer {
111 pub fn state(&self) -> PointerState {
112 PointerState {
113 pos: self.pos.map(|p| [p.x, p.y]),
114 buttons: ALL_BUTTONS.iter().copied().filter(|b| self.buttons[b.index()]).collect(),
115 modifiers: self.modifiers,
116 }
117 }
118
119 pub fn moved(&mut self, x: f32, y: f32, out: &mut Vec<egui::Event>) {
120 let pos = egui::pos2(x, y);
121 self.pos = Some(pos);
122 if let Some((origin, _)) = self.press {
123 self.moved_far |= origin.distance(pos) > CLICK_DIST;
124 }
125 out.push(egui::Event::PointerMoved(pos));
126 }
127
128 pub fn button(&mut self, button: Button, pressed: bool, out: &mut Vec<egui::Event>) -> Result<(), String> {
129 let pos = self.pos.ok_or("the pointer has no position yet: move it first")?;
130 self.buttons[button.index()] = pressed;
131 let now = self.now;
132 if pressed {
133 self.press = Some((pos, now));
134 self.moved_far = false;
135 } else if let Some((_, down)) = self.press.take() {
136 if !self.moved_far && now - down <= CLICK_DURATION {
137 self.released = Some(now);
138 }
139 }
140 out.push(egui::Event::PointerButton { pos, button: button.egui(), pressed, modifiers: self.modifiers.egui() });
141 Ok(())
142 }
143
144 pub fn click_gap(&mut self) -> bool {
164 let recent = self.released.is_some_and(|t| self.now - t < 2.0 * MULTI_CLICK_WINDOW);
165 if !recent || self.buttons.iter().any(|&b| b) {
166 return false;
167 }
168 self.released = None;
169 self.reset_clicks = true;
170 true
171 }
172
173 pub fn gone(&mut self, out: &mut Vec<egui::Event>) {
174 self.pos = None;
175 self.buttons = [false; 5];
176 out.push(egui::Event::PointerGone);
177 }
178
179 pub fn wheel(&mut self, dx: f32, dy: f32, unit: WheelUnit, out: &mut Vec<egui::Event>) {
180 let unit = match unit {
181 WheelUnit::Point => egui::MouseWheelUnit::Point,
182 WheelUnit::Line => egui::MouseWheelUnit::Line,
183 WheelUnit::Page => egui::MouseWheelUnit::Page,
184 };
185 out.push(egui::Event::MouseWheel {
186 unit,
187 delta: egui::vec2(dx, dy),
188 phase: egui::TouchPhase::Move,
189 modifiers: self.modifiers.egui(),
190 });
191 }
192
193 pub fn set_modifiers(&mut self, m: Modifiers) {
194 self.modifiers = m;
195 }
196
197 pub fn key(&mut self, name: &str, pressed: bool, repeat: bool, out: &mut Vec<egui::Event>) -> Result<(), String> {
198 let key = egui::Key::from_name(name).ok_or_else(|| format!("unknown key `{name}` (egui key names: A..Z, 0..9, Enter, Escape, Tab, Space, Backspace, Delete, ArrowLeft/Right/Up/Down, Home, End, PageUp/Down, F1..F12, Minus, Plus, …)"))?;
199 out.push(egui::Event::Key { key, physical_key: None, pressed, repeat, modifiers: self.modifiers.egui() });
200 Ok(())
201 }
202
203 pub fn text(&mut self, text: &str, out: &mut Vec<egui::Event>) {
204 if !text.is_empty() {
205 out.push(egui::Event::Text(text.to_string()));
206 }
207 }
208}