1use std::{any::TypeId, fmt::Debug, mem::Discriminant, rc::Rc};
2
3use collections::HashMap;
4
5use pathfinder_geometry::rect::RectF;
6
7use crate::{EventContext, MouseButton};
8
9use super::{
10 mouse_region_event::{
11 ClickRegionEvent, DownOutRegionEvent, DownRegionEvent, DragRegionEvent, HoverRegionEvent,
12 MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
13 },
14 ScrollWheelRegionEvent,
15};
16
17#[derive(Clone)]
18pub struct MouseRegion {
19 pub id: MouseRegionId,
20 pub bounds: RectF,
21 pub handlers: HandlerSet,
22 pub hoverable: bool,
23}
24
25impl MouseRegion {
26 /// Region ID is used to track semantically equivalent mouse regions across render passes.
27 /// e.g. if you have mouse handlers attached to a list item type, then each item of the list
28 /// should pass a different (consistent) region_id. If you have one big region that covers your
29 /// whole component, just pass the view_id again.
30 pub fn new<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
31 Self::from_handlers::<Tag>(view_id, region_id, bounds, Default::default())
32 }
33
34 pub fn handle_all<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
35 Self::from_handlers::<Tag>(view_id, region_id, bounds, HandlerSet::capture_all())
36 }
37
38 pub fn from_handlers<Tag: 'static>(
39 view_id: usize,
40 region_id: usize,
41 bounds: RectF,
42 handlers: HandlerSet,
43 ) -> Self {
44 Self {
45 id: MouseRegionId {
46 view_id,
47 tag: TypeId::of::<Tag>(),
48 region_id,
49 #[cfg(debug_assertions)]
50 tag_type_name: std::any::type_name::<Tag>(),
51 },
52 bounds,
53 handlers,
54 hoverable: true,
55 }
56 }
57
58 pub fn on_down(
59 mut self,
60 button: MouseButton,
61 handler: impl Fn(DownRegionEvent, &mut EventContext) + 'static,
62 ) -> Self {
63 self.handlers = self.handlers.on_down(button, handler);
64 self
65 }
66
67 pub fn on_up(
68 mut self,
69 button: MouseButton,
70 handler: impl Fn(UpRegionEvent, &mut EventContext) + 'static,
71 ) -> Self {
72 self.handlers = self.handlers.on_up(button, handler);
73 self
74 }
75
76 pub fn on_click(
77 mut self,
78 button: MouseButton,
79 handler: impl Fn(ClickRegionEvent, &mut EventContext) + 'static,
80 ) -> Self {
81 self.handlers = self.handlers.on_click(button, handler);
82 self
83 }
84
85 pub fn on_down_out(
86 mut self,
87 button: MouseButton,
88 handler: impl Fn(DownOutRegionEvent, &mut EventContext) + 'static,
89 ) -> Self {
90 self.handlers = self.handlers.on_down_out(button, handler);
91 self
92 }
93
94 pub fn on_up_out(
95 mut self,
96 button: MouseButton,
97 handler: impl Fn(UpOutRegionEvent, &mut EventContext) + 'static,
98 ) -> Self {
99 self.handlers = self.handlers.on_up_out(button, handler);
100 self
101 }
102
103 pub fn on_drag(
104 mut self,
105 button: MouseButton,
106 handler: impl Fn(DragRegionEvent, &mut EventContext) + 'static,
107 ) -> Self {
108 self.handlers = self.handlers.on_drag(button, handler);
109 self
110 }
111
112 pub fn on_hover(
113 mut self,
114 handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,
115 ) -> Self {
116 self.handlers = self.handlers.on_hover(handler);
117 self
118 }
119
120 pub fn on_move(
121 mut self,
122 handler: impl Fn(MoveRegionEvent, &mut EventContext) + 'static,
123 ) -> Self {
124 self.handlers = self.handlers.on_move(handler);
125 self
126 }
127
128 pub fn on_scroll(
129 mut self,
130 handler: impl Fn(ScrollWheelRegionEvent, &mut EventContext) + 'static,
131 ) -> Self {
132 self.handlers = self.handlers.on_scroll(handler);
133 self
134 }
135
136 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
137 self.hoverable = is_hoverable;
138 self
139 }
140}
141
142#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
143pub struct MouseRegionId {
144 view_id: usize,
145 tag: TypeId,
146 region_id: usize,
147 #[cfg(debug_assertions)]
148 tag_type_name: &'static str,
149}
150
151impl MouseRegionId {
152 pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
153 MouseRegionId {
154 view_id,
155 region_id,
156 tag: TypeId::of::<Tag>(),
157 #[cfg(debug_assertions)]
158 tag_type_name: std::any::type_name::<Tag>(),
159 }
160 }
161
162 pub fn view_id(&self) -> usize {
163 self.view_id
164 }
165
166 #[cfg(debug_assertions)]
167 pub fn tag_type_name(&self) -> &'static str {
168 self.tag_type_name
169 }
170}
171
172#[derive(Clone, Default)]
173pub struct HandlerSet {
174 #[allow(clippy::type_complexity)]
175 pub set: HashMap<
176 (Discriminant<MouseRegionEvent>, Option<MouseButton>),
177 Rc<dyn Fn(MouseRegionEvent, &mut EventContext)>,
178 >,
179}
180
181impl HandlerSet {
182 pub fn capture_all() -> Self {
183 #[allow(clippy::type_complexity)]
184 let mut set: HashMap<
185 (Discriminant<MouseRegionEvent>, Option<MouseButton>),
186 Rc<dyn Fn(MouseRegionEvent, &mut EventContext)>,
187 > = Default::default();
188
189 set.insert((MouseRegionEvent::move_disc(), None), Rc::new(|_, _| {}));
190 set.insert((MouseRegionEvent::hover_disc(), None), Rc::new(|_, _| {}));
191 for button in MouseButton::all() {
192 set.insert(
193 (MouseRegionEvent::drag_disc(), Some(button)),
194 Rc::new(|_, _| {}),
195 );
196 set.insert(
197 (MouseRegionEvent::down_disc(), Some(button)),
198 Rc::new(|_, _| {}),
199 );
200 set.insert(
201 (MouseRegionEvent::up_disc(), Some(button)),
202 Rc::new(|_, _| {}),
203 );
204 set.insert(
205 (MouseRegionEvent::click_disc(), Some(button)),
206 Rc::new(|_, _| {}),
207 );
208 set.insert(
209 (MouseRegionEvent::down_out_disc(), Some(button)),
210 Rc::new(|_, _| {}),
211 );
212 set.insert(
213 (MouseRegionEvent::up_out_disc(), Some(button)),
214 Rc::new(|_, _| {}),
215 );
216 }
217 set.insert(
218 (MouseRegionEvent::scroll_wheel_disc(), None),
219 Rc::new(|_, _| {}),
220 );
221
222 HandlerSet { set }
223 }
224
225 pub fn get(
226 &self,
227 key: &(Discriminant<MouseRegionEvent>, Option<MouseButton>),
228 ) -> Option<Rc<dyn Fn(MouseRegionEvent, &mut EventContext)>> {
229 self.set.get(key).cloned()
230 }
231
232 pub fn contains_handler(
233 &self,
234 event: Discriminant<MouseRegionEvent>,
235 button: Option<MouseButton>,
236 ) -> bool {
237 self.set.contains_key(&(event, button))
238 }
239
240 pub fn on_move(
241 mut self,
242 handler: impl Fn(MoveRegionEvent, &mut EventContext) + 'static,
243 ) -> Self {
244 self.set.insert((MouseRegionEvent::move_disc(), None),
245 Rc::new(move |region_event, cx| {
246 if let MouseRegionEvent::Move(e) = region_event {
247 handler(e, cx);
248 } else {
249 panic!(
250 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}",
251 region_event);
252 }
253 }));
254 self
255 }
256
257 pub fn on_down(
258 mut self,
259 button: MouseButton,
260 handler: impl Fn(DownRegionEvent, &mut EventContext) + 'static,
261 ) -> Self {
262 self.set.insert((MouseRegionEvent::down_disc(), Some(button)),
263 Rc::new(move |region_event, cx| {
264 if let MouseRegionEvent::Down(e) = region_event {
265 handler(e, cx);
266 } else {
267 panic!(
268 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Down, found {:?}",
269 region_event);
270 }
271 }));
272 self
273 }
274
275 pub fn on_up(
276 mut self,
277 button: MouseButton,
278 handler: impl Fn(UpRegionEvent, &mut EventContext) + 'static,
279 ) -> Self {
280 self.set.insert((MouseRegionEvent::up_disc(), Some(button)),
281 Rc::new(move |region_event, cx| {
282 if let MouseRegionEvent::Up(e) = region_event {
283 handler(e, cx);
284 } else {
285 panic!(
286 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Up, found {:?}",
287 region_event);
288 }
289 }));
290 self
291 }
292
293 pub fn on_click(
294 mut self,
295 button: MouseButton,
296 handler: impl Fn(ClickRegionEvent, &mut EventContext) + 'static,
297 ) -> Self {
298 self.set.insert((MouseRegionEvent::click_disc(), Some(button)),
299 Rc::new(move |region_event, cx| {
300 if let MouseRegionEvent::Click(e) = region_event {
301 handler(e, cx);
302 } else {
303 panic!(
304 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Click, found {:?}",
305 region_event);
306 }
307 }));
308 self
309 }
310
311 pub fn on_down_out(
312 mut self,
313 button: MouseButton,
314 handler: impl Fn(DownOutRegionEvent, &mut EventContext) + 'static,
315 ) -> Self {
316 self.set.insert((MouseRegionEvent::down_out_disc(), Some(button)),
317 Rc::new(move |region_event, cx| {
318 if let MouseRegionEvent::DownOut(e) = region_event {
319 handler(e, cx);
320 } else {
321 panic!(
322 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DownOut, found {:?}",
323 region_event);
324 }
325 }));
326 self
327 }
328
329 pub fn on_up_out(
330 mut self,
331 button: MouseButton,
332 handler: impl Fn(UpOutRegionEvent, &mut EventContext) + 'static,
333 ) -> Self {
334 self.set.insert((MouseRegionEvent::up_out_disc(), Some(button)),
335 Rc::new(move |region_event, cx| {
336 if let MouseRegionEvent::UpOut(e) = region_event {
337 handler(e, cx);
338 } else {
339 panic!(
340 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::UpOut, found {:?}",
341 region_event);
342 }
343 }));
344 self
345 }
346
347 pub fn on_drag(
348 mut self,
349 button: MouseButton,
350 handler: impl Fn(DragRegionEvent, &mut EventContext) + 'static,
351 ) -> Self {
352 self.set.insert((MouseRegionEvent::drag_disc(), Some(button)),
353 Rc::new(move |region_event, cx| {
354 if let MouseRegionEvent::Drag(e) = region_event {
355 handler(e, cx);
356 } else {
357 panic!(
358 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Drag, found {:?}",
359 region_event);
360 }
361 }));
362 self
363 }
364
365 pub fn on_hover(
366 mut self,
367 handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,
368 ) -> Self {
369 self.set.insert((MouseRegionEvent::hover_disc(), None),
370 Rc::new(move |region_event, cx| {
371 if let MouseRegionEvent::Hover(e) = region_event {
372 handler(e, cx);
373 } else {
374 panic!(
375 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Hover, found {:?}",
376 region_event);
377 }
378 }));
379 self
380 }
381
382 pub fn on_scroll(
383 mut self,
384 handler: impl Fn(ScrollWheelRegionEvent, &mut EventContext) + 'static,
385 ) -> Self {
386 self.set.insert((MouseRegionEvent::scroll_wheel_disc(), None),
387 Rc::new(move |region_event, cx| {
388 if let MouseRegionEvent::ScrollWheel(e) = region_event {
389 handler(e, cx);
390 } else {
391 panic!(
392 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
393 region_event
394 );
395 }
396 }));
397 self
398 }
399}