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