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