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    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_scroll(
128        mut self,
129        handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static,
130    ) -> Self {
131        self.handlers = self.handlers.on_scroll(handler);
132        self
133    }
134
135    pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
136        self.hoverable = is_hoverable;
137        self
138    }
139
140    pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
141        self.notify_on_hover = notify;
142        self
143    }
144
145    pub fn with_notify_on_click(mut self, notify: bool) -> Self {
146        self.notify_on_click = notify;
147        self
148    }
149}
150
151#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
152pub struct MouseRegionId {
153    view_id: usize,
154    tag: TypeId,
155    region_id: usize,
156    #[cfg(debug_assertions)]
157    tag_type_name: &'static str,
158}
159
160impl MouseRegionId {
161    pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
162        MouseRegionId {
163            view_id,
164            region_id,
165            tag: TypeId::of::<Tag>(),
166            #[cfg(debug_assertions)]
167            tag_type_name: std::any::type_name::<Tag>(),
168        }
169    }
170
171    pub fn view_id(&self) -> usize {
172        self.view_id
173    }
174
175    #[cfg(debug_assertions)]
176    pub fn tag_type_name(&self) -> &'static str {
177        self.tag_type_name
178    }
179}
180
181pub type HandlerCallback = Rc<dyn Fn(MouseEvent, &mut EventContext)>;
182
183#[derive(Clone, PartialEq, Eq, Hash)]
184pub struct HandlerKey {
185    event_kind: Discriminant<MouseEvent>,
186    button: Option<MouseButton>,
187}
188
189impl HandlerKey {
190    pub fn new(event_kind: Discriminant<MouseEvent>, button: Option<MouseButton>) -> HandlerKey {
191        HandlerKey { event_kind, button }
192    }
193}
194
195#[derive(Clone, Default)]
196pub struct HandlerSet {
197    set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>>,
198}
199
200impl HandlerSet {
201    pub fn capture_all() -> Self {
202        let mut set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>> = HashMap::default();
203
204        set.insert(
205            HandlerKey::new(MouseEvent::move_disc(), None),
206            SmallVec::from_buf([Rc::new(|_, _| {})]),
207        );
208        set.insert(
209            HandlerKey::new(MouseEvent::hover_disc(), None),
210            SmallVec::from_buf([Rc::new(|_, _| {})]),
211        );
212        for button in MouseButton::all() {
213            set.insert(
214                HandlerKey::new(MouseEvent::drag_disc(), Some(button)),
215                SmallVec::from_buf([Rc::new(|_, _| {})]),
216            );
217            set.insert(
218                HandlerKey::new(MouseEvent::down_disc(), Some(button)),
219                SmallVec::from_buf([Rc::new(|_, _| {})]),
220            );
221            set.insert(
222                HandlerKey::new(MouseEvent::up_disc(), Some(button)),
223                SmallVec::from_buf([Rc::new(|_, _| {})]),
224            );
225            set.insert(
226                HandlerKey::new(MouseEvent::click_disc(), Some(button)),
227                SmallVec::from_buf([Rc::new(|_, _| {})]),
228            );
229            set.insert(
230                HandlerKey::new(MouseEvent::down_out_disc(), Some(button)),
231                SmallVec::from_buf([Rc::new(|_, _| {})]),
232            );
233            set.insert(
234                HandlerKey::new(MouseEvent::up_out_disc(), Some(button)),
235                SmallVec::from_buf([Rc::new(|_, _| {})]),
236            );
237        }
238        set.insert(
239            HandlerKey::new(MouseEvent::scroll_wheel_disc(), None),
240            SmallVec::from_buf([Rc::new(|_, _| {})]),
241        );
242
243        HandlerSet { set }
244    }
245
246    pub fn get(&self, key: &HandlerKey) -> Option<&[HandlerCallback]> {
247        self.set.get(key).map(|vec| vec.as_slice())
248    }
249
250    pub fn contains(
251        &self,
252        discriminant: Discriminant<MouseEvent>,
253        button: Option<MouseButton>,
254    ) -> bool {
255        self.set
256            .contains_key(&HandlerKey::new(discriminant, button))
257    }
258
259    fn insert(
260        &mut self,
261        event_kind: Discriminant<MouseEvent>,
262        button: Option<MouseButton>,
263        callback: HandlerCallback,
264    ) {
265        use std::collections::hash_map::Entry;
266
267        match self.set.entry(HandlerKey::new(event_kind, button)) {
268            Entry::Occupied(mut vec) => {
269                vec.get_mut().push(callback);
270            }
271
272            Entry::Vacant(entry) => {
273                entry.insert(SmallVec::from_buf([callback]));
274            }
275        }
276    }
277
278    pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut EventContext) + 'static) -> Self {
279        self.insert(MouseEvent::move_disc(), None,
280            Rc::new(move |region_event, cx| {
281                if let MouseEvent::Move(e) = region_event {
282                    handler(e, cx);
283                } else {
284                    panic!(
285                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}", 
286                        region_event);
287                }
288            }));
289        self
290    }
291
292    pub fn on_down(
293        mut self,
294        button: MouseButton,
295        handler: impl Fn(MouseDown, &mut EventContext) + 'static,
296    ) -> Self {
297        self.insert(MouseEvent::down_disc(), Some(button),
298            Rc::new(move |region_event, cx| {
299                if let MouseEvent::Down(e) = region_event {
300                    handler(e, cx);
301                } else {
302                    panic!(
303                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Down, found {:?}", 
304                        region_event);
305                }
306            }));
307        self
308    }
309
310    pub fn on_up(
311        mut self,
312        button: MouseButton,
313        handler: impl Fn(MouseUp, &mut EventContext) + 'static,
314    ) -> Self {
315        self.insert(MouseEvent::up_disc(), Some(button),
316            Rc::new(move |region_event, cx| {
317                if let MouseEvent::Up(e) = region_event {
318                    handler(e, cx);
319                } else {
320                    panic!(
321                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Up, found {:?}", 
322                        region_event);
323                }
324            }));
325        self
326    }
327
328    pub fn on_click(
329        mut self,
330        button: MouseButton,
331        handler: impl Fn(MouseClick, &mut EventContext) + 'static,
332    ) -> Self {
333        self.insert(MouseEvent::click_disc(), Some(button),
334            Rc::new(move |region_event, cx| {
335                if let MouseEvent::Click(e) = region_event {
336                    handler(e, cx);
337                } else {
338                    panic!(
339                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Click, found {:?}", 
340                        region_event);
341                }
342            }));
343        self
344    }
345
346    pub fn on_down_out(
347        mut self,
348        button: MouseButton,
349        handler: impl Fn(MouseDownOut, &mut EventContext) + 'static,
350    ) -> Self {
351        self.insert(MouseEvent::down_out_disc(), Some(button),
352            Rc::new(move |region_event, cx| {
353                if let MouseEvent::DownOut(e) = region_event {
354                    handler(e, cx);
355                } else {
356                    panic!(
357                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DownOut, found {:?}", 
358                        region_event);
359                }
360            }));
361        self
362    }
363
364    pub fn on_up_out(
365        mut self,
366        button: MouseButton,
367        handler: impl Fn(MouseUpOut, &mut EventContext) + 'static,
368    ) -> Self {
369        self.insert(MouseEvent::up_out_disc(), Some(button),
370            Rc::new(move |region_event, cx| {
371                if let MouseEvent::UpOut(e) = region_event {
372                    handler(e, cx);
373                } else {
374                    panic!(
375                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::UpOut, found {:?}", 
376                        region_event);
377                }
378            }));
379        self
380    }
381
382    pub fn on_drag(
383        mut self,
384        button: MouseButton,
385        handler: impl Fn(MouseDrag, &mut EventContext) + 'static,
386    ) -> Self {
387        self.insert(MouseEvent::drag_disc(), Some(button),
388            Rc::new(move |region_event, cx| {
389                if let MouseEvent::Drag(e) = region_event {
390                    handler(e, cx);
391                } else {
392                    panic!(
393                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Drag, found {:?}", 
394                        region_event);
395                }
396            }));
397        self
398    }
399
400    pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut EventContext) + 'static) -> Self {
401        self.insert(MouseEvent::hover_disc(), None,
402            Rc::new(move |region_event, cx| {
403                if let MouseEvent::Hover(e) = region_event {
404                    handler(e, cx);
405                } else {
406                    panic!(
407                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Hover, found {:?}", 
408                        region_event);
409                }
410            }));
411        self
412    }
413
414    pub fn on_scroll(
415        mut self,
416        handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static,
417    ) -> Self {
418        self.insert(MouseEvent::scroll_wheel_disc(), None,
419            Rc::new(move |region_event, cx| {
420                if let MouseEvent::ScrollWheel(e) = region_event {
421                    handler(e, cx);
422                } else {
423                    panic!(
424                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
425                        region_event
426                    );
427                }
428            }));
429        self
430    }
431}