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