event_handler.rs

  1use crate::{
  2    geometry::vector::Vector2F, presenter::MeasurementContext, scene::HandlerSet, CursorRegion,
  3    DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseButton,
  4    MouseButtonEvent, MouseRegion, NavigationDirection, PaintContext, SizeConstraint,
  5};
  6use pathfinder_geometry::rect::RectF;
  7use serde_json::json;
  8use std::{any::TypeId, ops::Range};
  9
 10pub struct EventHandler {
 11    child: ElementBox,
 12    capture_all: Option<(TypeId, usize)>,
 13    mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
 14    right_mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
 15    navigate_mouse_down: Option<Box<dyn FnMut(NavigationDirection, &mut EventContext) -> bool>>,
 16}
 17
 18impl EventHandler {
 19    pub fn new(child: ElementBox) -> Self {
 20        Self {
 21            child,
 22            capture_all: None,
 23            mouse_down: None,
 24            right_mouse_down: None,
 25            navigate_mouse_down: None,
 26        }
 27    }
 28
 29    pub fn on_mouse_down<F>(mut self, callback: F) -> Self
 30    where
 31        F: 'static + FnMut(&mut EventContext) -> bool,
 32    {
 33        self.mouse_down = Some(Box::new(callback));
 34        self
 35    }
 36
 37    pub fn on_right_mouse_down<F>(mut self, callback: F) -> Self
 38    where
 39        F: 'static + FnMut(&mut EventContext) -> bool,
 40    {
 41        self.right_mouse_down = Some(Box::new(callback));
 42        self
 43    }
 44
 45    pub fn on_navigate_mouse_down<F>(mut self, callback: F) -> Self
 46    where
 47        F: 'static + FnMut(NavigationDirection, &mut EventContext) -> bool,
 48    {
 49        self.navigate_mouse_down = Some(Box::new(callback));
 50        self
 51    }
 52
 53    pub fn capture_all<T: 'static>(mut self, id: usize) -> Self {
 54        self.capture_all = Some((TypeId::of::<T>(), id));
 55        self
 56    }
 57}
 58
 59impl Element for EventHandler {
 60    type LayoutState = ();
 61    type PaintState = ();
 62
 63    fn layout(
 64        &mut self,
 65        constraint: SizeConstraint,
 66        cx: &mut LayoutContext,
 67    ) -> (Vector2F, Self::LayoutState) {
 68        let size = self.child.layout(constraint, cx);
 69        (size, ())
 70    }
 71
 72    fn paint(
 73        &mut self,
 74        bounds: RectF,
 75        visible_bounds: RectF,
 76        _: &mut Self::LayoutState,
 77        cx: &mut PaintContext,
 78    ) -> Self::PaintState {
 79        if let Some(discriminant) = self.capture_all {
 80            cx.scene.push_stacking_context(None);
 81            cx.scene.push_cursor_region(CursorRegion {
 82                bounds: visible_bounds,
 83                style: Default::default(),
 84            });
 85            cx.scene.push_mouse_region(MouseRegion {
 86                view_id: cx.current_view_id(),
 87                discriminant,
 88                bounds: visible_bounds,
 89                handlers: HandlerSet::capture_all(),
 90                hoverable: true,
 91            });
 92            cx.scene.pop_stacking_context();
 93        }
 94        self.child.paint(bounds.origin(), visible_bounds, cx);
 95    }
 96
 97    fn dispatch_event(
 98        &mut self,
 99        event: &Event,
100        _: RectF,
101        visible_bounds: RectF,
102        _: &mut Self::LayoutState,
103        _: &mut Self::PaintState,
104        cx: &mut EventContext,
105    ) -> bool {
106        if self.capture_all.is_some() {
107            return true;
108        }
109
110        if self.child.dispatch_event(event, cx) {
111            true
112        } else {
113            match event {
114                Event::MouseDown(MouseButtonEvent {
115                    button: MouseButton::Left,
116                    position,
117                    ..
118                }) => {
119                    if let Some(callback) = self.mouse_down.as_mut() {
120                        if visible_bounds.contains_point(*position) {
121                            return callback(cx);
122                        }
123                    }
124                    false
125                }
126                Event::MouseDown(MouseButtonEvent {
127                    button: MouseButton::Right,
128                    position,
129                    ..
130                }) => {
131                    if let Some(callback) = self.right_mouse_down.as_mut() {
132                        if visible_bounds.contains_point(*position) {
133                            return callback(cx);
134                        }
135                    }
136                    false
137                }
138                Event::MouseDown(MouseButtonEvent {
139                    button: MouseButton::Navigate(direction),
140                    position,
141                    ..
142                }) => {
143                    if let Some(callback) = self.navigate_mouse_down.as_mut() {
144                        if visible_bounds.contains_point(*position) {
145                            return callback(*direction, cx);
146                        }
147                    }
148                    false
149                }
150                _ => false,
151            }
152        }
153    }
154
155    fn rect_for_text_range(
156        &self,
157        range_utf16: Range<usize>,
158        _: RectF,
159        _: RectF,
160        _: &Self::LayoutState,
161        _: &Self::PaintState,
162        cx: &MeasurementContext,
163    ) -> Option<RectF> {
164        self.child.rect_for_text_range(range_utf16, cx)
165    }
166
167    fn debug(
168        &self,
169        _: RectF,
170        _: &Self::LayoutState,
171        _: &Self::PaintState,
172        cx: &DebugContext,
173    ) -> serde_json::Value {
174        json!({
175            "type": "EventHandler",
176            "child": self.child.debug(cx),
177        })
178    }
179}