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