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