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