1use crate::{platform::MouseButton, window::WindowContext, EventContext, TypeTag, ViewContext};
2use collections::HashMap;
3use pathfinder_geometry::rect::RectF;
4use smallvec::SmallVec;
5use std::{any::Any, fmt::Debug, mem::Discriminant, rc::Rc};
6
7use super::{
8 mouse_event::{
9 MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover, MouseMove, MouseUp,
10 MouseUpOut,
11 },
12 MouseClickOut, MouseMoveOut, MouseScrollWheel,
13};
14
15#[derive(Clone)]
16pub struct MouseRegion {
17 pub id: MouseRegionId,
18 pub bounds: RectF,
19 pub handlers: HandlerSet,
20 pub hoverable: bool,
21 pub notify_on_hover: bool,
22 pub notify_on_click: bool,
23}
24
25impl MouseRegion {
26 /// Region ID is used to track semantically equivalent mouse regions across render passes.
27 /// e.g. if you have mouse handlers attached to a list item type, then each item of the list
28 /// should pass a different (consistent) region_id. If you have one big region that covers your
29 /// whole component, just pass the view_id again.
30 pub fn new<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
31 Self::from_handlers(
32 TypeTag::new::<Tag>(),
33 view_id,
34 region_id,
35 bounds,
36 Default::default(),
37 )
38 }
39
40 pub fn handle_all<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
41 Self::from_handlers(
42 TypeTag::new::<Tag>(),
43 view_id,
44 region_id,
45 bounds,
46 HandlerSet::capture_all(),
47 )
48 }
49
50 pub fn from_handlers(
51 tag: TypeTag,
52 view_id: usize,
53 region_id: usize,
54 bounds: RectF,
55 handlers: HandlerSet,
56 ) -> Self {
57 Self {
58 id: MouseRegionId {
59 view_id,
60 tag,
61 region_id,
62 },
63 bounds,
64 handlers,
65 hoverable: true,
66 notify_on_hover: false,
67 notify_on_click: false,
68 }
69 }
70
71 pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
72 where
73 V: 'static,
74 F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
75 {
76 self.handlers = self.handlers.on_down(button, handler);
77 self
78 }
79
80 pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
81 where
82 V: 'static,
83 F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
84 {
85 self.handlers = self.handlers.on_up(button, handler);
86 self
87 }
88
89 pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
90 where
91 V: 'static,
92 F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
93 {
94 self.handlers = self.handlers.on_click(button, handler);
95 self
96 }
97
98 pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
99 where
100 V: 'static,
101 F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
102 {
103 self.handlers = self.handlers.on_click_out(button, handler);
104 self
105 }
106
107 pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
108 where
109 V: 'static,
110 F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
111 {
112 self.handlers = self.handlers.on_down_out(button, handler);
113 self
114 }
115
116 pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
117 where
118 V: 'static,
119 F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
120 {
121 self.handlers = self.handlers.on_up_out(button, handler);
122 self
123 }
124
125 pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
126 where
127 V: 'static,
128 F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
129 {
130 self.handlers = self.handlers.on_drag(button, handler);
131 self
132 }
133
134 pub fn on_hover<V, F>(mut self, handler: F) -> Self
135 where
136 V: 'static,
137 F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
138 {
139 self.handlers = self.handlers.on_hover(handler);
140 self
141 }
142
143 pub fn on_move<V, F>(mut self, handler: F) -> Self
144 where
145 V: 'static,
146 F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
147 {
148 self.handlers = self.handlers.on_move(handler);
149 self
150 }
151
152 pub fn on_move_out<V, F>(mut self, handler: F) -> Self
153 where
154 V: 'static,
155 F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
156 {
157 self.handlers = self.handlers.on_move_out(handler);
158 self
159 }
160
161 pub fn on_scroll<V, F>(mut self, handler: F) -> Self
162 where
163 V: 'static,
164 F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
165 {
166 self.handlers = self.handlers.on_scroll(handler);
167 self
168 }
169
170 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
171 self.hoverable = is_hoverable;
172 self
173 }
174
175 pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
176 self.notify_on_hover = notify;
177 self
178 }
179
180 pub fn with_notify_on_click(mut self, notify: bool) -> Self {
181 self.notify_on_click = notify;
182 self
183 }
184}
185
186#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
187pub struct MouseRegionId {
188 view_id: usize,
189 tag: TypeTag,
190 region_id: usize,
191}
192
193impl MouseRegionId {
194 pub(crate) fn new(tag: TypeTag, view_id: usize, region_id: usize) -> Self {
195 MouseRegionId {
196 view_id,
197 region_id,
198 tag,
199 }
200 }
201
202 pub fn view_id(&self) -> usize {
203 self.view_id
204 }
205
206 #[cfg(debug_assertions)]
207 pub fn tag_type_name(&self) -> &'static str {
208 self.tag.type_name()
209 }
210}
211
212pub type HandlerCallback = Rc<dyn Fn(MouseEvent, &mut dyn Any, &mut WindowContext, usize) -> bool>;
213
214#[derive(Clone, PartialEq, Eq, Hash)]
215pub struct HandlerKey {
216 event_kind: Discriminant<MouseEvent>,
217 button: Option<MouseButton>,
218}
219
220impl HandlerKey {
221 pub fn new(event_kind: Discriminant<MouseEvent>, button: Option<MouseButton>) -> HandlerKey {
222 HandlerKey { event_kind, button }
223 }
224}
225
226#[derive(Clone, Default)]
227pub struct HandlerSet {
228 set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>>,
229}
230
231impl HandlerSet {
232 pub fn capture_all() -> Self {
233 let mut set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>> = HashMap::default();
234
235 set.insert(
236 HandlerKey::new(MouseEvent::move_disc(), None),
237 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
238 );
239 set.insert(
240 HandlerKey::new(MouseEvent::hover_disc(), None),
241 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
242 );
243 for button in MouseButton::all() {
244 set.insert(
245 HandlerKey::new(MouseEvent::drag_disc(), Some(button)),
246 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
247 );
248 set.insert(
249 HandlerKey::new(MouseEvent::down_disc(), Some(button)),
250 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
251 );
252 set.insert(
253 HandlerKey::new(MouseEvent::up_disc(), Some(button)),
254 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
255 );
256 set.insert(
257 HandlerKey::new(MouseEvent::click_disc(), Some(button)),
258 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
259 );
260 set.insert(
261 HandlerKey::new(MouseEvent::click_out_disc(), Some(button)),
262 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
263 );
264 set.insert(
265 HandlerKey::new(MouseEvent::down_out_disc(), Some(button)),
266 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
267 );
268 set.insert(
269 HandlerKey::new(MouseEvent::up_out_disc(), Some(button)),
270 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
271 );
272 }
273 set.insert(
274 HandlerKey::new(MouseEvent::scroll_wheel_disc(), None),
275 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
276 );
277
278 HandlerSet { set }
279 }
280
281 pub fn get(&self, key: &HandlerKey) -> Option<&[HandlerCallback]> {
282 self.set.get(key).map(|vec| vec.as_slice())
283 }
284
285 pub fn contains(
286 &self,
287 discriminant: Discriminant<MouseEvent>,
288 button: Option<MouseButton>,
289 ) -> bool {
290 self.set
291 .contains_key(&HandlerKey::new(discriminant, button))
292 }
293
294 fn insert(
295 &mut self,
296 event_kind: Discriminant<MouseEvent>,
297 button: Option<MouseButton>,
298 callback: HandlerCallback,
299 ) {
300 use std::collections::hash_map::Entry;
301
302 match self.set.entry(HandlerKey::new(event_kind, button)) {
303 Entry::Occupied(mut vec) => {
304 vec.get_mut().push(callback);
305 }
306
307 Entry::Vacant(entry) => {
308 entry.insert(SmallVec::from_buf([callback]));
309 }
310 }
311 }
312
313 pub fn on_move<V, F>(mut self, handler: F) -> Self
314 where
315 V: 'static,
316 F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
317 {
318 self.insert(MouseEvent::move_disc(), None,
319 Rc::new(move |region_event, view, cx, view_id| {
320 if let MouseEvent::Move(e) = region_event {
321 let view = view.downcast_mut().unwrap();
322 let mut cx = ViewContext::mutable(cx, view_id);
323 let mut cx = EventContext::new(&mut cx);
324 handler(e, view, &mut cx);
325 cx.handled
326 } else {
327 panic!(
328 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}",
329 region_event);
330 }
331 }));
332 self
333 }
334
335 pub fn on_move_out<V, F>(mut self, handler: F) -> Self
336 where
337 V: 'static,
338 F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
339 {
340 self.insert(MouseEvent::move_out_disc(), None,
341 Rc::new(move |region_event, view, cx, view_id| {
342 if let MouseEvent::MoveOut(e) = region_event {
343 let view = view.downcast_mut().unwrap();
344 let mut cx = ViewContext::<V>::mutable(cx, view_id);
345 let mut cx = EventContext::new(&mut cx);
346 handler(e, view, &mut cx);
347 cx.handled
348 } else {
349 panic!(
350 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::MoveOut, found {:?}",
351 region_event);
352 }
353 }));
354 self
355 }
356
357 pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
358 where
359 V: 'static,
360 F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
361 {
362 self.insert(MouseEvent::down_disc(), Some(button),
363 Rc::new(move |region_event, view, cx, view_id| {
364 if let MouseEvent::Down(e) = region_event {
365 let view = view.downcast_mut().unwrap();
366 let mut cx = ViewContext::mutable(cx, view_id);
367 let mut cx = EventContext::new(&mut cx);
368 handler(e, view, &mut cx);
369 cx.handled
370 } else {
371 panic!(
372 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Down, found {:?}",
373 region_event);
374 }
375 }));
376 self
377 }
378
379 pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
380 where
381 V: 'static,
382 F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
383 {
384 self.insert(MouseEvent::up_disc(), Some(button),
385 Rc::new(move |region_event, view, cx, view_id| {
386 if let MouseEvent::Up(e) = region_event {
387 let view = view.downcast_mut().unwrap();
388 let mut cx = ViewContext::mutable(cx, view_id);
389 let mut cx = EventContext::new(&mut cx);
390 handler(e, view, &mut cx);
391 cx.handled
392 } else {
393 panic!(
394 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Up, found {:?}",
395 region_event);
396 }
397 }));
398 self
399 }
400
401 pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
402 where
403 V: 'static,
404 F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
405 {
406 self.insert(MouseEvent::click_disc(), Some(button),
407 Rc::new(move |region_event, view, cx, view_id| {
408 if let MouseEvent::Click(e) = region_event {
409 let view = view.downcast_mut().unwrap();
410 let mut cx = ViewContext::mutable(cx, view_id);
411 let mut cx = EventContext::new(&mut cx);
412 handler(e, view, &mut cx);
413 cx.handled
414 } else {
415 panic!(
416 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Click, found {:?}",
417 region_event);
418 }
419 }));
420 self
421 }
422
423 pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
424 where
425 V: 'static,
426 F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
427 {
428 self.insert(MouseEvent::click_out_disc(), Some(button),
429 Rc::new(move |region_event, view, cx, view_id| {
430 if let MouseEvent::ClickOut(e) = region_event {
431 let view = view.downcast_mut().unwrap();
432 let mut cx = ViewContext::mutable(cx, view_id);
433 let mut cx = EventContext::new(&mut cx);
434 handler(e, view, &mut cx);
435 cx.handled
436 } else {
437 panic!(
438 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ClickOut, found {:?}",
439 region_event);
440 }
441 }));
442 self
443 }
444
445 pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
446 where
447 V: 'static,
448 F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
449 {
450 self.insert(MouseEvent::down_out_disc(), Some(button),
451 Rc::new(move |region_event, view, cx, view_id| {
452 if let MouseEvent::DownOut(e) = region_event {
453 let view = view.downcast_mut().unwrap();
454 let mut cx = ViewContext::mutable(cx, view_id);
455 let mut cx = EventContext::new(&mut cx);
456 handler(e, view, &mut cx);
457 cx.handled
458 } else {
459 panic!(
460 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DownOut, found {:?}",
461 region_event);
462 }
463 }));
464 self
465 }
466
467 pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
468 where
469 V: 'static,
470 F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
471 {
472 self.insert(MouseEvent::up_out_disc(), Some(button),
473 Rc::new(move |region_event, view, cx, view_id| {
474 if let MouseEvent::UpOut(e) = region_event {
475 let view = view.downcast_mut().unwrap();
476 let mut cx = ViewContext::mutable(cx, view_id);
477 let mut cx = EventContext::new(&mut cx);
478 handler(e, view, &mut cx);
479 cx.handled
480 } else {
481 panic!(
482 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::UpOut, found {:?}",
483 region_event);
484 }
485 }));
486 self
487 }
488
489 pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
490 where
491 V: 'static,
492 F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
493 {
494 self.insert(MouseEvent::drag_disc(), Some(button),
495 Rc::new(move |region_event, view, cx, view_id| {
496 if let MouseEvent::Drag(e) = region_event {
497 let view = view.downcast_mut().unwrap();
498 let mut cx = ViewContext::mutable(cx, view_id);
499 let mut cx = EventContext::new(&mut cx);
500 handler(e, view, &mut cx);
501 cx.handled
502 } else {
503 panic!(
504 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Drag, found {:?}",
505 region_event);
506 }
507 }));
508 self
509 }
510
511 pub fn on_hover<V, F>(mut self, handler: F) -> Self
512 where
513 V: 'static,
514 F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
515 {
516 self.insert(MouseEvent::hover_disc(), None,
517 Rc::new(move |region_event, view, cx, view_id| {
518 if let MouseEvent::Hover(e) = region_event {
519 let view = view.downcast_mut().unwrap();
520 let mut cx = ViewContext::mutable(cx, view_id);
521 let mut cx = EventContext::new(&mut cx);
522 handler(e, view, &mut cx);
523 cx.handled
524 } else {
525 panic!(
526 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Hover, found {:?}",
527 region_event);
528 }
529 }));
530 self
531 }
532
533 pub fn on_scroll<V, F>(mut self, handler: F) -> Self
534 where
535 V: 'static,
536 F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
537 {
538 self.insert(MouseEvent::scroll_wheel_disc(), None,
539 Rc::new(move |region_event, view, cx, view_id| {
540 if let MouseEvent::ScrollWheel(e) = region_event {
541 let view = view.downcast_mut().unwrap();
542 let mut cx = ViewContext::mutable(cx, view_id);
543 let mut cx = EventContext::new(&mut cx);
544 handler(e, view, &mut cx);
545 cx.handled
546 } else {
547 panic!(
548 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
549 region_event
550 );
551 }
552 }));
553 self
554 }
555}