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