1use super::Padding;
2use crate::{
3 geometry::{
4 rect::RectF,
5 vector::{vec2f, Vector2F},
6 },
7 platform::CursorStyle,
8 platform::MouseButton,
9 scene::{
10 CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
11 MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
12 },
13 AnyElement, Element, EventContext, MouseRegion, MouseState, SizeConstraint, TypeTag,
14 ViewContext,
15};
16use serde_json::json;
17use std::ops::Range;
18
19pub struct MouseEventHandler<V: 'static> {
20 child: AnyElement<V>,
21 region_id: usize,
22 cursor_style: Option<CursorStyle>,
23 handlers: HandlerSet,
24 hoverable: bool,
25 notify_on_hover: bool,
26 notify_on_click: bool,
27 above: bool,
28 padding: Padding,
29 tag: TypeTag,
30}
31
32/// Element which provides a render_child callback with a MouseState and paints a mouse
33/// region under (or above) it for easy mouse event handling.
34impl<V: 'static> MouseEventHandler<V> {
35 pub fn for_child<Tag: 'static>(child: impl Element<V>, region_id: usize) -> Self {
36 Self {
37 child: child.into_any(),
38 region_id,
39 cursor_style: None,
40 handlers: Default::default(),
41 notify_on_hover: false,
42 notify_on_click: false,
43 hoverable: false,
44 above: false,
45 padding: Default::default(),
46 tag: TypeTag::new::<Tag>(),
47 }
48 }
49
50 pub fn new<Tag: 'static, E>(
51 region_id: usize,
52 cx: &mut ViewContext<V>,
53 render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> E,
54 ) -> Self
55 where
56 E: Element<V>,
57 {
58 let mut mouse_state = cx.mouse_state_dynamic(TypeTag::new::<Tag>(), region_id);
59 let child = render_child(&mut mouse_state, cx).into_any();
60 let notify_on_hover = mouse_state.accessed_hovered();
61 let notify_on_click = mouse_state.accessed_clicked();
62 Self {
63 child,
64 region_id,
65 cursor_style: None,
66 handlers: Default::default(),
67 notify_on_hover,
68 notify_on_click,
69 hoverable: true,
70 above: false,
71 padding: Default::default(),
72 tag: TypeTag::new::<Tag>(),
73 }
74 }
75
76 pub fn new_dynamic(
77 tag: TypeTag,
78 region_id: usize,
79 cx: &mut ViewContext<V>,
80 render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> AnyElement<V>,
81 ) -> Self {
82 let mut mouse_state = cx.mouse_state_dynamic(tag, region_id);
83 let child = render_child(&mut mouse_state, cx);
84 let notify_on_hover = mouse_state.accessed_hovered();
85 let notify_on_click = mouse_state.accessed_clicked();
86 Self {
87 child,
88 region_id,
89 cursor_style: None,
90 handlers: Default::default(),
91 notify_on_hover,
92 notify_on_click,
93 hoverable: true,
94 above: false,
95 padding: Default::default(),
96 tag,
97 }
98 }
99
100 /// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
101 /// for drag and drop handling and similar events which should be captured before the child
102 /// gets the opportunity
103 pub fn above<Tag: 'static, D>(
104 region_id: usize,
105 cx: &mut ViewContext<V>,
106 render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
107 ) -> Self
108 where
109 D: Element<V>,
110 {
111 let mut handler = Self::new::<Tag, _>(region_id, cx, render_child);
112 handler.above = true;
113 handler
114 }
115
116 pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
117 self.cursor_style = Some(cursor);
118 self
119 }
120
121 pub fn capture_all(mut self) -> Self {
122 self.handlers = HandlerSet::capture_all();
123 self
124 }
125
126 pub fn on_move(
127 mut self,
128 handler: impl Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
129 ) -> Self {
130 self.handlers = self.handlers.on_move(handler);
131 self
132 }
133
134 pub fn on_move_out(
135 mut self,
136 handler: impl Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
137 ) -> Self {
138 self.handlers = self.handlers.on_move_out(handler);
139 self
140 }
141
142 pub fn on_down(
143 mut self,
144 button: MouseButton,
145 handler: impl Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
146 ) -> Self {
147 self.handlers = self.handlers.on_down(button, handler);
148 self
149 }
150
151 pub fn on_up(
152 mut self,
153 button: MouseButton,
154 handler: impl Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
155 ) -> Self {
156 self.handlers = self.handlers.on_up(button, handler);
157 self
158 }
159
160 pub fn on_click(
161 mut self,
162 button: MouseButton,
163 handler: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
164 ) -> Self {
165 self.handlers = self.handlers.on_click(button, handler);
166 self
167 }
168
169 pub fn on_click_out(
170 mut self,
171 button: MouseButton,
172 handler: impl Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
173 ) -> Self {
174 self.handlers = self.handlers.on_click_out(button, handler);
175 self
176 }
177
178 pub fn on_down_out(
179 mut self,
180 button: MouseButton,
181 handler: impl Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
182 ) -> Self {
183 self.handlers = self.handlers.on_down_out(button, handler);
184 self
185 }
186
187 pub fn on_up_out(
188 mut self,
189 button: MouseButton,
190 handler: impl Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
191 ) -> Self {
192 self.handlers = self.handlers.on_up_out(button, handler);
193 self
194 }
195
196 pub fn on_drag(
197 mut self,
198 button: MouseButton,
199 handler: impl Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
200 ) -> Self {
201 self.handlers = self.handlers.on_drag(button, handler);
202 self
203 }
204
205 pub fn on_hover(
206 mut self,
207 handler: impl Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
208 ) -> Self {
209 self.handlers = self.handlers.on_hover(handler);
210 self
211 }
212
213 pub fn on_scroll(
214 mut self,
215 handler: impl Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
216 ) -> Self {
217 self.handlers = self.handlers.on_scroll(handler);
218 self
219 }
220
221 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
222 self.hoverable = is_hoverable;
223 self
224 }
225
226 pub fn with_padding(mut self, padding: Padding) -> Self {
227 self.padding = padding;
228 self
229 }
230
231 fn hit_bounds(&self, bounds: RectF) -> RectF {
232 RectF::from_points(
233 bounds.origin() - vec2f(self.padding.left, self.padding.top),
234 bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
235 )
236 .round_out()
237 }
238
239 fn paint_regions(&self, bounds: RectF, visible_bounds: RectF, cx: &mut ViewContext<V>) {
240 let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
241 let hit_bounds = self.hit_bounds(visible_bounds);
242
243 if let Some(style) = self.cursor_style {
244 cx.scene().push_cursor_region(CursorRegion {
245 bounds: hit_bounds,
246 style,
247 });
248 }
249 let view_id = cx.view_id();
250 cx.scene().push_mouse_region(
251 MouseRegion::from_handlers(
252 self.tag,
253 view_id,
254 self.region_id,
255 hit_bounds,
256 self.handlers.clone(),
257 )
258 .with_hoverable(self.hoverable)
259 .with_notify_on_hover(self.notify_on_hover)
260 .with_notify_on_click(self.notify_on_click),
261 );
262 }
263}
264
265impl<V: 'static> Element<V> for MouseEventHandler<V> {
266 type LayoutState = ();
267 type PaintState = ();
268
269 fn layout(
270 &mut self,
271 constraint: SizeConstraint,
272 view: &mut V,
273 cx: &mut ViewContext<V>,
274 ) -> (Vector2F, Self::LayoutState) {
275 (self.child.layout(constraint, view, cx), ())
276 }
277
278 fn paint(
279 &mut self,
280 bounds: RectF,
281 visible_bounds: RectF,
282 _: &mut Self::LayoutState,
283 view: &mut V,
284 cx: &mut ViewContext<V>,
285 ) -> Self::PaintState {
286 if self.above {
287 self.child.paint(bounds.origin(), visible_bounds, view, cx);
288 cx.paint_layer(None, |cx| {
289 self.paint_regions(bounds, visible_bounds, cx);
290 });
291 } else {
292 self.paint_regions(bounds, visible_bounds, cx);
293 self.child.paint(bounds.origin(), visible_bounds, view, cx);
294 }
295 }
296
297 fn rect_for_text_range(
298 &self,
299 range_utf16: Range<usize>,
300 _: RectF,
301 _: RectF,
302 _: &Self::LayoutState,
303 _: &Self::PaintState,
304 view: &V,
305 cx: &ViewContext<V>,
306 ) -> Option<RectF> {
307 self.child.rect_for_text_range(range_utf16, view, cx)
308 }
309
310 fn debug(
311 &self,
312 _: RectF,
313 _: &Self::LayoutState,
314 _: &Self::PaintState,
315 view: &V,
316 cx: &ViewContext<V>,
317 ) -> serde_json::Value {
318 json!({
319 "type": "MouseEventHandler",
320 "child": self.child.debug(view, cx),
321 })
322 }
323}