mouse_event_handler.rs

  1use super::Padding;
  2use crate::{
  3    geometry::{
  4        rect::RectF,
  5        vector::{vec2f, Vector2F},
  6    },
  7    platform::CursorStyle,
  8    platform::MouseButton,
  9    scene::{
 10        CursorRegion, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseHover,
 11        MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
 12    },
 13    AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, SceneBuilder,
 14    SizeConstraint, View, ViewContext,
 15};
 16use serde_json::json;
 17use std::{marker::PhantomData, ops::Range};
 18
 19pub struct MouseEventHandler<Tag: 'static, V: View> {
 20    child: AnyElement<V>,
 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    above: bool,
 28    padding: Padding,
 29    _tag: PhantomData<Tag>,
 30}
 31
 32/// Element which provides a render_child callback with a MouseState and paints a mouse
 33/// region under (or above) it for easy mouse event handling.
 34impl<Tag, V: View> MouseEventHandler<Tag, V> {
 35    pub fn new<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
 36    where
 37        D: Element<V>,
 38        F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
 39    {
 40        let mut mouse_state = cx.mouse_state::<Tag>(region_id);
 41        let child = render_child(&mut mouse_state, cx).into_any();
 42        let notify_on_hover = mouse_state.accessed_hovered();
 43        let notify_on_click = mouse_state.accessed_clicked();
 44        Self {
 45            child,
 46            region_id,
 47            cursor_style: None,
 48            handlers: Default::default(),
 49            notify_on_hover,
 50            notify_on_click,
 51            hoverable: true,
 52            above: false,
 53            padding: Default::default(),
 54            _tag: PhantomData,
 55        }
 56    }
 57
 58    /// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
 59    /// for drag and drop handling and similar events which should be captured before the child
 60    /// gets the opportunity
 61    pub fn above<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
 62    where
 63        D: Element<V>,
 64        F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
 65    {
 66        let mut handler = Self::new(region_id, cx, render_child);
 67        handler.above = true;
 68        handler
 69    }
 70
 71    pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
 72        self.cursor_style = Some(cursor);
 73        self
 74    }
 75
 76    pub fn capture_all(mut self) -> Self {
 77        self.handlers = HandlerSet::capture_all();
 78        self
 79    }
 80
 81    pub fn on_move(
 82        mut self,
 83        handler: impl Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
 84    ) -> Self {
 85        self.handlers = self.handlers.on_move(handler);
 86        self
 87    }
 88
 89    pub fn on_move_out(
 90        mut self,
 91        handler: impl Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
 92    ) -> Self {
 93        self.handlers = self.handlers.on_move_out(handler);
 94        self
 95    }
 96
 97    pub fn on_down(
 98        mut self,
 99        button: MouseButton,
100        handler: impl Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
101    ) -> Self {
102        self.handlers = self.handlers.on_down(button, handler);
103        self
104    }
105
106    pub fn on_up(
107        mut self,
108        button: MouseButton,
109        handler: impl Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
110    ) -> Self {
111        self.handlers = self.handlers.on_up(button, handler);
112        self
113    }
114
115    pub fn on_click(
116        mut self,
117        button: MouseButton,
118        handler: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
119    ) -> Self {
120        self.handlers = self.handlers.on_click(button, handler);
121        self
122    }
123
124    pub fn on_down_out(
125        mut self,
126        button: MouseButton,
127        handler: impl Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
128    ) -> Self {
129        self.handlers = self.handlers.on_down_out(button, handler);
130        self
131    }
132
133    pub fn on_up_out(
134        mut self,
135        button: MouseButton,
136        handler: impl Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
137    ) -> Self {
138        self.handlers = self.handlers.on_up_out(button, handler);
139        self
140    }
141
142    pub fn on_drag(
143        mut self,
144        button: MouseButton,
145        handler: impl Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
146    ) -> Self {
147        self.handlers = self.handlers.on_drag(button, handler);
148        self
149    }
150
151    pub fn on_hover(
152        mut self,
153        handler: impl Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
154    ) -> Self {
155        self.handlers = self.handlers.on_hover(handler);
156        self
157    }
158
159    pub fn on_scroll(
160        mut self,
161        handler: impl Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
162    ) -> Self {
163        self.handlers = self.handlers.on_scroll(handler);
164        self
165    }
166
167    pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
168        self.hoverable = is_hoverable;
169        self
170    }
171
172    pub fn with_padding(mut self, padding: Padding) -> Self {
173        self.padding = padding;
174        self
175    }
176
177    fn hit_bounds(&self, bounds: RectF) -> RectF {
178        RectF::from_points(
179            bounds.origin() - vec2f(self.padding.left, self.padding.top),
180            bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
181        )
182        .round_out()
183    }
184
185    fn paint_regions(
186        &self,
187        scene: &mut SceneBuilder,
188        bounds: RectF,
189        visible_bounds: RectF,
190        cx: &mut ViewContext<V>,
191    ) {
192        let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
193        let hit_bounds = self.hit_bounds(visible_bounds);
194
195        if let Some(style) = self.cursor_style {
196            scene.push_cursor_region(CursorRegion {
197                bounds: hit_bounds,
198                style,
199            });
200        }
201        scene.push_mouse_region(
202            MouseRegion::from_handlers::<Tag>(
203                cx.view_id(),
204                self.region_id,
205                hit_bounds,
206                self.handlers.clone(),
207            )
208            .with_hoverable(self.hoverable)
209            .with_notify_on_hover(self.notify_on_hover)
210            .with_notify_on_click(self.notify_on_click),
211        );
212    }
213}
214
215impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
216    type LayoutState = ();
217    type PaintState = ();
218
219    fn layout(
220        &mut self,
221        constraint: SizeConstraint,
222        view: &mut V,
223        cx: &mut LayoutContext<V>,
224    ) -> (Vector2F, Self::LayoutState) {
225        (self.child.layout(constraint, view, cx), ())
226    }
227
228    fn paint(
229        &mut self,
230        scene: &mut SceneBuilder,
231        bounds: RectF,
232        visible_bounds: RectF,
233        _: &mut Self::LayoutState,
234        view: &mut V,
235        cx: &mut ViewContext<V>,
236    ) -> Self::PaintState {
237        if self.above {
238            self.child
239                .paint(scene, bounds.origin(), visible_bounds, view, cx);
240
241            scene.paint_layer(None, |scene| {
242                self.paint_regions(scene, bounds, visible_bounds, cx);
243            });
244        } else {
245            self.paint_regions(scene, bounds, visible_bounds, cx);
246            self.child
247                .paint(scene, bounds.origin(), visible_bounds, view, cx);
248        }
249    }
250
251    fn rect_for_text_range(
252        &self,
253        range_utf16: Range<usize>,
254        _: RectF,
255        _: RectF,
256        _: &Self::LayoutState,
257        _: &Self::PaintState,
258        view: &V,
259        cx: &ViewContext<V>,
260    ) -> Option<RectF> {
261        self.child.rect_for_text_range(range_utf16, view, cx)
262    }
263
264    fn debug(
265        &self,
266        _: RectF,
267        _: &Self::LayoutState,
268        _: &Self::PaintState,
269        view: &V,
270        cx: &ViewContext<V>,
271    ) -> serde_json::Value {
272        json!({
273            "type": "MouseEventHandler",
274            "child": self.child.debug(view, cx),
275        })
276    }
277}