mouse_region.rs

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