1use super::sense::Sense;
7use super::state::InputState;
8use super::widget_state::WidgetId;
9use crate::types::WidgetRect;
10
11#[derive(Clone, Debug)]
16pub struct WidgetResponse {
17 pub id: WidgetId,
19 pub rect: WidgetRect,
21 pub sense: Sense,
23
24 pub hovered: bool,
27 pub hover_started: bool,
29 pub hover_ended: bool,
31
32 pub clicked: bool,
35 pub double_clicked: bool,
37 pub triple_clicked: bool,
39 pub right_clicked: bool,
41 pub middle_clicked: bool,
43
44 pub drag_started: bool,
47 pub dragged: bool,
49 pub drag_stopped: bool,
51 pub drag_delta: (f64, f64),
53 pub drag_total: (f64, f64),
55
56 pub has_focus: bool,
59 pub gained_focus: bool,
61 pub lost_focus: bool,
63
64 pub changed: bool,
67
68 pub enabled: bool,
71}
72
73impl Default for WidgetResponse {
74 fn default() -> Self {
75 Self {
76 id: WidgetId::new(""),
77 rect: WidgetRect::default(),
78 sense: Sense::NONE,
79 hovered: false,
80 hover_started: false,
81 hover_ended: false,
82 clicked: false,
83 double_clicked: false,
84 triple_clicked: false,
85 right_clicked: false,
86 middle_clicked: false,
87 drag_started: false,
88 dragged: false,
89 drag_stopped: false,
90 drag_delta: (0.0, 0.0),
91 drag_total: (0.0, 0.0),
92 has_focus: false,
93 gained_focus: false,
94 lost_focus: false,
95 changed: false,
96 enabled: true,
97 }
98 }
99}
100
101impl WidgetResponse {
102 pub fn new(id: WidgetId, rect: WidgetRect, sense: Sense) -> Self {
104 Self {
105 id,
106 rect,
107 sense,
108 ..Default::default()
109 }
110 }
111
112 pub fn with_hover(mut self, hovered: bool) -> Self {
114 self.hovered = hovered;
115 self
116 }
117
118 pub fn with_click(mut self) -> Self {
120 self.clicked = true;
121 self
122 }
123
124 pub fn with_focus(mut self, has_focus: bool) -> Self {
126 self.has_focus = has_focus;
127 self
128 }
129
130 pub fn with_changed(mut self) -> Self {
132 self.changed = true;
133 self
134 }
135
136 pub fn disabled(mut self) -> Self {
138 self.enabled = false;
139 self
140 }
141}
142
143impl WidgetResponse {
145 pub fn any_click(&self) -> bool {
147 self.clicked || self.right_clicked || self.middle_clicked
148 }
149
150 pub fn interacted(&self) -> bool {
152 self.any_click() || self.drag_started || self.gained_focus
153 }
154
155 pub fn is_active(&self) -> bool {
157 self.dragged || self.has_focus
158 }
159
160 pub fn is_pointer_over(&self) -> bool {
162 self.hovered || self.dragged
163 }
164}
165
166impl WidgetResponse {
168 pub fn union(self, other: WidgetResponse) -> WidgetResponse {
172 WidgetResponse {
173 id: self.id,
174 rect: self.rect,
175 sense: self.sense.union(other.sense),
176 hovered: self.hovered || other.hovered,
177 hover_started: self.hover_started || other.hover_started,
178 hover_ended: self.hover_ended || other.hover_ended,
179 clicked: self.clicked || other.clicked,
180 double_clicked: self.double_clicked || other.double_clicked,
181 triple_clicked: self.triple_clicked || other.triple_clicked,
182 right_clicked: self.right_clicked || other.right_clicked,
183 middle_clicked: self.middle_clicked || other.middle_clicked,
184 drag_started: self.drag_started || other.drag_started,
185 dragged: self.dragged || other.dragged,
186 drag_stopped: self.drag_stopped || other.drag_stopped,
187 drag_delta: if self.dragged {
188 self.drag_delta
189 } else {
190 other.drag_delta
191 },
192 drag_total: if self.dragged {
193 self.drag_total
194 } else {
195 other.drag_total
196 },
197 has_focus: self.has_focus || other.has_focus,
198 gained_focus: self.gained_focus || other.gained_focus,
199 lost_focus: self.lost_focus || other.lost_focus,
200 changed: self.changed || other.changed,
201 enabled: self.enabled && other.enabled,
202 }
203 }
204}
205
206impl std::ops::BitOr for WidgetResponse {
207 type Output = WidgetResponse;
208
209 fn bitor(self, rhs: Self) -> Self::Output {
211 self.union(rhs)
212 }
213}
214
215pub fn create_response(
217 id: WidgetId,
218 rect: WidgetRect,
219 sense: Sense,
220 input: &InputState,
221 prev_hovered: bool,
222 prev_focused: bool,
223) -> WidgetResponse {
224 let hovered = if sense.hover {
225 input.is_hovered(&rect)
226 } else {
227 false
228 };
229
230 let hover_started = hovered && !prev_hovered;
231 let hover_ended = !hovered && prev_hovered;
232
233 let clicked = sense.click && hovered && input.is_clicked();
234 let double_clicked = sense.click && hovered && input.is_double_clicked();
235 let right_clicked = sense.click && hovered && input.is_right_clicked();
236 let middle_clicked = sense.click && hovered && input.is_middle_clicked();
237
238 let (dragged, drag_delta, drag_started, drag_stopped) = if sense.drag {
239 let is_dragging = input.is_dragging();
240 let delta = input.drag_delta().unwrap_or((0.0, 0.0));
241 (is_dragging, delta, false, false)
242 } else {
243 (false, (0.0, 0.0), false, false)
244 };
245
246 WidgetResponse {
247 id,
248 rect,
249 sense,
250 hovered,
251 hover_started,
252 hover_ended,
253 clicked,
254 double_clicked,
255 triple_clicked: false,
256 right_clicked,
257 middle_clicked,
258 drag_started,
259 dragged,
260 drag_stopped,
261 drag_delta,
262 drag_total: (0.0, 0.0),
263 has_focus: prev_focused,
264 gained_focus: false,
265 lost_focus: false,
266 changed: false,
267 enabled: true,
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn test_default_response() {
277 let response = WidgetResponse::default();
278 assert!(!response.clicked);
279 assert!(!response.hovered);
280 assert!(response.enabled);
281 assert_eq!(response.sense, Sense::NONE);
282 }
283
284 #[test]
285 fn test_response_new() {
286 let id = WidgetId::new("test_button");
287 let rect = WidgetRect::new(10.0, 10.0, 100.0, 40.0);
288 let response = WidgetResponse::new(id.clone(), rect, Sense::CLICK);
289
290 assert_eq!(response.id, id);
291 assert_eq!(response.rect.x, 10.0);
292 assert_eq!(response.sense, Sense::CLICK);
293 }
294
295 #[test]
296 fn test_builder_methods() {
297 let response = WidgetResponse::new(
298 WidgetId::new("test"),
299 WidgetRect::default(),
300 Sense::CLICK,
301 )
302 .with_hover(true)
303 .with_click()
304 .with_focus(true)
305 .with_changed();
306
307 assert!(response.hovered);
308 assert!(response.clicked);
309 assert!(response.has_focus);
310 assert!(response.changed);
311 }
312
313 #[test]
314 fn test_disabled_builder() {
315 let response = WidgetResponse::new(
316 WidgetId::new("test"),
317 WidgetRect::default(),
318 Sense::CLICK,
319 )
320 .disabled();
321
322 assert!(!response.enabled);
323 }
324
325 #[test]
326 fn test_any_click() {
327 let mut response = WidgetResponse::default();
328 assert!(!response.any_click());
329
330 response.clicked = true;
331 assert!(response.any_click());
332
333 response.clicked = false;
334 response.right_clicked = true;
335 assert!(response.any_click());
336
337 response.right_clicked = false;
338 response.middle_clicked = true;
339 assert!(response.any_click());
340 }
341
342 #[test]
343 fn test_interacted() {
344 let mut response = WidgetResponse::default();
345 assert!(!response.interacted());
346
347 response.clicked = true;
348 assert!(response.interacted());
349
350 response = WidgetResponse::default();
351 response.drag_started = true;
352 assert!(response.interacted());
353
354 response = WidgetResponse::default();
355 response.gained_focus = true;
356 assert!(response.interacted());
357 }
358
359 #[test]
360 fn test_is_active() {
361 let mut response = WidgetResponse::default();
362 assert!(!response.is_active());
363
364 response.dragged = true;
365 assert!(response.is_active());
366
367 response.dragged = false;
368 response.has_focus = true;
369 assert!(response.is_active());
370 }
371
372 #[test]
373 fn test_is_pointer_over() {
374 let mut response = WidgetResponse::default();
375 assert!(!response.is_pointer_over());
376
377 response.hovered = true;
378 assert!(response.is_pointer_over());
379
380 response.hovered = false;
381 response.dragged = true;
382 assert!(response.is_pointer_over());
383 }
384
385 #[test]
386 fn test_response_union() {
387 let response1 = WidgetResponse::new(
388 WidgetId::new("widget1"),
389 WidgetRect::new(0.0, 0.0, 100.0, 100.0),
390 Sense::CLICK,
391 )
392 .with_hover(true);
393
394 let response2 = WidgetResponse::new(
395 WidgetId::new("widget2"),
396 WidgetRect::new(50.0, 50.0, 100.0, 100.0),
397 Sense::DRAG,
398 )
399 .with_click();
400
401 let combined = response1.clone().union(response2);
402
403 assert_eq!(combined.id, response1.id);
404 assert_eq!(combined.rect.x, response1.rect.x);
405 assert!(combined.hovered);
406 assert!(combined.clicked);
407 assert!(combined.sense.hover);
408 assert!(combined.sense.click);
409 assert!(combined.sense.drag);
410 }
411
412 #[test]
413 fn test_response_bitor_operator() {
414 let response1 = WidgetResponse::default().with_hover(true);
415 let response2 = WidgetResponse::default().with_click();
416
417 let combined = response1 | response2;
418
419 assert!(combined.hovered);
420 assert!(combined.clicked);
421 }
422
423 #[test]
424 fn test_union_preserves_drag_data() {
425 let mut response1 = WidgetResponse::default();
426 response1.dragged = true;
427 response1.drag_delta = (10.0, 20.0);
428 response1.drag_total = (30.0, 40.0);
429
430 let mut response2 = WidgetResponse::default();
431 response2.drag_delta = (5.0, 5.0);
432 response2.drag_total = (15.0, 15.0);
433
434 let combined = response1.clone().union(response2);
435
436 assert!(combined.dragged);
437 assert_eq!(combined.drag_delta, (10.0, 20.0));
438 assert_eq!(combined.drag_total, (30.0, 40.0));
439 }
440
441 #[test]
442 fn test_union_enabled_state() {
443 let response1 = WidgetResponse::default();
444 let response2 = WidgetResponse::default().disabled();
445
446 let combined = response1.union(response2);
447 assert!(!combined.enabled);
448 }
449
450 #[test]
451 fn test_create_response_hover() {
452 let mut input = InputState::new();
453 input.pointer.pos = Some((50.0, 50.0));
454
455 let rect = WidgetRect::new(10.0, 10.0, 100.0, 100.0);
456 let response = create_response(
457 WidgetId::new("test"),
458 rect,
459 Sense::HOVER,
460 &input,
461 false,
462 false,
463 );
464
465 assert!(response.hovered);
466 assert!(response.hover_started);
467 }
468
469 #[test]
470 fn test_create_response_hover_ended() {
471 let mut input = InputState::new();
472 input.pointer.pos = Some((5.0, 5.0));
473
474 let rect = WidgetRect::new(10.0, 10.0, 100.0, 100.0);
475 let response = create_response(
476 WidgetId::new("test"),
477 rect,
478 Sense::HOVER,
479 &input,
480 true,
481 false,
482 );
483
484 assert!(!response.hovered);
485 assert!(response.hover_ended);
486 }
487
488 #[test]
489 fn test_create_response_click() {
490 let mut input = InputState::new();
491 input.pointer.pos = Some((50.0, 50.0));
492 input.pointer.clicked = Some(crate::input::state::MouseButton::Left);
493
494 let rect = WidgetRect::new(10.0, 10.0, 100.0, 100.0);
495 let response = create_response(
496 WidgetId::new("test"),
497 rect,
498 Sense::CLICK,
499 &input,
500 false,
501 false,
502 );
503
504 assert!(response.hovered);
505 assert!(response.clicked);
506 }
507
508 #[test]
509 fn test_create_response_no_sense() {
510 let mut input = InputState::new();
511 input.pointer.pos = Some((50.0, 50.0));
512 input.pointer.clicked = Some(crate::input::state::MouseButton::Left);
513
514 let rect = WidgetRect::new(10.0, 10.0, 100.0, 100.0);
515 let response = create_response(
516 WidgetId::new("test"),
517 rect,
518 Sense::NONE,
519 &input,
520 false,
521 false,
522 );
523
524 assert!(!response.hovered);
525 assert!(!response.clicked);
526 }
527
528 #[test]
529 fn test_create_response_right_click() {
530 let mut input = InputState::new();
531 input.pointer.pos = Some((50.0, 50.0));
532 input.pointer.clicked = Some(crate::input::state::MouseButton::Right);
533
534 let rect = WidgetRect::new(10.0, 10.0, 100.0, 100.0);
535 let response = create_response(
536 WidgetId::new("test"),
537 rect,
538 Sense::CLICK,
539 &input,
540 false,
541 false,
542 );
543
544 assert!(response.right_clicked);
545 assert!(!response.clicked);
546 }
547}