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