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(MouseRegion {
171            view_id: cx.current_view_id(),
172            discriminant: self.discriminant,
173            bounds: hit_bounds,
174            handlers: self.handlers.clone(),
175            hoverable: self.hoverable,
176        });
177
178        self.child.paint(bounds.origin(), visible_bounds, cx);
179    }
180
181    fn dispatch_event(
182        &mut self,
183        event: &Event,
184        _: RectF,
185        _: RectF,
186        _: &mut Self::LayoutState,
187        _: &mut Self::PaintState,
188        cx: &mut EventContext,
189    ) -> bool {
190        self.child.dispatch_event(event, cx)
191    }
192
193    fn rect_for_text_range(
194        &self,
195        range_utf16: Range<usize>,
196        _: RectF,
197        _: RectF,
198        _: &Self::LayoutState,
199        _: &Self::PaintState,
200        cx: &MeasurementContext,
201    ) -> Option<RectF> {
202        self.child.rect_for_text_range(range_utf16, cx)
203    }
204
205    fn debug(
206        &self,
207        _: RectF,
208        _: &Self::LayoutState,
209        _: &Self::PaintState,
210        cx: &DebugContext,
211    ) -> serde_json::Value {
212        json!({
213            "type": "MouseEventHandler",
214            "child": self.child.debug(cx),
215        })
216    }
217}