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