mouse_region.rs

  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}