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