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, MouseDown, MouseDownOut, MouseDrag, MouseHover,
11 MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
12 },
13 AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, SceneBuilder,
14 SizeConstraint, View, ViewContext,
15};
16use serde_json::json;
17use std::{marker::PhantomData, ops::Range};
18
19pub struct MouseEventHandler<Tag: 'static, V: View> {
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: PhantomData<Tag>,
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<Tag, V: View> MouseEventHandler<Tag, V> {
35 pub fn new<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
36 where
37 D: Element<V>,
38 F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
39 {
40 let mut mouse_state = cx.mouse_state::<Tag>(region_id);
41 let child = render_child(&mut mouse_state, cx).into_any();
42 let notify_on_hover = mouse_state.accessed_hovered();
43 let notify_on_click = mouse_state.accessed_clicked();
44 Self {
45 child,
46 region_id,
47 cursor_style: None,
48 handlers: Default::default(),
49 notify_on_hover,
50 notify_on_click,
51 hoverable: true,
52 above: false,
53 padding: Default::default(),
54 _tag: PhantomData,
55 }
56 }
57
58 /// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
59 /// for drag and drop handling and similar events which should be captured before the child
60 /// gets the opportunity
61 pub fn above<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
62 where
63 D: Element<V>,
64 F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
65 {
66 let mut handler = Self::new(region_id, cx, render_child);
67 handler.above = true;
68 handler
69 }
70
71 pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
72 self.cursor_style = Some(cursor);
73 self
74 }
75
76 pub fn capture_all(mut self) -> Self {
77 self.handlers = HandlerSet::capture_all();
78 self
79 }
80
81 pub fn on_move(
82 mut self,
83 handler: impl Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
84 ) -> Self {
85 self.handlers = self.handlers.on_move(handler);
86 self
87 }
88
89 pub fn on_move_out(
90 mut self,
91 handler: impl Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
92 ) -> Self {
93 self.handlers = self.handlers.on_move_out(handler);
94 self
95 }
96
97 pub fn on_down(
98 mut self,
99 button: MouseButton,
100 handler: impl Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
101 ) -> Self {
102 self.handlers = self.handlers.on_down(button, handler);
103 self
104 }
105
106 pub fn on_up(
107 mut self,
108 button: MouseButton,
109 handler: impl Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
110 ) -> Self {
111 self.handlers = self.handlers.on_up(button, handler);
112 self
113 }
114
115 pub fn on_click(
116 mut self,
117 button: MouseButton,
118 handler: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
119 ) -> Self {
120 self.handlers = self.handlers.on_click(button, handler);
121 self
122 }
123
124 pub fn on_down_out(
125 mut self,
126 button: MouseButton,
127 handler: impl Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
128 ) -> Self {
129 self.handlers = self.handlers.on_down_out(button, handler);
130 self
131 }
132
133 pub fn on_up_out(
134 mut self,
135 button: MouseButton,
136 handler: impl Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
137 ) -> Self {
138 self.handlers = self.handlers.on_up_out(button, handler);
139 self
140 }
141
142 pub fn on_drag(
143 mut self,
144 button: MouseButton,
145 handler: impl Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
146 ) -> Self {
147 self.handlers = self.handlers.on_drag(button, handler);
148 self
149 }
150
151 pub fn on_hover(
152 mut self,
153 handler: impl Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
154 ) -> Self {
155 self.handlers = self.handlers.on_hover(handler);
156 self
157 }
158
159 pub fn on_scroll(
160 mut self,
161 handler: impl Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
162 ) -> Self {
163 self.handlers = self.handlers.on_scroll(handler);
164 self
165 }
166
167 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
168 self.hoverable = is_hoverable;
169 self
170 }
171
172 pub fn with_padding(mut self, padding: Padding) -> Self {
173 self.padding = padding;
174 self
175 }
176
177 fn hit_bounds(&self, bounds: RectF) -> RectF {
178 RectF::from_points(
179 bounds.origin() - vec2f(self.padding.left, self.padding.top),
180 bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
181 )
182 .round_out()
183 }
184
185 fn paint_regions(
186 &self,
187 scene: &mut SceneBuilder,
188 bounds: RectF,
189 visible_bounds: RectF,
190 cx: &mut ViewContext<V>,
191 ) {
192 let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
193 let hit_bounds = self.hit_bounds(visible_bounds);
194
195 if let Some(style) = self.cursor_style {
196 scene.push_cursor_region(CursorRegion {
197 bounds: hit_bounds,
198 style,
199 });
200 }
201 scene.push_mouse_region(
202 MouseRegion::from_handlers::<Tag>(
203 cx.view_id(),
204 self.region_id,
205 hit_bounds,
206 self.handlers.clone(),
207 )
208 .with_hoverable(self.hoverable)
209 .with_notify_on_hover(self.notify_on_hover)
210 .with_notify_on_click(self.notify_on_click),
211 );
212 }
213}
214
215impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
216 type LayoutState = ();
217 type PaintState = ();
218
219 fn layout(
220 &mut self,
221 constraint: SizeConstraint,
222 view: &mut V,
223 cx: &mut LayoutContext<V>,
224 ) -> (Vector2F, Self::LayoutState) {
225 (self.child.layout(constraint, view, cx), ())
226 }
227
228 fn paint(
229 &mut self,
230 scene: &mut SceneBuilder,
231 bounds: RectF,
232 visible_bounds: RectF,
233 _: &mut Self::LayoutState,
234 view: &mut V,
235 cx: &mut ViewContext<V>,
236 ) -> Self::PaintState {
237 if self.above {
238 self.child
239 .paint(scene, bounds.origin(), visible_bounds, view, cx);
240
241 scene.paint_layer(None, |scene| {
242 self.paint_regions(scene, bounds, visible_bounds, cx);
243 });
244 } else {
245 self.paint_regions(scene, bounds, visible_bounds, cx);
246 self.child
247 .paint(scene, bounds.origin(), visible_bounds, view, cx);
248 }
249 }
250
251 fn rect_for_text_range(
252 &self,
253 range_utf16: Range<usize>,
254 _: RectF,
255 _: RectF,
256 _: &Self::LayoutState,
257 _: &Self::PaintState,
258 view: &V,
259 cx: &ViewContext<V>,
260 ) -> Option<RectF> {
261 self.child.rect_for_text_range(range_utf16, view, cx)
262 }
263
264 fn debug(
265 &self,
266 _: RectF,
267 _: &Self::LayoutState,
268 _: &Self::PaintState,
269 view: &V,
270 cx: &ViewContext<V>,
271 ) -> serde_json::Value {
272 json!({
273 "type": "MouseEventHandler",
274 "child": self.child.debug(view, cx),
275 })
276 }
277}