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