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}