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 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_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
93 where
94 V: View,
95 F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
96 {
97 self.handlers = self.handlers.on_down_out(button, handler);
98 self
99 }
100
101 pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
102 where
103 V: View,
104 F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
105 {
106 self.handlers = self.handlers.on_up_out(button, handler);
107 self
108 }
109
110 pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
111 where
112 V: View,
113 F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
114 {
115 self.handlers = self.handlers.on_drag(button, handler);
116 self
117 }
118
119 pub fn on_hover<V, F>(mut self, handler: F) -> Self
120 where
121 V: View,
122 F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
123 {
124 self.handlers = self.handlers.on_hover(handler);
125 self
126 }
127
128 pub fn on_move<V, F>(mut self, handler: F) -> Self
129 where
130 V: View,
131 F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
132 {
133 self.handlers = self.handlers.on_move(handler);
134 self
135 }
136
137 pub fn on_move_out<V, F>(mut self, handler: F) -> Self
138 where
139 V: View,
140 F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
141 {
142 self.handlers = self.handlers.on_move_out(handler);
143 self
144 }
145
146 pub fn on_scroll<V, F>(mut self, handler: F) -> Self
147 where
148 V: View,
149 F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
150 {
151 self.handlers = self.handlers.on_scroll(handler);
152 self
153 }
154
155 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
156 self.hoverable = is_hoverable;
157 self
158 }
159
160 pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
161 self.notify_on_hover = notify;
162 self
163 }
164
165 pub fn with_notify_on_click(mut self, notify: bool) -> Self {
166 self.notify_on_click = notify;
167 self
168 }
169}
170
171#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
172pub struct MouseRegionId {
173 view_id: usize,
174 tag: TypeId,
175 region_id: usize,
176 #[cfg(debug_assertions)]
177 tag_type_name: &'static str,
178}
179
180impl MouseRegionId {
181 pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
182 MouseRegionId {
183 view_id,
184 region_id,
185 tag: TypeId::of::<Tag>(),
186 #[cfg(debug_assertions)]
187 tag_type_name: std::any::type_name::<Tag>(),
188 }
189 }
190
191 pub fn view_id(&self) -> usize {
192 self.view_id
193 }
194
195 #[cfg(debug_assertions)]
196 pub fn tag_type_name(&self) -> &'static str {
197 self.tag_type_name
198 }
199}
200
201pub type HandlerCallback = Rc<dyn Fn(MouseEvent, &mut dyn Any, &mut WindowContext, usize) -> bool>;
202
203#[derive(Clone, PartialEq, Eq, Hash)]
204pub struct HandlerKey {
205 event_kind: Discriminant<MouseEvent>,
206 button: Option<MouseButton>,
207}
208
209impl HandlerKey {
210 pub fn new(event_kind: Discriminant<MouseEvent>, button: Option<MouseButton>) -> HandlerKey {
211 HandlerKey { event_kind, button }
212 }
213}
214
215#[derive(Clone, Default)]
216pub struct HandlerSet {
217 set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>>,
218}
219
220impl HandlerSet {
221 pub fn capture_all() -> Self {
222 let mut set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>> = HashMap::default();
223
224 set.insert(
225 HandlerKey::new(MouseEvent::move_disc(), None),
226 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
227 );
228 set.insert(
229 HandlerKey::new(MouseEvent::hover_disc(), None),
230 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
231 );
232 for button in MouseButton::all() {
233 set.insert(
234 HandlerKey::new(MouseEvent::drag_disc(), Some(button)),
235 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
236 );
237 set.insert(
238 HandlerKey::new(MouseEvent::down_disc(), Some(button)),
239 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
240 );
241 set.insert(
242 HandlerKey::new(MouseEvent::up_disc(), Some(button)),
243 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
244 );
245 set.insert(
246 HandlerKey::new(MouseEvent::click_disc(), Some(button)),
247 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
248 );
249 set.insert(
250 HandlerKey::new(MouseEvent::down_out_disc(), Some(button)),
251 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
252 );
253 set.insert(
254 HandlerKey::new(MouseEvent::up_out_disc(), Some(button)),
255 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
256 );
257 }
258 set.insert(
259 HandlerKey::new(MouseEvent::scroll_wheel_disc(), None),
260 SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
261 );
262
263 HandlerSet { set }
264 }
265
266 pub fn get(&self, key: &HandlerKey) -> Option<&[HandlerCallback]> {
267 self.set.get(key).map(|vec| vec.as_slice())
268 }
269
270 pub fn contains(
271 &self,
272 discriminant: Discriminant<MouseEvent>,
273 button: Option<MouseButton>,
274 ) -> bool {
275 self.set
276 .contains_key(&HandlerKey::new(discriminant, button))
277 }
278
279 fn insert(
280 &mut self,
281 event_kind: Discriminant<MouseEvent>,
282 button: Option<MouseButton>,
283 callback: HandlerCallback,
284 ) {
285 use std::collections::hash_map::Entry;
286
287 match self.set.entry(HandlerKey::new(event_kind, button)) {
288 Entry::Occupied(mut vec) => {
289 vec.get_mut().push(callback);
290 }
291
292 Entry::Vacant(entry) => {
293 entry.insert(SmallVec::from_buf([callback]));
294 }
295 }
296 }
297
298 pub fn on_move<V, F>(mut self, handler: F) -> Self
299 where
300 V: View,
301 F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
302 {
303 self.insert(MouseEvent::move_disc(), None,
304 Rc::new(move |region_event, view, cx, view_id| {
305 if let MouseEvent::Move(e) = region_event {
306 let view = view.downcast_mut().unwrap();
307 let mut cx = ViewContext::mutable(cx, view_id);
308 let mut cx = EventContext::new(&mut cx);
309 handler(e, view, &mut cx);
310 cx.handled
311 } else {
312 panic!(
313 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}",
314 region_event);
315 }
316 }));
317 self
318 }
319
320 pub fn on_move_out<V, F>(mut self, handler: F) -> Self
321 where
322 V: View,
323 F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
324 {
325 self.insert(MouseEvent::move_out_disc(), None,
326 Rc::new(move |region_event, view, cx, view_id| {
327 if let MouseEvent::MoveOut(e) = region_event {
328 let view = view.downcast_mut().unwrap();
329 let mut cx = ViewContext::<V>::mutable(cx, view_id);
330 let mut cx = EventContext::new(&mut cx);
331 handler(e, view, &mut cx);
332 cx.handled
333 } else {
334 panic!(
335 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::MoveOut, found {:?}",
336 region_event);
337 }
338 }));
339 self
340 }
341
342 pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
343 where
344 V: View,
345 F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
346 {
347 self.insert(MouseEvent::down_disc(), Some(button),
348 Rc::new(move |region_event, view, cx, view_id| {
349 if let MouseEvent::Down(e) = region_event {
350 let view = view.downcast_mut().unwrap();
351 let mut cx = ViewContext::mutable(cx, view_id);
352 let mut cx = EventContext::new(&mut cx);
353 handler(e, view, &mut cx);
354 cx.handled
355 } else {
356 panic!(
357 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Down, found {:?}",
358 region_event);
359 }
360 }));
361 self
362 }
363
364 pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
365 where
366 V: View,
367 F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
368 {
369 self.insert(MouseEvent::up_disc(), Some(button),
370 Rc::new(move |region_event, view, cx, view_id| {
371 if let MouseEvent::Up(e) = region_event {
372 let view = view.downcast_mut().unwrap();
373 let mut cx = ViewContext::mutable(cx, view_id);
374 let mut cx = EventContext::new(&mut cx);
375 handler(e, view, &mut cx);
376 cx.handled
377 } else {
378 panic!(
379 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Up, found {:?}",
380 region_event);
381 }
382 }));
383 self
384 }
385
386 pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
387 where
388 V: View,
389 F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
390 {
391 self.insert(MouseEvent::click_disc(), Some(button),
392 Rc::new(move |region_event, view, cx, view_id| {
393 if let MouseEvent::Click(e) = region_event {
394 let view = view.downcast_mut().unwrap();
395 let mut cx = ViewContext::mutable(cx, view_id);
396 let mut cx = EventContext::new(&mut cx);
397 handler(e, view, &mut cx);
398 cx.handled
399 } else {
400 panic!(
401 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Click, found {:?}",
402 region_event);
403 }
404 }));
405 self
406 }
407
408 pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
409 where
410 V: View,
411 F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
412 {
413 self.insert(MouseEvent::down_out_disc(), Some(button),
414 Rc::new(move |region_event, view, cx, view_id| {
415 if let MouseEvent::DownOut(e) = region_event {
416 let view = view.downcast_mut().unwrap();
417 let mut cx = ViewContext::mutable(cx, view_id);
418 let mut cx = EventContext::new(&mut cx);
419 handler(e, view, &mut cx);
420 cx.handled
421 } else {
422 panic!(
423 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DownOut, found {:?}",
424 region_event);
425 }
426 }));
427 self
428 }
429
430 pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
431 where
432 V: View,
433 F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
434 {
435 self.insert(MouseEvent::up_out_disc(), Some(button),
436 Rc::new(move |region_event, view, cx, view_id| {
437 if let MouseEvent::UpOut(e) = region_event {
438 let view = view.downcast_mut().unwrap();
439 let mut cx = ViewContext::mutable(cx, view_id);
440 let mut cx = EventContext::new(&mut cx);
441 handler(e, view, &mut cx);
442 cx.handled
443 } else {
444 panic!(
445 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::UpOut, found {:?}",
446 region_event);
447 }
448 }));
449 self
450 }
451
452 pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
453 where
454 V: View,
455 F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
456 {
457 self.insert(MouseEvent::drag_disc(), Some(button),
458 Rc::new(move |region_event, view, cx, view_id| {
459 if let MouseEvent::Drag(e) = region_event {
460 let view = view.downcast_mut().unwrap();
461 let mut cx = ViewContext::mutable(cx, view_id);
462 let mut cx = EventContext::new(&mut cx);
463 handler(e, view, &mut cx);
464 cx.handled
465 } else {
466 panic!(
467 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Drag, found {:?}",
468 region_event);
469 }
470 }));
471 self
472 }
473
474 pub fn on_hover<V, F>(mut self, handler: F) -> Self
475 where
476 V: View,
477 F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
478 {
479 self.insert(MouseEvent::hover_disc(), None,
480 Rc::new(move |region_event, view, cx, view_id| {
481 if let MouseEvent::Hover(e) = region_event {
482 let view = view.downcast_mut().unwrap();
483 let mut cx = ViewContext::mutable(cx, view_id);
484 let mut cx = EventContext::new(&mut cx);
485 handler(e, view, &mut cx);
486 cx.handled
487 } else {
488 panic!(
489 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Hover, found {:?}",
490 region_event);
491 }
492 }));
493 self
494 }
495
496 pub fn on_scroll<V, F>(mut self, handler: F) -> Self
497 where
498 V: View,
499 F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
500 {
501 self.insert(MouseEvent::scroll_wheel_disc(), None,
502 Rc::new(move |region_event, view, cx, view_id| {
503 if let MouseEvent::ScrollWheel(e) = region_event {
504 let view = view.downcast_mut().unwrap();
505 let mut cx = ViewContext::mutable(cx, view_id);
506 let mut cx = EventContext::new(&mut cx);
507 handler(e, view, &mut cx);
508 cx.handled
509 } else {
510 panic!(
511 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
512 region_event
513 );
514 }
515 }));
516 self
517 }
518}