1pub use crate::types::state::WidgetId;
6
7#[derive(Clone, Debug, Default)]
9pub struct FocusState {
10 pub focused: Option<WidgetId>,
12 pub pending_focus: Option<WidgetId>,
14}
15
16impl FocusState {
17 pub fn new() -> Self {
18 Self::default()
19 }
20
21 pub fn set_focus(&mut self, id: WidgetId) {
23 self.focused = Some(id);
24 }
25
26 pub fn clear_focus(&mut self) {
28 self.focused = None;
29 }
30
31 pub fn is_focused(&self, id: &WidgetId) -> bool {
33 self.focused.as_ref() == Some(id)
34 }
35
36 pub fn request_focus(&mut self, id: WidgetId) {
38 self.pending_focus = Some(id);
39 }
40
41 pub fn process_pending(&mut self) {
43 if let Some(id) = self.pending_focus.take() {
44 self.focused = Some(id);
45 }
46 }
47}
48
49#[derive(Clone, Debug, Default)]
51pub struct HoverState {
52 pub hovered: Option<WidgetId>,
54 pub mouse_pos: (f64, f64),
56 pub mouse_pressed: bool,
58}
59
60impl HoverState {
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 pub fn update_mouse(&mut self, x: f64, y: f64) {
67 self.mouse_pos = (x, y);
68 }
69
70 pub fn set_hovered(&mut self, id: Option<WidgetId>) {
72 self.hovered = id;
73 }
74
75 pub fn is_hovered(&self, id: &WidgetId) -> bool {
77 self.hovered.as_ref() == Some(id)
78 }
79
80 pub fn set_pressed(&mut self, pressed: bool) {
82 self.mouse_pressed = pressed;
83 }
84}
85
86#[derive(Clone, Debug, Default)]
88pub struct DragState {
89 pub dragging: Option<WidgetId>,
91 pub start_pos: (f64, f64),
93 pub current_pos: (f64, f64),
95 pub offset: (f64, f64),
97 pub initial_value: f64,
99}
100
101impl DragState {
102 pub fn new() -> Self {
103 Self::default()
104 }
105
106 pub fn start(&mut self, id: WidgetId, x: f64, y: f64, offset_x: f64, offset_y: f64) {
108 self.dragging = Some(id);
109 self.start_pos = (x, y);
110 self.current_pos = (x, y);
111 self.offset = (offset_x, offset_y);
112 }
113
114 pub fn start_with_value(&mut self, id: WidgetId, x: f64, y: f64, value: f64) {
116 self.dragging = Some(id);
117 self.start_pos = (x, y);
118 self.current_pos = (x, y);
119 self.offset = (0.0, 0.0);
120 self.initial_value = value;
121 }
122
123 pub fn update(&mut self, x: f64, y: f64) {
125 self.current_pos = (x, y);
126 }
127
128 pub fn end(&mut self) {
130 self.dragging = None;
131 }
132
133 pub fn is_dragging(&self, id: &WidgetId) -> bool {
135 self.dragging.as_ref() == Some(id)
136 }
137
138 pub fn delta(&self) -> (f64, f64) {
140 (
141 self.current_pos.0 - self.start_pos.0,
142 self.current_pos.1 - self.start_pos.1,
143 )
144 }
145
146 pub fn delta_from(&self, last_pos: (f64, f64)) -> (f64, f64) {
148 (
149 self.current_pos.0 - last_pos.0,
150 self.current_pos.1 - last_pos.1,
151 )
152 }
153}
154
155#[derive(Clone, Copy, Debug, PartialEq, Eq)]
157#[derive(Default)]
158pub enum WidgetInteraction {
159 #[default]
160 None,
161 Hover,
162 Press,
163 Drag,
164 Click,
165 DoubleClick,
166 TripleClick,
167 Focus,
168}
169
170
171#[derive(Clone, Debug, Default)]
173pub struct WidgetInputState {
174 pub focus: FocusState,
176 pub hover: HoverState,
178 pub drag: DragState,
180 pub active: Option<WidgetId>,
182 pub last_click_time: f64,
184 pub last_click_pos: (f64, f64),
186 pub last_click_widget: Option<WidgetId>,
188 pub double_click_threshold_ms: f64,
190 pub double_click_distance: f64,
192 pub click_count: u8,
194 pub triple_click_threshold_ms: f64,
196}
197
198impl WidgetInputState {
199 pub fn new() -> Self {
200 Self {
201 double_click_threshold_ms: 500.0,
202 double_click_distance: 5.0,
203 click_count: 0,
204 triple_click_threshold_ms: 300.0,
205 ..Default::default()
206 }
207 }
208
209 pub fn update_mouse(&mut self, x: f64, y: f64) {
211 self.hover.update_mouse(x, y);
212 if self.drag.dragging.is_some() {
213 self.drag.update(x, y);
214 }
215 }
216
217 pub fn mouse_press(&mut self, _x: f64, _y: f64, widget_id: Option<WidgetId>) {
219 self.hover.set_pressed(true);
220 self.active = widget_id;
221 }
222
223 pub fn mouse_release(&mut self, x: f64, y: f64, now: f64) -> WidgetInteraction {
225 self.hover.set_pressed(false);
226
227 let was_dragging = self.drag.dragging.is_some();
228 self.drag.end();
229
230 if was_dragging {
231 self.active = None;
232 return WidgetInteraction::None;
233 }
234
235 if let Some(ref active_id) = self.active {
236 let is_same_widget = self.last_click_widget.as_ref() == Some(active_id);
237 let time_since_last = now - self.last_click_time;
238 let dist = ((x - self.last_click_pos.0).powi(2) + (y - self.last_click_pos.1).powi(2)).sqrt();
239 let dist_ok = dist < self.double_click_distance;
240
241 let interaction = if is_same_widget && dist_ok {
242 if time_since_last < self.triple_click_threshold_ms && self.click_count == 2 {
243 self.click_count = 3;
244 WidgetInteraction::TripleClick
245 } else if time_since_last < self.double_click_threshold_ms && self.click_count == 1 {
246 self.click_count = 2;
247 WidgetInteraction::DoubleClick
248 } else {
249 self.click_count = 1;
250 WidgetInteraction::Click
251 }
252 } else {
253 self.click_count = 1;
254 WidgetInteraction::Click
255 };
256
257 self.last_click_time = now;
258 self.last_click_pos = (x, y);
259 self.last_click_widget = Some(active_id.clone());
260
261 self.active = None;
262 return interaction;
263 }
264
265 self.active = None;
266 WidgetInteraction::None
267 }
268
269 pub fn start_drag(&mut self, id: WidgetId, x: f64, y: f64) {
271 self.drag.start(id, x, y, 0.0, 0.0);
272 }
273
274 pub fn start_drag_with_value(&mut self, id: WidgetId, x: f64, y: f64, value: f64) {
276 self.drag.start_with_value(id, x, y, value);
277 }
278
279 pub fn end_frame(&mut self) {
281 self.focus.process_pending();
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_widget_id() {
291 let id1 = WidgetId::new("button1");
292 let id2: WidgetId = "button2".into();
293 assert_ne!(id1, id2);
294 }
295
296 #[test]
297 fn test_focus_state() {
298 let mut focus = FocusState::new();
299 let id = WidgetId::new("input1");
300
301 assert!(!focus.is_focused(&id));
302
303 focus.set_focus(id.clone());
304 assert!(focus.is_focused(&id));
305
306 focus.clear_focus();
307 assert!(!focus.is_focused(&id));
308 }
309
310 #[test]
311 fn test_hover_state() {
312 let mut hover = HoverState::new();
313 let id = WidgetId::new("button1");
314
315 hover.update_mouse(100.0, 50.0);
316 assert_eq!(hover.mouse_pos, (100.0, 50.0));
317
318 hover.set_hovered(Some(id.clone()));
319 assert!(hover.is_hovered(&id));
320
321 hover.set_hovered(None);
322 assert!(!hover.is_hovered(&id));
323 }
324
325 #[test]
326 fn test_drag_state() {
327 let mut drag = DragState::new();
328 let id = WidgetId::new("slider1");
329
330 drag.start(id.clone(), 100.0, 50.0, 5.0, 0.0);
331 assert!(drag.is_dragging(&id));
332
333 drag.update(150.0, 60.0);
334 assert_eq!(drag.delta(), (50.0, 10.0));
335
336 drag.end();
337 assert!(!drag.is_dragging(&id));
338 }
339
340 #[test]
341 fn test_click_detection() {
342 let mut state = WidgetInputState::new();
343 let id = WidgetId::new("button1");
344
345 state.mouse_press(100.0, 50.0, Some(id.clone()));
346 let interaction = state.mouse_release(100.0, 50.0, 1000.0);
347 assert_eq!(interaction, WidgetInteraction::Click);
348 }
349
350 #[test]
351 fn test_double_click_detection() {
352 let mut state = WidgetInputState::new();
353 let id = WidgetId::new("button1");
354
355 state.mouse_press(100.0, 50.0, Some(id.clone()));
356 state.mouse_release(100.0, 50.0, 1000.0);
357
358 state.mouse_press(101.0, 51.0, Some(id.clone()));
359 let interaction = state.mouse_release(101.0, 51.0, 1200.0);
360 assert_eq!(interaction, WidgetInteraction::DoubleClick);
361 }
362
363 #[test]
364 fn test_triple_click_detection() {
365 let mut state = WidgetInputState::new();
366 let id = WidgetId::new("button1");
367
368 state.mouse_press(100.0, 50.0, Some(id.clone()));
369 let interaction1 = state.mouse_release(100.0, 50.0, 1000.0);
370 assert_eq!(interaction1, WidgetInteraction::Click);
371
372 state.mouse_press(101.0, 51.0, Some(id.clone()));
373 let interaction2 = state.mouse_release(101.0, 51.0, 1200.0);
374 assert_eq!(interaction2, WidgetInteraction::DoubleClick);
375
376 state.mouse_press(100.0, 50.0, Some(id.clone()));
377 let interaction3 = state.mouse_release(100.0, 50.0, 1400.0);
378 assert_eq!(interaction3, WidgetInteraction::TripleClick);
379 }
380
381 #[test]
382 fn test_triple_click_timeout() {
383 let mut state = WidgetInputState::new();
384 let id = WidgetId::new("button1");
385
386 state.mouse_press(100.0, 50.0, Some(id.clone()));
387 state.mouse_release(100.0, 50.0, 1000.0);
388
389 state.mouse_press(101.0, 51.0, Some(id.clone()));
390 state.mouse_release(101.0, 51.0, 1200.0);
391
392 state.mouse_press(100.0, 50.0, Some(id.clone()));
393 let interaction = state.mouse_release(100.0, 50.0, 2000.0);
394 assert_eq!(interaction, WidgetInteraction::Click);
395 }
396
397 #[test]
398 fn test_triple_click_different_widget() {
399 let mut state = WidgetInputState::new();
400 let id1 = WidgetId::new("button1");
401 let id2 = WidgetId::new("button2");
402
403 state.mouse_press(100.0, 50.0, Some(id1.clone()));
404 state.mouse_release(100.0, 50.0, 1000.0);
405
406 state.mouse_press(101.0, 51.0, Some(id1.clone()));
407 state.mouse_release(101.0, 51.0, 1200.0);
408
409 state.mouse_press(200.0, 50.0, Some(id2.clone()));
410 let interaction = state.mouse_release(200.0, 50.0, 1400.0);
411 assert_eq!(interaction, WidgetInteraction::Click);
412 }
413
414 #[test]
415 fn test_triple_click_too_far() {
416 let mut state = WidgetInputState::new();
417 let id = WidgetId::new("button1");
418
419 state.mouse_press(100.0, 50.0, Some(id.clone()));
420 state.mouse_release(100.0, 50.0, 1000.0);
421
422 state.mouse_press(101.0, 51.0, Some(id.clone()));
423 state.mouse_release(101.0, 51.0, 1200.0);
424
425 state.mouse_press(200.0, 50.0, Some(id.clone()));
426 let interaction = state.mouse_release(200.0, 50.0, 1400.0);
427 assert_eq!(interaction, WidgetInteraction::Click);
428 }
429
430 #[test]
431 fn test_click_count_reset_after_triple() {
432 let mut state = WidgetInputState::new();
433 let id = WidgetId::new("button1");
434
435 state.mouse_press(100.0, 50.0, Some(id.clone()));
436 state.mouse_release(100.0, 50.0, 1000.0);
437 state.mouse_press(100.0, 50.0, Some(id.clone()));
438 state.mouse_release(100.0, 50.0, 1200.0);
439 state.mouse_press(100.0, 50.0, Some(id.clone()));
440 let interaction = state.mouse_release(100.0, 50.0, 1400.0);
441 assert_eq!(interaction, WidgetInteraction::TripleClick);
442
443 state.mouse_press(100.0, 50.0, Some(id.clone()));
444 let interaction = state.mouse_release(100.0, 50.0, 1600.0);
445 assert_eq!(interaction, WidgetInteraction::Click);
446 }
447}