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