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