1use super::Padding;
2use crate::{
3 geometry::{
4 rect::RectF,
5 vector::{vec2f, Vector2F},
6 },
7 platform::CursorStyle,
8 scene::{
9 ClickRegionEvent, CursorRegion, DownOutRegionEvent, DownRegionEvent, DragRegionEvent,
10 HandlerSet, HoverRegionEvent, MoveRegionEvent, ScrollWheelRegionEvent, UpOutRegionEvent,
11 UpRegionEvent,
12 },
13 DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MeasurementContext,
14 MouseButton, 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 padding: Padding,
28 _tag: PhantomData<Tag>,
29}
30
31impl<Tag> MouseEventHandler<Tag> {
32 pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
33 where
34 V: View,
35 F: FnOnce(&mut MouseState, &mut RenderContext<V>) -> ElementBox,
36 {
37 let mut mouse_state = cx.mouse_state::<Tag>(region_id);
38 let child = render_child(&mut mouse_state, cx);
39 let notify_on_hover = mouse_state.accessed_hovered();
40 let notify_on_click = mouse_state.accessed_clicked();
41 Self {
42 child,
43 region_id,
44 cursor_style: None,
45 handlers: Default::default(),
46 notify_on_hover,
47 notify_on_click,
48 hoverable: true,
49 padding: Default::default(),
50 _tag: PhantomData,
51 }
52 }
53
54 pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
55 self.cursor_style = Some(cursor);
56 self
57 }
58
59 pub fn capture_all(mut self) -> Self {
60 self.handlers = HandlerSet::capture_all();
61 self
62 }
63
64 pub fn on_move(
65 mut self,
66 handler: impl Fn(MoveRegionEvent, &mut EventContext) + 'static,
67 ) -> Self {
68 self.handlers = self.handlers.on_move(handler);
69 self
70 }
71
72 pub fn on_down(
73 mut self,
74 button: MouseButton,
75 handler: impl Fn(DownRegionEvent, &mut EventContext) + 'static,
76 ) -> Self {
77 self.handlers = self.handlers.on_down(button, handler);
78 self
79 }
80
81 pub fn on_up(
82 mut self,
83 button: MouseButton,
84 handler: impl Fn(UpRegionEvent, &mut EventContext) + 'static,
85 ) -> Self {
86 self.handlers = self.handlers.on_up(button, handler);
87 self
88 }
89
90 pub fn on_click(
91 mut self,
92 button: MouseButton,
93 handler: impl Fn(ClickRegionEvent, &mut EventContext) + 'static,
94 ) -> Self {
95 self.handlers = self.handlers.on_click(button, handler);
96 self
97 }
98
99 pub fn on_down_out(
100 mut self,
101 button: MouseButton,
102 handler: impl Fn(DownOutRegionEvent, &mut EventContext) + 'static,
103 ) -> Self {
104 self.handlers = self.handlers.on_down_out(button, handler);
105 self
106 }
107
108 pub fn on_up_out(
109 mut self,
110 button: MouseButton,
111 handler: impl Fn(UpOutRegionEvent, &mut EventContext) + 'static,
112 ) -> Self {
113 self.handlers = self.handlers.on_up_out(button, handler);
114 self
115 }
116
117 pub fn on_drag(
118 mut self,
119 button: MouseButton,
120 handler: impl Fn(DragRegionEvent, &mut EventContext) + 'static,
121 ) -> Self {
122 self.handlers = self.handlers.on_drag(button, handler);
123 self
124 }
125
126 pub fn on_hover(
127 mut self,
128 handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,
129 ) -> Self {
130 self.handlers = self.handlers.on_hover(handler);
131 self
132 }
133
134 pub fn on_scroll(
135 mut self,
136 handler: impl Fn(ScrollWheelRegionEvent, &mut EventContext) + 'static,
137 ) -> Self {
138 self.handlers = self.handlers.on_scroll(handler);
139 self
140 }
141
142 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
143 self.hoverable = is_hoverable;
144 self
145 }
146
147 pub fn with_padding(mut self, padding: Padding) -> Self {
148 self.padding = padding;
149 self
150 }
151
152 fn hit_bounds(&self, bounds: RectF) -> RectF {
153 RectF::from_points(
154 bounds.origin() - vec2f(self.padding.left, self.padding.top),
155 bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
156 )
157 .round_out()
158 }
159}
160
161impl<Tag> Element for MouseEventHandler<Tag> {
162 type LayoutState = ();
163 type PaintState = ();
164
165 fn layout(
166 &mut self,
167 constraint: SizeConstraint,
168 cx: &mut LayoutContext,
169 ) -> (Vector2F, Self::LayoutState) {
170 (self.child.layout(constraint, cx), ())
171 }
172
173 fn paint(
174 &mut self,
175 bounds: RectF,
176 visible_bounds: RectF,
177 _: &mut Self::LayoutState,
178 cx: &mut PaintContext,
179 ) -> Self::PaintState {
180 let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
181 let hit_bounds = self.hit_bounds(visible_bounds);
182 if let Some(style) = self.cursor_style {
183 cx.scene.push_cursor_region(CursorRegion {
184 bounds: hit_bounds,
185 style,
186 });
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 self.child.paint(bounds.origin(), visible_bounds, cx);
202 }
203
204 fn dispatch_event(
205 &mut self,
206 event: &Event,
207 _: RectF,
208 _: RectF,
209 _: &mut Self::LayoutState,
210 _: &mut Self::PaintState,
211 cx: &mut EventContext,
212 ) -> bool {
213 self.child.dispatch_event(event, cx)
214 }
215
216 fn rect_for_text_range(
217 &self,
218 range_utf16: Range<usize>,
219 _: RectF,
220 _: RectF,
221 _: &Self::LayoutState,
222 _: &Self::PaintState,
223 cx: &MeasurementContext,
224 ) -> Option<RectF> {
225 self.child.rect_for_text_range(range_utf16, cx)
226 }
227
228 fn debug(
229 &self,
230 _: RectF,
231 _: &Self::LayoutState,
232 _: &Self::PaintState,
233 cx: &DebugContext,
234 ) -> serde_json::Value {
235 json!({
236 "type": "MouseEventHandler",
237 "child": self.child.debug(cx),
238 })
239 }
240}