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