mouse_event_handler.rs

  1use super::Padding;
  2use crate::{
  3    geometry::{
  4        rect::RectF,
  5        vector::{vec2f, Vector2F},
  6    },
  7    platform::CursorStyle,
  8    scene::{
  9        ClickRegionEvent, CursorRegion, DownOutRegionEvent, DownRegionEvent, DragRegionEvent,
 10        HandlerSet, HoverRegionEvent, MoveRegionEvent, ScrollWheelRegionEvent, UpOutRegionEvent,
 11        UpRegionEvent,
 12    },
 13    DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MeasurementContext,
 14    MouseButton, MouseRegion, MouseState, PaintContext, RenderContext, SizeConstraint, View,
 15};
 16use serde_json::json;
 17use std::{marker::PhantomData, ops::Range};
 18
 19pub struct MouseEventHandler<Tag: 'static> {
 20    child: ElementBox,
 21    region_id: usize,
 22    cursor_style: Option<CursorStyle>,
 23    handlers: HandlerSet,
 24    hoverable: bool,
 25    padding: Padding,
 26    _tag: PhantomData<Tag>,
 27}
 28
 29impl<Tag> MouseEventHandler<Tag> {
 30    pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
 31    where
 32        V: View,
 33        F: FnOnce(MouseState, &mut RenderContext<V>) -> ElementBox,
 34    {
 35        Self {
 36            child: render_child(cx.mouse_state::<Tag>(region_id), cx),
 37            region_id,
 38            cursor_style: None,
 39            handlers: Default::default(),
 40            hoverable: true,
 41            padding: Default::default(),
 42            _tag: PhantomData,
 43        }
 44    }
 45
 46    pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
 47        self.cursor_style = Some(cursor);
 48        self
 49    }
 50
 51    pub fn capture_all(mut self) -> Self {
 52        self.handlers = HandlerSet::capture_all();
 53        self
 54    }
 55
 56    pub fn on_move(
 57        mut self,
 58        handler: impl Fn(MoveRegionEvent, &mut EventContext) + 'static,
 59    ) -> Self {
 60        self.handlers = self.handlers.on_move(handler);
 61        self
 62    }
 63
 64    pub fn on_down(
 65        mut self,
 66        button: MouseButton,
 67        handler: impl Fn(DownRegionEvent, &mut EventContext) + 'static,
 68    ) -> Self {
 69        self.handlers = self.handlers.on_down(button, handler);
 70        self
 71    }
 72
 73    pub fn on_up(
 74        mut self,
 75        button: MouseButton,
 76        handler: impl Fn(UpRegionEvent, &mut EventContext) + 'static,
 77    ) -> Self {
 78        self.handlers = self.handlers.on_up(button, handler);
 79        self
 80    }
 81
 82    pub fn on_click(
 83        mut self,
 84        button: MouseButton,
 85        handler: impl Fn(ClickRegionEvent, &mut EventContext) + 'static,
 86    ) -> Self {
 87        self.handlers = self.handlers.on_click(button, handler);
 88        self
 89    }
 90
 91    pub fn on_down_out(
 92        mut self,
 93        button: MouseButton,
 94        handler: impl Fn(DownOutRegionEvent, &mut EventContext) + 'static,
 95    ) -> Self {
 96        self.handlers = self.handlers.on_down_out(button, handler);
 97        self
 98    }
 99
100    pub fn on_up_out(
101        mut self,
102        button: MouseButton,
103        handler: impl Fn(UpOutRegionEvent, &mut EventContext) + 'static,
104    ) -> Self {
105        self.handlers = self.handlers.on_up_out(button, handler);
106        self
107    }
108
109    pub fn on_drag(
110        mut self,
111        button: MouseButton,
112        handler: impl Fn(DragRegionEvent, &mut EventContext) + 'static,
113    ) -> Self {
114        self.handlers = self.handlers.on_drag(button, handler);
115        self
116    }
117
118    pub fn on_hover(
119        mut self,
120        handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,
121    ) -> Self {
122        self.handlers = self.handlers.on_hover(handler);
123        self
124    }
125
126    pub fn on_scroll(
127        mut self,
128        handler: impl Fn(ScrollWheelRegionEvent, &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_padding(mut self, padding: Padding) -> Self {
140        self.padding = padding;
141        self
142    }
143
144    fn hit_bounds(&self, bounds: RectF) -> RectF {
145        RectF::from_points(
146            bounds.origin() - vec2f(self.padding.left, self.padding.top),
147            bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
148        )
149        .round_out()
150    }
151}
152
153impl<Tag> Element for MouseEventHandler<Tag> {
154    type LayoutState = ();
155    type PaintState = ();
156
157    fn layout(
158        &mut self,
159        constraint: SizeConstraint,
160        cx: &mut LayoutContext,
161    ) -> (Vector2F, Self::LayoutState) {
162        (self.child.layout(constraint, cx), ())
163    }
164
165    fn paint(
166        &mut self,
167        bounds: RectF,
168        visible_bounds: RectF,
169        _: &mut Self::LayoutState,
170        cx: &mut PaintContext,
171    ) -> Self::PaintState {
172        let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
173        let hit_bounds = self.hit_bounds(visible_bounds);
174        if let Some(style) = self.cursor_style {
175            cx.scene.push_cursor_region(CursorRegion {
176                bounds: hit_bounds,
177                style,
178            });
179        }
180
181        cx.scene.push_mouse_region(
182            MouseRegion::from_handlers::<Tag>(
183                cx.current_view_id(),
184                self.region_id,
185                hit_bounds,
186                self.handlers.clone(),
187            )
188            .with_hoverable(self.hoverable),
189        );
190
191        self.child.paint(bounds.origin(), visible_bounds, cx);
192    }
193
194    fn dispatch_event(
195        &mut self,
196        event: &Event,
197        _: RectF,
198        _: RectF,
199        _: &mut Self::LayoutState,
200        _: &mut Self::PaintState,
201        cx: &mut EventContext,
202    ) -> bool {
203        self.child.dispatch_event(event, cx)
204    }
205
206    fn rect_for_text_range(
207        &self,
208        range_utf16: Range<usize>,
209        _: RectF,
210        _: RectF,
211        _: &Self::LayoutState,
212        _: &Self::PaintState,
213        cx: &MeasurementContext,
214    ) -> Option<RectF> {
215        self.child.rect_for_text_range(range_utf16, cx)
216    }
217
218    fn debug(
219        &self,
220        _: RectF,
221        _: &Self::LayoutState,
222        _: &Self::PaintState,
223        cx: &DebugContext,
224    ) -> serde_json::Value {
225        json!({
226            "type": "MouseEventHandler",
227            "child": self.child.debug(cx),
228        })
229    }
230}