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