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