mouse_region.rs

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