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