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 DebugContext, Element, ElementBox, EventContext, LayoutContext, MeasurementContext,
14 MouseRegion, MouseState, PaintContext, RenderContext, SizeConstraint, View,
15};
16use serde_json::json;
17use std::{marker::PhantomData, ops::Range};
18
19pub struct MouseEventHandler<Tag: 'static> {
20 child: ElementBox,
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> MouseEventHandler<Tag> {
35 pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
36 where
37 V: View,
38 F: FnOnce(&mut MouseState, &mut RenderContext<V>) -> ElementBox,
39 {
40 let mut mouse_state = cx.mouse_state::<Tag>(region_id);
41 let child = render_child(&mut mouse_state, cx);
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<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
62 where
63 V: View,
64 F: FnOnce(&mut MouseState, &mut RenderContext<V>) -> ElementBox,
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(mut self, handler: impl Fn(MouseMove, &mut EventContext) + 'static) -> Self {
82 self.handlers = self.handlers.on_move(handler);
83 self
84 }
85
86 pub fn on_move_out(
87 mut self,
88 handler: impl Fn(MouseMoveOut, &mut EventContext) + 'static,
89 ) -> Self {
90 self.handlers = self.handlers.on_move_out(handler);
91 self
92 }
93
94 pub fn on_down(
95 mut self,
96 button: MouseButton,
97 handler: impl Fn(MouseDown, &mut EventContext) + 'static,
98 ) -> Self {
99 self.handlers = self.handlers.on_down(button, handler);
100 self
101 }
102
103 pub fn on_up(
104 mut self,
105 button: MouseButton,
106 handler: impl Fn(MouseUp, &mut EventContext) + 'static,
107 ) -> Self {
108 self.handlers = self.handlers.on_up(button, handler);
109 self
110 }
111
112 pub fn on_click(
113 mut self,
114 button: MouseButton,
115 handler: impl Fn(MouseClick, &mut EventContext) + 'static,
116 ) -> Self {
117 self.handlers = self.handlers.on_click(button, handler);
118 self
119 }
120
121 pub fn on_down_out(
122 mut self,
123 button: MouseButton,
124 handler: impl Fn(MouseDownOut, &mut EventContext) + 'static,
125 ) -> Self {
126 self.handlers = self.handlers.on_down_out(button, handler);
127 self
128 }
129
130 pub fn on_up_out(
131 mut self,
132 button: MouseButton,
133 handler: impl Fn(MouseUpOut, &mut EventContext) + 'static,
134 ) -> Self {
135 self.handlers = self.handlers.on_up_out(button, handler);
136 self
137 }
138
139 pub fn on_drag(
140 mut self,
141 button: MouseButton,
142 handler: impl Fn(MouseDrag, &mut EventContext) + 'static,
143 ) -> Self {
144 self.handlers = self.handlers.on_drag(button, handler);
145 self
146 }
147
148 pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut EventContext) + 'static) -> Self {
149 self.handlers = self.handlers.on_hover(handler);
150 self
151 }
152
153 pub fn on_scroll(
154 mut self,
155 handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static,
156 ) -> Self {
157 self.handlers = self.handlers.on_scroll(handler);
158 self
159 }
160
161 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
162 self.hoverable = is_hoverable;
163 self
164 }
165
166 pub fn with_padding(mut self, padding: Padding) -> Self {
167 self.padding = padding;
168 self
169 }
170
171 fn hit_bounds(&self, bounds: RectF) -> RectF {
172 RectF::from_points(
173 bounds.origin() - vec2f(self.padding.left, self.padding.top),
174 bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
175 )
176 .round_out()
177 }
178
179 fn paint_regions(&self, bounds: RectF, visible_bounds: RectF, cx: &mut PaintContext) {
180 let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
181 let hit_bounds = self.hit_bounds(visible_bounds);
182
183 if let Some(style) = self.cursor_style {
184 cx.scene.push_cursor_region(CursorRegion {
185 bounds: hit_bounds,
186 style,
187 });
188 }
189 cx.scene.push_mouse_region(
190 MouseRegion::from_handlers::<Tag>(
191 cx.current_view_id(),
192 self.region_id,
193 hit_bounds,
194 self.handlers.clone(),
195 )
196 .with_hoverable(self.hoverable)
197 .with_notify_on_hover(self.notify_on_hover)
198 .with_notify_on_click(self.notify_on_click),
199 );
200 }
201}
202
203impl<Tag> Element for MouseEventHandler<Tag> {
204 type LayoutState = ();
205 type PaintState = ();
206
207 fn layout(
208 &mut self,
209 constraint: SizeConstraint,
210 cx: &mut LayoutContext,
211 ) -> (Vector2F, Self::LayoutState) {
212 (self.child.layout(constraint, cx), ())
213 }
214
215 fn paint(
216 &mut self,
217 bounds: RectF,
218 visible_bounds: RectF,
219 _: &mut Self::LayoutState,
220 cx: &mut PaintContext,
221 ) -> Self::PaintState {
222 if self.above {
223 self.child.paint(bounds.origin(), visible_bounds, cx);
224
225 cx.paint_layer(None, |cx| {
226 self.paint_regions(bounds, visible_bounds, cx);
227 });
228 } else {
229 self.paint_regions(bounds, visible_bounds, cx);
230 self.child.paint(bounds.origin(), visible_bounds, cx);
231 }
232 }
233
234 fn rect_for_text_range(
235 &self,
236 range_utf16: Range<usize>,
237 _: RectF,
238 _: RectF,
239 _: &Self::LayoutState,
240 _: &Self::PaintState,
241 cx: &MeasurementContext,
242 ) -> Option<RectF> {
243 self.child.rect_for_text_range(range_utf16, cx)
244 }
245
246 fn debug(
247 &self,
248 _: RectF,
249 _: &Self::LayoutState,
250 _: &Self::PaintState,
251 cx: &DebugContext,
252 ) -> serde_json::Value {
253 json!({
254 "type": "MouseEventHandler",
255 "child": self.child.debug(cx),
256 })
257 }
258}