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