1use crate::{
2 point, px, view, Action, AnyDrag, AppContext, BorrowWindow, Bounds, DispatchContext,
3 DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Overflow,
4 Pixels, Point, SharedString, Size, Style, StyleRefinement, ViewContext,
5};
6use collections::HashMap;
7use derive_more::{Deref, DerefMut};
8use parking_lot::Mutex;
9use refineable::Refineable;
10use smallvec::SmallVec;
11use std::{
12 any::{Any, TypeId},
13 fmt::Debug,
14 marker::PhantomData,
15 ops::Deref,
16 sync::Arc,
17};
18
19pub trait StatelessInteractive: Element {
20 fn stateless_interactivity(&mut self) -> &mut StatelessInteraction<Self::ViewState>;
21
22 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
23 where
24 Self: Sized,
25 {
26 self.stateless_interactivity().hover_style = f(StyleRefinement::default());
27 self
28 }
29
30 fn group_hover(
31 mut self,
32 group_name: impl Into<SharedString>,
33 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
34 ) -> Self
35 where
36 Self: Sized,
37 {
38 self.stateless_interactivity().group_hover_style = Some(GroupStyle {
39 group: group_name.into(),
40 style: f(StyleRefinement::default()),
41 });
42 self
43 }
44
45 fn on_mouse_down(
46 mut self,
47 button: MouseButton,
48 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
49 + Send
50 + Sync
51 + 'static,
52 ) -> Self
53 where
54 Self: Sized,
55 {
56 self.stateless_interactivity()
57 .mouse_down_listeners
58 .push(Arc::new(move |view, event, bounds, phase, cx| {
59 if phase == DispatchPhase::Bubble
60 && event.button == button
61 && bounds.contains_point(&event.position)
62 {
63 handler(view, event, cx)
64 }
65 }));
66 self
67 }
68
69 fn on_mouse_up(
70 mut self,
71 button: MouseButton,
72 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
73 + Send
74 + Sync
75 + 'static,
76 ) -> Self
77 where
78 Self: Sized,
79 {
80 self.stateless_interactivity()
81 .mouse_up_listeners
82 .push(Arc::new(move |view, event, bounds, phase, cx| {
83 if phase == DispatchPhase::Bubble
84 && event.button == button
85 && bounds.contains_point(&event.position)
86 {
87 handler(view, event, cx)
88 }
89 }));
90 self
91 }
92
93 fn on_mouse_down_out(
94 mut self,
95 button: MouseButton,
96 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
97 + Send
98 + Sync
99 + 'static,
100 ) -> Self
101 where
102 Self: Sized,
103 {
104 self.stateless_interactivity()
105 .mouse_down_listeners
106 .push(Arc::new(move |view, event, bounds, phase, cx| {
107 if phase == DispatchPhase::Capture
108 && event.button == button
109 && !bounds.contains_point(&event.position)
110 {
111 handler(view, event, cx)
112 }
113 }));
114 self
115 }
116
117 fn on_mouse_up_out(
118 mut self,
119 button: MouseButton,
120 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
121 + Send
122 + Sync
123 + 'static,
124 ) -> Self
125 where
126 Self: Sized,
127 {
128 self.stateless_interactivity()
129 .mouse_up_listeners
130 .push(Arc::new(move |view, event, bounds, phase, cx| {
131 if phase == DispatchPhase::Capture
132 && event.button == button
133 && !bounds.contains_point(&event.position)
134 {
135 handler(view, event, cx);
136 }
137 }));
138 self
139 }
140
141 fn on_mouse_move(
142 mut self,
143 handler: impl Fn(&mut Self::ViewState, &MouseMoveEvent, &mut ViewContext<Self::ViewState>)
144 + Send
145 + Sync
146 + 'static,
147 ) -> Self
148 where
149 Self: Sized,
150 {
151 self.stateless_interactivity()
152 .mouse_move_listeners
153 .push(Arc::new(move |view, event, bounds, phase, cx| {
154 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
155 handler(view, event, cx);
156 }
157 }));
158 self
159 }
160
161 fn on_scroll_wheel(
162 mut self,
163 handler: impl Fn(&mut Self::ViewState, &ScrollWheelEvent, &mut ViewContext<Self::ViewState>)
164 + Send
165 + Sync
166 + 'static,
167 ) -> Self
168 where
169 Self: Sized,
170 {
171 self.stateless_interactivity()
172 .scroll_wheel_listeners
173 .push(Arc::new(move |view, event, bounds, phase, cx| {
174 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
175 handler(view, event, cx);
176 }
177 }));
178 self
179 }
180
181 fn context<C>(mut self, context: C) -> Self
182 where
183 Self: Sized,
184 C: TryInto<DispatchContext>,
185 C::Error: Debug,
186 {
187 self.stateless_interactivity().dispatch_context =
188 context.try_into().expect("invalid dispatch context");
189 self
190 }
191
192 fn on_action<A: 'static>(
193 mut self,
194 listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
195 + Send
196 + Sync
197 + 'static,
198 ) -> Self
199 where
200 Self: Sized,
201 {
202 self.stateless_interactivity().key_listeners.push((
203 TypeId::of::<A>(),
204 Arc::new(move |view, event, _, phase, cx| {
205 let event = event.downcast_ref().unwrap();
206 listener(view, event, phase, cx);
207 None
208 }),
209 ));
210 self
211 }
212
213 fn on_key_down(
214 mut self,
215 listener: impl Fn(
216 &mut Self::ViewState,
217 &KeyDownEvent,
218 DispatchPhase,
219 &mut ViewContext<Self::ViewState>,
220 ) + Send
221 + Sync
222 + 'static,
223 ) -> Self
224 where
225 Self: Sized,
226 {
227 self.stateless_interactivity().key_listeners.push((
228 TypeId::of::<KeyDownEvent>(),
229 Arc::new(move |view, event, _, phase, cx| {
230 let event = event.downcast_ref().unwrap();
231 listener(view, event, phase, cx);
232 None
233 }),
234 ));
235 self
236 }
237
238 fn on_key_up(
239 mut self,
240 listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
241 + Send
242 + Sync
243 + 'static,
244 ) -> Self
245 where
246 Self: Sized,
247 {
248 self.stateless_interactivity().key_listeners.push((
249 TypeId::of::<KeyUpEvent>(),
250 Arc::new(move |view, event, _, phase, cx| {
251 let event = event.downcast_ref().unwrap();
252 listener(view, event, phase, cx);
253 None
254 }),
255 ));
256 self
257 }
258}
259
260pub trait StatefulInteractive: StatelessInteractive {
261 fn stateful_interaction(&mut self) -> &mut StatefulInteraction<Self::ViewState>;
262
263 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
264 where
265 Self: Sized,
266 {
267 self.stateful_interaction().active_style = f(StyleRefinement::default());
268 self
269 }
270
271 fn group_active(
272 mut self,
273 group_name: impl Into<SharedString>,
274 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
275 ) -> Self
276 where
277 Self: Sized,
278 {
279 self.stateful_interaction().group_active_style = Some(GroupStyle {
280 group: group_name.into(),
281 style: f(StyleRefinement::default()),
282 });
283 self
284 }
285
286 fn on_click(
287 mut self,
288 listener: impl Fn(&mut Self::ViewState, &ClickEvent, &mut ViewContext<Self::ViewState>)
289 + Send
290 + Sync
291 + 'static,
292 ) -> Self
293 where
294 Self: Sized,
295 {
296 self.stateful_interaction()
297 .click_listeners
298 .push(Arc::new(move |view, event, cx| listener(view, event, cx)));
299 self
300 }
301
302 fn on_drag<S, R, E>(
303 mut self,
304 listener: impl Fn(
305 &mut Self::ViewState,
306 &mut ViewContext<Self::ViewState>,
307 ) -> Drag<S, R, Self::ViewState, E>
308 + Send
309 + Sync
310 + 'static,
311 ) -> Self
312 where
313 Self: Sized,
314 S: 'static + Send + Sync,
315 R: 'static + Fn(&mut Self::ViewState, &mut ViewContext<Self::ViewState>) -> E + Send + Sync,
316 E: Element<ViewState = Self::ViewState>,
317 {
318 debug_assert!(
319 self.stateful_interaction().drag_listener.is_none(),
320 "calling on_drag more than once on the same element is not supported"
321 );
322 self.stateful_interaction().drag_listener =
323 Some(Arc::new(move |view_state, cursor_offset, cx| {
324 let drag = listener(view_state, cx);
325 let view_handle = cx.handle().upgrade().unwrap();
326 let drag_handle_view = view(view_handle, move |view_state, cx| {
327 (drag.render_drag_handle)(view_state, cx)
328 })
329 .into_any();
330 AnyDrag {
331 drag_handle_view,
332 cursor_offset,
333 state: Box::new(drag.state),
334 state_type: TypeId::of::<S>(),
335 }
336 }));
337 self
338 }
339
340 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
341 where
342 Self: Sized,
343 {
344 self.stateful_interaction()
345 .drag_over_styles
346 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
347 self
348 }
349
350 fn group_drag_over<S: 'static>(
351 mut self,
352 group_name: impl Into<SharedString>,
353 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
354 ) -> Self
355 where
356 Self: Sized,
357 {
358 self.stateful_interaction().group_drag_over_styles.push((
359 TypeId::of::<S>(),
360 GroupStyle {
361 group: group_name.into(),
362 style: f(StyleRefinement::default()),
363 },
364 ));
365 self
366 }
367}
368
369pub trait ElementInteraction<V: 'static + Send + Sync>: 'static + Send + Sync {
370 fn as_stateless(&self) -> &StatelessInteraction<V>;
371 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V>;
372 fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
373 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
374
375 fn initialize<R>(
376 &mut self,
377 cx: &mut ViewContext<V>,
378 f: impl FnOnce(&mut ViewContext<V>) -> R,
379 ) -> R {
380 if let Some(stateful) = self.as_stateful_mut() {
381 cx.with_element_id(stateful.id.clone(), |global_id, cx| {
382 stateful.key_listeners.push((
383 TypeId::of::<KeyDownEvent>(),
384 Arc::new(move |_, key_down, context, phase, cx| {
385 if phase == DispatchPhase::Bubble {
386 let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
387 if let KeyMatch::Some(action) =
388 cx.match_keystroke(&global_id, &key_down.keystroke, context)
389 {
390 return Some(action);
391 }
392 }
393
394 None
395 }),
396 ));
397 let result = stateful.stateless.initialize(cx, f);
398 stateful.key_listeners.pop();
399 result
400 })
401 } else {
402 let stateless = self.as_stateless();
403 cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
404 cx.with_key_listeners(&stateless.key_listeners, f)
405 })
406 }
407 }
408
409 fn refine_style(
410 &self,
411 style: &mut Style,
412 bounds: Bounds<Pixels>,
413 element_state: &InteractiveElementState,
414 cx: &mut ViewContext<V>,
415 ) {
416 let mouse_position = cx.mouse_position();
417 let stateless = self.as_stateless();
418 if let Some(group_hover) = stateless.group_hover_style.as_ref() {
419 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
420 if group_bounds.contains_point(&mouse_position) {
421 style.refine(&group_hover.style);
422 }
423 }
424 }
425 if bounds.contains_point(&mouse_position) {
426 style.refine(&stateless.hover_style);
427 }
428
429 if let Some(drag) = cx.active_drag.take() {
430 for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
431 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
432 if *state_type == drag.state_type
433 && group_bounds.contains_point(&mouse_position)
434 {
435 style.refine(&group_drag_style.style);
436 }
437 }
438 }
439
440 for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
441 if *state_type == drag.state_type && bounds.contains_point(&mouse_position) {
442 style.refine(drag_over_style);
443 }
444 }
445
446 cx.active_drag = Some(drag);
447 }
448
449 if let Some(stateful) = self.as_stateful() {
450 let active_state = element_state.active_state.lock();
451 if active_state.group {
452 if let Some(group_style) = stateful.group_active_style.as_ref() {
453 style.refine(&group_style.style);
454 }
455 }
456 if active_state.element {
457 style.refine(&stateful.active_style);
458 }
459 }
460 }
461
462 fn paint(
463 &mut self,
464 bounds: Bounds<Pixels>,
465 content_size: Size<Pixels>,
466 overflow: Point<Overflow>,
467 element_state: &mut InteractiveElementState,
468 cx: &mut ViewContext<V>,
469 ) {
470 let stateless = self.as_stateless();
471 for listener in stateless.mouse_down_listeners.iter().cloned() {
472 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
473 listener(state, event, &bounds, phase, cx);
474 })
475 }
476
477 for listener in stateless.mouse_up_listeners.iter().cloned() {
478 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
479 listener(state, event, &bounds, phase, cx);
480 })
481 }
482
483 for listener in stateless.mouse_move_listeners.iter().cloned() {
484 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
485 listener(state, event, &bounds, phase, cx);
486 })
487 }
488
489 for listener in stateless.scroll_wheel_listeners.iter().cloned() {
490 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
491 listener(state, event, &bounds, phase, cx);
492 })
493 }
494
495 let hover_group_bounds = stateless
496 .group_hover_style
497 .as_ref()
498 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
499
500 if let Some(group_bounds) = hover_group_bounds {
501 let hovered = group_bounds.contains_point(&cx.mouse_position());
502 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
503 if phase == DispatchPhase::Capture {
504 if group_bounds.contains_point(&event.position) != hovered {
505 cx.notify();
506 }
507 }
508 });
509 }
510
511 if stateless.hover_style.is_some()
512 || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
513 {
514 let hovered = bounds.contains_point(&cx.mouse_position());
515 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
516 if phase == DispatchPhase::Capture {
517 if bounds.contains_point(&event.position) != hovered {
518 cx.notify();
519 }
520 }
521 });
522 }
523
524 if let Some(stateful) = self.as_stateful() {
525 let click_listeners = stateful.click_listeners.clone();
526 let drag_listener = stateful.drag_listener.clone();
527
528 if !click_listeners.is_empty() || drag_listener.is_some() {
529 let pending_mouse_down = element_state.pending_mouse_down.clone();
530 let mouse_down = pending_mouse_down.lock().clone();
531 if let Some(mouse_down) = mouse_down {
532 if let Some(drag_listener) = drag_listener {
533 let active_state = element_state.active_state.clone();
534 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
535 if cx.active_drag.is_some() {
536 if phase == DispatchPhase::Capture {
537 cx.notify();
538 }
539 } else if phase == DispatchPhase::Bubble
540 && bounds.contains_point(&event.position)
541 {
542 let cursor_offset = event.position - bounds.origin;
543 let any_drag = drag_listener(view_state, cursor_offset, cx);
544 *active_state.lock() = ActiveState::default();
545 cx.start_drag(any_drag);
546 cx.stop_propagation();
547 }
548 });
549 }
550
551 cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
552 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
553 {
554 let mouse_click = ClickEvent {
555 down: mouse_down.clone(),
556 up: event.clone(),
557 };
558 for listener in &click_listeners {
559 listener(view_state, &mouse_click, cx);
560 }
561 }
562
563 if cx.active_drag.is_some() {
564 cx.end_drag();
565 }
566 *pending_mouse_down.lock() = None;
567 });
568 } else {
569 cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
570 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
571 {
572 *pending_mouse_down.lock() = Some(event.clone());
573 }
574 });
575 }
576 }
577
578 let active_state = element_state.active_state.clone();
579 if active_state.lock().is_none() {
580 let active_group_bounds = stateful
581 .group_active_style
582 .as_ref()
583 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
584 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
585 if phase == DispatchPhase::Bubble {
586 let group = active_group_bounds
587 .map_or(false, |bounds| bounds.contains_point(&down.position));
588 let element = bounds.contains_point(&down.position);
589 if group || element {
590 *active_state.lock() = ActiveState { group, element };
591 cx.notify();
592 }
593 }
594 });
595 } else {
596 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
597 if phase == DispatchPhase::Capture {
598 *active_state.lock() = ActiveState::default();
599 cx.notify();
600 }
601 });
602 }
603
604 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
605 let scroll_offset = element_state
606 .scroll_offset
607 .get_or_insert_with(Arc::default)
608 .clone();
609 let line_height = cx.line_height();
610 let scroll_max = (content_size - bounds.size).max(&Size::default());
611
612 cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
613 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
614 let mut scroll_offset = scroll_offset.lock();
615 let old_scroll_offset = *scroll_offset;
616 let delta = event.delta.pixel_delta(line_height);
617
618 if overflow.x == Overflow::Scroll {
619 scroll_offset.x =
620 (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
621 }
622
623 if overflow.y == Overflow::Scroll {
624 scroll_offset.y =
625 (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
626 }
627
628 if *scroll_offset != old_scroll_offset {
629 cx.notify();
630 cx.stop_propagation();
631 }
632 }
633 });
634 }
635 }
636 }
637}
638
639#[derive(Deref, DerefMut)]
640pub struct StatefulInteraction<V: 'static + Send + Sync> {
641 pub id: ElementId,
642 #[deref]
643 #[deref_mut]
644 stateless: StatelessInteraction<V>,
645 click_listeners: SmallVec<[ClickListener<V>; 2]>,
646 active_style: StyleRefinement,
647 group_active_style: Option<GroupStyle>,
648 drag_listener: Option<DragListener<V>>,
649}
650
651impl<V> ElementInteraction<V> for StatefulInteraction<V>
652where
653 V: 'static + Send + Sync,
654{
655 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
656 Some(self)
657 }
658
659 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
660 Some(self)
661 }
662
663 fn as_stateless(&self) -> &StatelessInteraction<V> {
664 &self.stateless
665 }
666
667 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
668 &mut self.stateless
669 }
670}
671
672impl<V> From<ElementId> for StatefulInteraction<V>
673where
674 V: 'static + Send + Sync,
675{
676 fn from(id: ElementId) -> Self {
677 Self {
678 id,
679 stateless: StatelessInteraction::default(),
680 click_listeners: SmallVec::new(),
681 drag_listener: None,
682 active_style: StyleRefinement::default(),
683 group_active_style: None,
684 }
685 }
686}
687
688pub struct StatelessInteraction<V> {
689 pub dispatch_context: DispatchContext,
690 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
691 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
692 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
693 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
694 pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
695 pub hover_style: StyleRefinement,
696 pub group_hover_style: Option<GroupStyle>,
697 drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
698 group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
699}
700
701impl<V> StatelessInteraction<V>
702where
703 V: 'static + Send + Sync,
704{
705 pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
706 StatefulInteraction {
707 id: id.into(),
708 stateless: self,
709 click_listeners: SmallVec::new(),
710 drag_listener: None,
711 active_style: StyleRefinement::default(),
712 group_active_style: None,
713 }
714 }
715}
716
717pub struct GroupStyle {
718 pub group: SharedString,
719 pub style: StyleRefinement,
720}
721
722#[derive(Default)]
723pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
724
725impl GroupBounds {
726 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
727 cx.default_global_mut::<Self>()
728 .0
729 .get(name)
730 .and_then(|bounds_stack| bounds_stack.last())
731 .cloned()
732 }
733
734 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
735 cx.default_global_mut::<Self>()
736 .0
737 .entry(name)
738 .or_default()
739 .push(bounds);
740 }
741
742 pub fn pop(name: &SharedString, cx: &mut AppContext) {
743 cx.default_global_mut::<Self>()
744 .0
745 .get_mut(name)
746 .unwrap()
747 .pop();
748 }
749}
750
751#[derive(Copy, Clone, Default, Eq, PartialEq)]
752struct ActiveState {
753 pub group: bool,
754 pub element: bool,
755}
756
757impl ActiveState {
758 pub fn is_none(&self) -> bool {
759 !self.group && !self.element
760 }
761}
762
763#[derive(Default)]
764pub struct InteractiveElementState {
765 active_state: Arc<Mutex<ActiveState>>,
766 pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
767 scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
768}
769
770impl InteractiveElementState {
771 pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
772 self.scroll_offset
773 .as_ref()
774 .map(|offset| offset.lock().clone())
775 }
776}
777
778impl<V> Default for StatelessInteraction<V> {
779 fn default() -> Self {
780 Self {
781 dispatch_context: DispatchContext::default(),
782 mouse_down_listeners: SmallVec::new(),
783 mouse_up_listeners: SmallVec::new(),
784 mouse_move_listeners: SmallVec::new(),
785 scroll_wheel_listeners: SmallVec::new(),
786 key_listeners: SmallVec::new(),
787 hover_style: StyleRefinement::default(),
788 group_hover_style: None,
789 drag_over_styles: SmallVec::new(),
790 group_drag_over_styles: SmallVec::new(),
791 }
792 }
793}
794
795impl<V> ElementInteraction<V> for StatelessInteraction<V>
796where
797 V: 'static + Send + Sync,
798{
799 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
800 None
801 }
802
803 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
804 None
805 }
806
807 fn as_stateless(&self) -> &StatelessInteraction<V> {
808 self
809 }
810
811 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
812 self
813 }
814}
815
816#[derive(Clone, Debug, Eq, PartialEq)]
817pub struct KeyDownEvent {
818 pub keystroke: Keystroke,
819 pub is_held: bool,
820}
821
822#[derive(Clone, Debug)]
823pub struct KeyUpEvent {
824 pub keystroke: Keystroke,
825}
826
827#[derive(Clone, Debug, Default)]
828pub struct ModifiersChangedEvent {
829 pub modifiers: Modifiers,
830}
831
832impl Deref for ModifiersChangedEvent {
833 type Target = Modifiers;
834
835 fn deref(&self) -> &Self::Target {
836 &self.modifiers
837 }
838}
839
840/// The phase of a touch motion event.
841/// Based on the winit enum of the same name.
842#[derive(Clone, Copy, Debug)]
843pub enum TouchPhase {
844 Started,
845 Moved,
846 Ended,
847}
848
849#[derive(Clone, Debug, Default)]
850pub struct MouseDownEvent {
851 pub button: MouseButton,
852 pub position: Point<Pixels>,
853 pub modifiers: Modifiers,
854 pub click_count: usize,
855}
856
857#[derive(Clone, Debug, Default)]
858pub struct MouseUpEvent {
859 pub button: MouseButton,
860 pub position: Point<Pixels>,
861 pub modifiers: Modifiers,
862 pub click_count: usize,
863}
864
865#[derive(Clone, Debug, Default)]
866pub struct ClickEvent {
867 pub down: MouseDownEvent,
868 pub up: MouseUpEvent,
869}
870
871pub struct Drag<S, R, V, E>
872where
873 S: 'static + Send + Sync,
874 R: Fn(&mut V, &mut ViewContext<V>) -> E,
875 V: 'static + Send + Sync,
876 E: Element<ViewState = V>,
877{
878 pub state: S,
879 pub render_drag_handle: R,
880 view_type: PhantomData<V>,
881}
882
883impl<S, R, V, E> Drag<S, R, V, E>
884where
885 S: 'static + Send + Sync,
886 R: Fn(&mut V, &mut ViewContext<V>) -> E + Send + Sync,
887 V: 'static + Send + Sync,
888 E: Element<ViewState = V>,
889{
890 pub fn new(state: S, render_drag_handle: R) -> Self {
891 Drag {
892 state,
893 render_drag_handle,
894 view_type: PhantomData,
895 }
896 }
897}
898
899#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
900pub enum MouseButton {
901 Left,
902 Right,
903 Middle,
904 Navigate(NavigationDirection),
905}
906
907impl MouseButton {
908 pub fn all() -> Vec<Self> {
909 vec![
910 MouseButton::Left,
911 MouseButton::Right,
912 MouseButton::Middle,
913 MouseButton::Navigate(NavigationDirection::Back),
914 MouseButton::Navigate(NavigationDirection::Forward),
915 ]
916 }
917}
918
919impl Default for MouseButton {
920 fn default() -> Self {
921 Self::Left
922 }
923}
924
925#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
926pub enum NavigationDirection {
927 Back,
928 Forward,
929}
930
931impl Default for NavigationDirection {
932 fn default() -> Self {
933 Self::Back
934 }
935}
936
937#[derive(Clone, Debug, Default)]
938pub struct MouseMoveEvent {
939 pub position: Point<Pixels>,
940 pub pressed_button: Option<MouseButton>,
941 pub modifiers: Modifiers,
942}
943
944#[derive(Clone, Debug)]
945pub struct ScrollWheelEvent {
946 pub position: Point<Pixels>,
947 pub delta: ScrollDelta,
948 pub modifiers: Modifiers,
949 pub touch_phase: TouchPhase,
950}
951
952impl Deref for ScrollWheelEvent {
953 type Target = Modifiers;
954
955 fn deref(&self) -> &Self::Target {
956 &self.modifiers
957 }
958}
959
960#[derive(Clone, Copy, Debug)]
961pub enum ScrollDelta {
962 Pixels(Point<Pixels>),
963 Lines(Point<f32>),
964}
965
966impl Default for ScrollDelta {
967 fn default() -> Self {
968 Self::Lines(Default::default())
969 }
970}
971
972impl ScrollDelta {
973 pub fn precise(&self) -> bool {
974 match self {
975 ScrollDelta::Pixels(_) => true,
976 ScrollDelta::Lines(_) => false,
977 }
978 }
979
980 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
981 match self {
982 ScrollDelta::Pixels(delta) => *delta,
983 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
984 }
985 }
986}
987
988#[derive(Clone, Debug, Default)]
989pub struct MouseExitEvent {
990 pub position: Point<Pixels>,
991 pub pressed_button: Option<MouseButton>,
992 pub modifiers: Modifiers,
993}
994
995impl Deref for MouseExitEvent {
996 type Target = Modifiers;
997
998 fn deref(&self) -> &Self::Target {
999 &self.modifiers
1000 }
1001}
1002
1003#[derive(Clone, Debug)]
1004pub enum InputEvent {
1005 KeyDown(KeyDownEvent),
1006 KeyUp(KeyUpEvent),
1007 ModifiersChanged(ModifiersChangedEvent),
1008 MouseDown(MouseDownEvent),
1009 MouseUp(MouseUpEvent),
1010 MouseMoved(MouseMoveEvent),
1011 MouseExited(MouseExitEvent),
1012 ScrollWheel(ScrollWheelEvent),
1013}
1014
1015impl InputEvent {
1016 pub fn position(&self) -> Option<Point<Pixels>> {
1017 match self {
1018 InputEvent::KeyDown { .. } => None,
1019 InputEvent::KeyUp { .. } => None,
1020 InputEvent::ModifiersChanged { .. } => None,
1021 InputEvent::MouseDown(event) => Some(event.position),
1022 InputEvent::MouseUp(event) => Some(event.position),
1023 InputEvent::MouseMoved(event) => Some(event.position),
1024 InputEvent::MouseExited(event) => Some(event.position),
1025 InputEvent::ScrollWheel(event) => Some(event.position),
1026 }
1027 }
1028
1029 pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1030 match self {
1031 InputEvent::KeyDown { .. } => None,
1032 InputEvent::KeyUp { .. } => None,
1033 InputEvent::ModifiersChanged { .. } => None,
1034 InputEvent::MouseDown(event) => Some(event),
1035 InputEvent::MouseUp(event) => Some(event),
1036 InputEvent::MouseMoved(event) => Some(event),
1037 InputEvent::MouseExited(event) => Some(event),
1038 InputEvent::ScrollWheel(event) => Some(event),
1039 }
1040 }
1041
1042 pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1043 match self {
1044 InputEvent::KeyDown(event) => Some(event),
1045 InputEvent::KeyUp(event) => Some(event),
1046 InputEvent::ModifiersChanged(event) => Some(event),
1047 InputEvent::MouseDown(_) => None,
1048 InputEvent::MouseUp(_) => None,
1049 InputEvent::MouseMoved(_) => None,
1050 InputEvent::MouseExited(_) => None,
1051 InputEvent::ScrollWheel(_) => None,
1052 }
1053 }
1054}
1055
1056pub struct FocusEvent {
1057 pub blurred: Option<FocusHandle>,
1058 pub focused: Option<FocusHandle>,
1059}
1060
1061pub type MouseDownListener<V> = Arc<
1062 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1063 + Send
1064 + Sync
1065 + 'static,
1066>;
1067pub type MouseUpListener<V> = Arc<
1068 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1069 + Send
1070 + Sync
1071 + 'static,
1072>;
1073
1074pub type MouseMoveListener<V> = Arc<
1075 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1076 + Send
1077 + Sync
1078 + 'static,
1079>;
1080
1081pub type ScrollWheelListener<V> = Arc<
1082 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1083 + Send
1084 + Sync
1085 + 'static,
1086>;
1087
1088pub type ClickListener<V> =
1089 Arc<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
1090
1091pub(crate) type DragListener<V> =
1092 Arc<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + Send + Sync + 'static>;
1093
1094pub type KeyListener<V> = Arc<
1095 dyn Fn(
1096 &mut V,
1097 &dyn Any,
1098 &[&DispatchContext],
1099 DispatchPhase,
1100 &mut ViewContext<V>,
1101 ) -> Option<Box<dyn Action>>
1102 + Send
1103 + Sync
1104 + 'static,
1105>;