1use parking_lot::Mutex;
2use smallvec::SmallVec;
3
4use crate::{
5 point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, Keystroke,
6 Modifiers, Pixels, Point, ViewContext,
7};
8use std::{
9 any::{Any, TypeId},
10 mem,
11 ops::Deref,
12 sync::Arc,
13};
14
15pub trait Interactive: Element {
16 fn interactivity(&mut self) -> &mut Interactivity<Self::ViewState>;
17
18 fn on_mouse_down(
19 mut self,
20 button: MouseButton,
21 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
22 + Send
23 + Sync
24 + 'static,
25 ) -> Self
26 where
27 Self: Sized,
28 {
29 self.interactivity()
30 .mouse_down
31 .push(Arc::new(move |view, event, bounds, phase, cx| {
32 if phase == DispatchPhase::Bubble
33 && event.button == button
34 && bounds.contains_point(&event.position)
35 {
36 handler(view, event, cx)
37 }
38 }));
39 self
40 }
41
42 fn on_mouse_up(
43 mut self,
44 button: MouseButton,
45 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
46 + Send
47 + Sync
48 + 'static,
49 ) -> Self
50 where
51 Self: Sized,
52 {
53 self.interactivity()
54 .mouse_up
55 .push(Arc::new(move |view, event, bounds, phase, cx| {
56 if phase == DispatchPhase::Bubble
57 && event.button == button
58 && bounds.contains_point(&event.position)
59 {
60 handler(view, event, cx)
61 }
62 }));
63 self
64 }
65
66 fn on_mouse_down_out(
67 mut self,
68 button: MouseButton,
69 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
70 + Send
71 + Sync
72 + 'static,
73 ) -> Self
74 where
75 Self: Sized,
76 {
77 self.interactivity()
78 .mouse_down
79 .push(Arc::new(move |view, event, bounds, phase, cx| {
80 if phase == DispatchPhase::Capture
81 && event.button == button
82 && !bounds.contains_point(&event.position)
83 {
84 handler(view, event, cx)
85 }
86 }));
87 self
88 }
89
90 fn on_mouse_up_out(
91 mut self,
92 button: MouseButton,
93 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
94 + Send
95 + Sync
96 + 'static,
97 ) -> Self
98 where
99 Self: Sized,
100 {
101 self.interactivity()
102 .mouse_up
103 .push(Arc::new(move |view, event, bounds, phase, cx| {
104 if phase == DispatchPhase::Capture
105 && event.button == button
106 && !bounds.contains_point(&event.position)
107 {
108 handler(view, event, cx);
109 }
110 }));
111 self
112 }
113
114 fn on_mouse_move(
115 mut self,
116 handler: impl Fn(&mut Self::ViewState, &MouseMoveEvent, &mut ViewContext<Self::ViewState>)
117 + Send
118 + Sync
119 + 'static,
120 ) -> Self
121 where
122 Self: Sized,
123 {
124 self.interactivity()
125 .mouse_move
126 .push(Arc::new(move |view, event, bounds, phase, cx| {
127 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
128 handler(view, event, cx);
129 }
130 }));
131 self
132 }
133
134 fn on_scroll_wheel(
135 mut self,
136 handler: impl Fn(&mut Self::ViewState, &ScrollWheelEvent, &mut ViewContext<Self::ViewState>)
137 + Send
138 + Sync
139 + 'static,
140 ) -> Self
141 where
142 Self: Sized,
143 {
144 self.interactivity()
145 .scroll_wheel
146 .push(Arc::new(move |view, event, bounds, phase, cx| {
147 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
148 handler(view, event, cx);
149 }
150 }));
151 self
152 }
153
154 fn on_key_down(
155 mut self,
156 listener: impl Fn(
157 &mut Self::ViewState,
158 &KeyDownEvent,
159 DispatchPhase,
160 &mut ViewContext<Self::ViewState>,
161 ) + Send
162 + Sync
163 + 'static,
164 ) -> Self
165 where
166 Self: Sized,
167 {
168 self.interactivity().key.push((
169 TypeId::of::<KeyDownEvent>(),
170 Arc::new(move |view, event, _, phase, cx| {
171 let event = event.downcast_ref().unwrap();
172 listener(view, event, phase, cx);
173 None
174 }),
175 ));
176 self
177 }
178
179 fn on_key_up(
180 mut self,
181 listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
182 + Send
183 + Sync
184 + 'static,
185 ) -> Self
186 where
187 Self: Sized,
188 {
189 self.interactivity().key.push((
190 TypeId::of::<KeyUpEvent>(),
191 Arc::new(move |view, event, _, phase, cx| {
192 let event = event.downcast_ref().unwrap();
193 listener(view, event, phase, cx);
194 None
195 }),
196 ));
197 self
198 }
199
200 fn on_action<A: 'static>(
201 mut self,
202 listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
203 + Send
204 + Sync
205 + 'static,
206 ) -> Self
207 where
208 Self: Sized,
209 {
210 self.interactivity().key.push((
211 TypeId::of::<A>(),
212 Arc::new(move |view, event, _, phase, cx| {
213 let event = event.downcast_ref().unwrap();
214 listener(view, event, phase, cx);
215 None
216 }),
217 ));
218 self
219 }
220}
221
222pub trait Click: Interactive {
223 fn on_click(
224 mut self,
225 handler: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
226 + Send
227 + Sync
228 + 'static,
229 ) -> Self
230 where
231 Self: Sized,
232 {
233 self.interactivity()
234 .mouse_click
235 .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
236 self
237 }
238}
239
240#[derive(Clone, Debug, Eq, PartialEq)]
241pub struct KeyDownEvent {
242 pub keystroke: Keystroke,
243 pub is_held: bool,
244}
245
246#[derive(Clone, Debug)]
247pub struct KeyUpEvent {
248 pub keystroke: Keystroke,
249}
250
251#[derive(Clone, Debug, Default)]
252pub struct ModifiersChangedEvent {
253 pub modifiers: Modifiers,
254}
255
256impl Deref for ModifiersChangedEvent {
257 type Target = Modifiers;
258
259 fn deref(&self) -> &Self::Target {
260 &self.modifiers
261 }
262}
263
264/// The phase of a touch motion event.
265/// Based on the winit enum of the same name.
266#[derive(Clone, Copy, Debug)]
267pub enum TouchPhase {
268 Started,
269 Moved,
270 Ended,
271}
272
273#[derive(Clone, Debug, Default)]
274pub struct MouseDownEvent {
275 pub button: MouseButton,
276 pub position: Point<Pixels>,
277 pub modifiers: Modifiers,
278 pub click_count: usize,
279}
280
281#[derive(Clone, Debug, Default)]
282pub struct MouseUpEvent {
283 pub button: MouseButton,
284 pub position: Point<Pixels>,
285 pub modifiers: Modifiers,
286 pub click_count: usize,
287}
288
289#[derive(Clone, Debug, Default)]
290pub struct MouseClickEvent {
291 pub down: MouseDownEvent,
292 pub up: MouseUpEvent,
293}
294
295#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
296pub enum MouseButton {
297 Left,
298 Right,
299 Middle,
300 Navigate(NavigationDirection),
301}
302
303impl MouseButton {
304 pub fn all() -> Vec<Self> {
305 vec![
306 MouseButton::Left,
307 MouseButton::Right,
308 MouseButton::Middle,
309 MouseButton::Navigate(NavigationDirection::Back),
310 MouseButton::Navigate(NavigationDirection::Forward),
311 ]
312 }
313}
314
315impl Default for MouseButton {
316 fn default() -> Self {
317 Self::Left
318 }
319}
320
321#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
322pub enum NavigationDirection {
323 Back,
324 Forward,
325}
326
327impl Default for NavigationDirection {
328 fn default() -> Self {
329 Self::Back
330 }
331}
332
333#[derive(Clone, Debug, Default)]
334pub struct MouseMoveEvent {
335 pub position: Point<Pixels>,
336 pub pressed_button: Option<MouseButton>,
337 pub modifiers: Modifiers,
338}
339
340#[derive(Clone, Debug)]
341pub struct ScrollWheelEvent {
342 pub position: Point<Pixels>,
343 pub delta: ScrollDelta,
344 pub modifiers: Modifiers,
345 pub touch_phase: TouchPhase,
346}
347
348impl Deref for ScrollWheelEvent {
349 type Target = Modifiers;
350
351 fn deref(&self) -> &Self::Target {
352 &self.modifiers
353 }
354}
355
356#[derive(Clone, Copy, Debug)]
357pub enum ScrollDelta {
358 Pixels(Point<Pixels>),
359 Lines(Point<f32>),
360}
361
362impl Default for ScrollDelta {
363 fn default() -> Self {
364 Self::Lines(Default::default())
365 }
366}
367
368impl ScrollDelta {
369 pub fn precise(&self) -> bool {
370 match self {
371 ScrollDelta::Pixels(_) => true,
372 ScrollDelta::Lines(_) => false,
373 }
374 }
375
376 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
377 match self {
378 ScrollDelta::Pixels(delta) => *delta,
379 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
380 }
381 }
382}
383
384#[derive(Clone, Debug, Default)]
385pub struct MouseExitEvent {
386 pub position: Point<Pixels>,
387 pub pressed_button: Option<MouseButton>,
388 pub modifiers: Modifiers,
389}
390
391impl Deref for MouseExitEvent {
392 type Target = Modifiers;
393
394 fn deref(&self) -> &Self::Target {
395 &self.modifiers
396 }
397}
398
399#[derive(Clone, Debug)]
400pub enum InputEvent {
401 KeyDown(KeyDownEvent),
402 KeyUp(KeyUpEvent),
403 ModifiersChanged(ModifiersChangedEvent),
404 MouseDown(MouseDownEvent),
405 MouseUp(MouseUpEvent),
406 MouseMoved(MouseMoveEvent),
407 MouseExited(MouseExitEvent),
408 ScrollWheel(ScrollWheelEvent),
409}
410
411impl InputEvent {
412 pub fn position(&self) -> Option<Point<Pixels>> {
413 match self {
414 InputEvent::KeyDown { .. } => None,
415 InputEvent::KeyUp { .. } => None,
416 InputEvent::ModifiersChanged { .. } => None,
417 InputEvent::MouseDown(event) => Some(event.position),
418 InputEvent::MouseUp(event) => Some(event.position),
419 InputEvent::MouseMoved(event) => Some(event.position),
420 InputEvent::MouseExited(event) => Some(event.position),
421 InputEvent::ScrollWheel(event) => Some(event.position),
422 }
423 }
424
425 pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
426 match self {
427 InputEvent::KeyDown { .. } => None,
428 InputEvent::KeyUp { .. } => None,
429 InputEvent::ModifiersChanged { .. } => None,
430 InputEvent::MouseDown(event) => Some(event),
431 InputEvent::MouseUp(event) => Some(event),
432 InputEvent::MouseMoved(event) => Some(event),
433 InputEvent::MouseExited(event) => Some(event),
434 InputEvent::ScrollWheel(event) => Some(event),
435 }
436 }
437
438 pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
439 match self {
440 InputEvent::KeyDown(event) => Some(event),
441 InputEvent::KeyUp(event) => Some(event),
442 InputEvent::ModifiersChanged(event) => Some(event),
443 InputEvent::MouseDown(_) => None,
444 InputEvent::MouseUp(_) => None,
445 InputEvent::MouseMoved(_) => None,
446 InputEvent::MouseExited(_) => None,
447 InputEvent::ScrollWheel(_) => None,
448 }
449 }
450}
451
452pub struct FocusEvent {
453 pub blurred: Option<FocusHandle>,
454 pub focused: Option<FocusHandle>,
455}
456
457pub type MouseDownListener<V> = Arc<
458 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
459 + Send
460 + Sync
461 + 'static,
462>;
463pub type MouseUpListener<V> = Arc<
464 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
465 + Send
466 + Sync
467 + 'static,
468>;
469pub type MouseClickListener<V> =
470 Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
471
472pub type MouseMoveListener<V> = Arc<
473 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
474 + Send
475 + Sync
476 + 'static,
477>;
478
479pub type ScrollWheelListener<V> = Arc<
480 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
481 + Send
482 + Sync
483 + 'static,
484>;
485
486pub type KeyListener<V> = Arc<
487 dyn Fn(
488 &mut V,
489 &dyn Any,
490 &[&DispatchContext],
491 DispatchPhase,
492 &mut ViewContext<V>,
493 ) -> Option<Box<dyn Action>>
494 + Send
495 + Sync
496 + 'static,
497>;
498
499pub struct Interactivity<V> {
500 pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
501 pub mouse_up: SmallVec<[MouseUpListener<V>; 2]>,
502 pub mouse_click: SmallVec<[MouseClickListener<V>; 2]>,
503 pub mouse_move: SmallVec<[MouseMoveListener<V>; 2]>,
504 pub scroll_wheel: SmallVec<[ScrollWheelListener<V>; 2]>,
505 pub key: SmallVec<[(TypeId, KeyListener<V>); 32]>,
506}
507
508impl<V> Default for Interactivity<V> {
509 fn default() -> Self {
510 Self {
511 mouse_down: SmallVec::new(),
512 mouse_up: SmallVec::new(),
513 mouse_click: SmallVec::new(),
514 mouse_move: SmallVec::new(),
515 scroll_wheel: SmallVec::new(),
516 key: SmallVec::new(),
517 }
518 }
519}
520
521impl<V> Interactivity<V>
522where
523 V: 'static + Send + Sync,
524{
525 pub fn paint(
526 &mut self,
527 bounds: Bounds<Pixels>,
528 pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
529 cx: &mut ViewContext<V>,
530 ) {
531 let click_listeners = mem::take(&mut self.mouse_click);
532
533 let mouse_down = pending_click.lock().clone();
534 if let Some(mouse_down) = mouse_down {
535 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
536 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
537 let mouse_click = MouseClickEvent {
538 down: mouse_down.clone(),
539 up: event.clone(),
540 };
541 for listener in &click_listeners {
542 listener(state, &mouse_click, cx);
543 }
544 }
545
546 *pending_click.lock() = None;
547 });
548 } else {
549 cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
550 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
551 *pending_click.lock() = Some(event.clone());
552 }
553 });
554 }
555
556 for listener in mem::take(&mut self.mouse_down) {
557 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
558 listener(state, event, &bounds, phase, cx);
559 })
560 }
561
562 for listener in mem::take(&mut self.mouse_up) {
563 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
564 listener(state, event, &bounds, phase, cx);
565 })
566 }
567
568 for listener in mem::take(&mut self.mouse_move) {
569 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
570 listener(state, event, &bounds, phase, cx);
571 })
572 }
573
574 for listener in mem::take(&mut self.scroll_wheel) {
575 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
576 listener(state, event, &bounds, phase, cx);
577 })
578 }
579 }
580}