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    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}