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