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