mouse_region.rs

  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}