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: Any + Send + Sync,
364 R: Fn(&mut Self::ViewState, &mut ViewContext<Self::ViewState>) -> E,
365 R: 'static + 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>: '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> {
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: 'static> ElementInteraction<V> for StatefulInteraction<V> {
697 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
698 Some(self)
699 }
700
701 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
702 Some(self)
703 }
704
705 fn as_stateless(&self) -> &StatelessInteraction<V> {
706 &self.stateless
707 }
708
709 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
710 &mut self.stateless
711 }
712}
713
714impl<V> From<ElementId> for StatefulInteraction<V> {
715 fn from(id: ElementId) -> Self {
716 Self {
717 id,
718 stateless: StatelessInteraction::default(),
719 click_listeners: SmallVec::new(),
720 drag_listener: None,
721 active_style: StyleRefinement::default(),
722 group_active_style: None,
723 }
724 }
725}
726
727type DropListener<V> = dyn Fn(&mut V, AnyBox, &mut ViewContext<V>) + 'static + Send + Sync;
728
729pub struct StatelessInteraction<V> {
730 pub dispatch_context: DispatchContext,
731 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
732 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
733 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
734 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
735 pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
736 pub hover_style: StyleRefinement,
737 pub group_hover_style: Option<GroupStyle>,
738 drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
739 group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
740 drop_listeners: SmallVec<[(TypeId, Arc<DropListener<V>>); 2]>,
741}
742
743impl<V> StatelessInteraction<V> {
744 pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
745 StatefulInteraction {
746 id: id.into(),
747 stateless: self,
748 click_listeners: SmallVec::new(),
749 drag_listener: None,
750 active_style: StyleRefinement::default(),
751 group_active_style: None,
752 }
753 }
754}
755
756pub struct GroupStyle {
757 pub group: SharedString,
758 pub style: StyleRefinement,
759}
760
761#[derive(Default)]
762pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
763
764impl GroupBounds {
765 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
766 cx.default_global_mut::<Self>()
767 .0
768 .get(name)
769 .and_then(|bounds_stack| bounds_stack.last())
770 .cloned()
771 }
772
773 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
774 cx.default_global_mut::<Self>()
775 .0
776 .entry(name)
777 .or_default()
778 .push(bounds);
779 }
780
781 pub fn pop(name: &SharedString, cx: &mut AppContext) {
782 cx.default_global_mut::<Self>()
783 .0
784 .get_mut(name)
785 .unwrap()
786 .pop();
787 }
788}
789
790#[derive(Copy, Clone, Default, Eq, PartialEq)]
791struct ActiveState {
792 pub group: bool,
793 pub element: bool,
794}
795
796impl ActiveState {
797 pub fn is_none(&self) -> bool {
798 !self.group && !self.element
799 }
800}
801
802#[derive(Default)]
803pub struct InteractiveElementState {
804 active_state: Arc<Mutex<ActiveState>>,
805 pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
806 scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
807}
808
809impl InteractiveElementState {
810 pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
811 self.scroll_offset
812 .as_ref()
813 .map(|offset| offset.lock().clone())
814 }
815}
816
817impl<V> Default for StatelessInteraction<V> {
818 fn default() -> Self {
819 Self {
820 dispatch_context: DispatchContext::default(),
821 mouse_down_listeners: SmallVec::new(),
822 mouse_up_listeners: SmallVec::new(),
823 mouse_move_listeners: SmallVec::new(),
824 scroll_wheel_listeners: SmallVec::new(),
825 key_listeners: SmallVec::new(),
826 hover_style: StyleRefinement::default(),
827 group_hover_style: None,
828 drag_over_styles: SmallVec::new(),
829 group_drag_over_styles: SmallVec::new(),
830 drop_listeners: SmallVec::new(),
831 }
832 }
833}
834
835impl<V: 'static> ElementInteraction<V> for StatelessInteraction<V> {
836 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
837 None
838 }
839
840 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
841 None
842 }
843
844 fn as_stateless(&self) -> &StatelessInteraction<V> {
845 self
846 }
847
848 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
849 self
850 }
851}
852
853#[derive(Clone, Debug, Eq, PartialEq)]
854pub struct KeyDownEvent {
855 pub keystroke: Keystroke,
856 pub is_held: bool,
857}
858
859#[derive(Clone, Debug)]
860pub struct KeyUpEvent {
861 pub keystroke: Keystroke,
862}
863
864#[derive(Clone, Debug, Default)]
865pub struct ModifiersChangedEvent {
866 pub modifiers: Modifiers,
867}
868
869impl Deref for ModifiersChangedEvent {
870 type Target = Modifiers;
871
872 fn deref(&self) -> &Self::Target {
873 &self.modifiers
874 }
875}
876
877/// The phase of a touch motion event.
878/// Based on the winit enum of the same name.
879#[derive(Clone, Copy, Debug)]
880pub enum TouchPhase {
881 Started,
882 Moved,
883 Ended,
884}
885
886#[derive(Clone, Debug, Default)]
887pub struct MouseDownEvent {
888 pub button: MouseButton,
889 pub position: Point<Pixels>,
890 pub modifiers: Modifiers,
891 pub click_count: usize,
892}
893
894#[derive(Clone, Debug, Default)]
895pub struct MouseUpEvent {
896 pub button: MouseButton,
897 pub position: Point<Pixels>,
898 pub modifiers: Modifiers,
899 pub click_count: usize,
900}
901
902#[derive(Clone, Debug, Default)]
903pub struct ClickEvent {
904 pub down: MouseDownEvent,
905 pub up: MouseUpEvent,
906}
907
908pub struct Drag<S, R, V, E>
909where
910 R: Fn(&mut V, &mut ViewContext<V>) -> E,
911 E: Element<ViewState = V>,
912{
913 pub state: S,
914 pub render_drag_handle: R,
915 view_type: PhantomData<V>,
916}
917
918impl<S, R, V, E> Drag<S, R, V, E>
919where
920 R: Fn(&mut V, &mut ViewContext<V>) -> E,
921 E: Element<ViewState = V>,
922{
923 pub fn new(state: S, render_drag_handle: R) -> Self {
924 Drag {
925 state,
926 render_drag_handle,
927 view_type: PhantomData,
928 }
929 }
930}
931
932#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
933pub enum MouseButton {
934 Left,
935 Right,
936 Middle,
937 Navigate(NavigationDirection),
938}
939
940impl MouseButton {
941 pub fn all() -> Vec<Self> {
942 vec![
943 MouseButton::Left,
944 MouseButton::Right,
945 MouseButton::Middle,
946 MouseButton::Navigate(NavigationDirection::Back),
947 MouseButton::Navigate(NavigationDirection::Forward),
948 ]
949 }
950}
951
952impl Default for MouseButton {
953 fn default() -> Self {
954 Self::Left
955 }
956}
957
958#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
959pub enum NavigationDirection {
960 Back,
961 Forward,
962}
963
964impl Default for NavigationDirection {
965 fn default() -> Self {
966 Self::Back
967 }
968}
969
970#[derive(Clone, Debug, Default)]
971pub struct MouseMoveEvent {
972 pub position: Point<Pixels>,
973 pub pressed_button: Option<MouseButton>,
974 pub modifiers: Modifiers,
975}
976
977#[derive(Clone, Debug)]
978pub struct ScrollWheelEvent {
979 pub position: Point<Pixels>,
980 pub delta: ScrollDelta,
981 pub modifiers: Modifiers,
982 pub touch_phase: TouchPhase,
983}
984
985impl Deref for ScrollWheelEvent {
986 type Target = Modifiers;
987
988 fn deref(&self) -> &Self::Target {
989 &self.modifiers
990 }
991}
992
993#[derive(Clone, Copy, Debug)]
994pub enum ScrollDelta {
995 Pixels(Point<Pixels>),
996 Lines(Point<f32>),
997}
998
999impl Default for ScrollDelta {
1000 fn default() -> Self {
1001 Self::Lines(Default::default())
1002 }
1003}
1004
1005impl ScrollDelta {
1006 pub fn precise(&self) -> bool {
1007 match self {
1008 ScrollDelta::Pixels(_) => true,
1009 ScrollDelta::Lines(_) => false,
1010 }
1011 }
1012
1013 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
1014 match self {
1015 ScrollDelta::Pixels(delta) => *delta,
1016 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
1017 }
1018 }
1019}
1020
1021#[derive(Clone, Debug, Default)]
1022pub struct MouseExitEvent {
1023 pub position: Point<Pixels>,
1024 pub pressed_button: Option<MouseButton>,
1025 pub modifiers: Modifiers,
1026}
1027
1028impl Deref for MouseExitEvent {
1029 type Target = Modifiers;
1030
1031 fn deref(&self) -> &Self::Target {
1032 &self.modifiers
1033 }
1034}
1035
1036#[derive(Clone, Debug)]
1037pub enum InputEvent {
1038 KeyDown(KeyDownEvent),
1039 KeyUp(KeyUpEvent),
1040 ModifiersChanged(ModifiersChangedEvent),
1041 MouseDown(MouseDownEvent),
1042 MouseUp(MouseUpEvent),
1043 MouseMoved(MouseMoveEvent),
1044 MouseExited(MouseExitEvent),
1045 ScrollWheel(ScrollWheelEvent),
1046}
1047
1048impl InputEvent {
1049 pub fn position(&self) -> Option<Point<Pixels>> {
1050 match self {
1051 InputEvent::KeyDown { .. } => None,
1052 InputEvent::KeyUp { .. } => None,
1053 InputEvent::ModifiersChanged { .. } => None,
1054 InputEvent::MouseDown(event) => Some(event.position),
1055 InputEvent::MouseUp(event) => Some(event.position),
1056 InputEvent::MouseMoved(event) => Some(event.position),
1057 InputEvent::MouseExited(event) => Some(event.position),
1058 InputEvent::ScrollWheel(event) => Some(event.position),
1059 }
1060 }
1061
1062 pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1063 match self {
1064 InputEvent::KeyDown { .. } => None,
1065 InputEvent::KeyUp { .. } => None,
1066 InputEvent::ModifiersChanged { .. } => None,
1067 InputEvent::MouseDown(event) => Some(event),
1068 InputEvent::MouseUp(event) => Some(event),
1069 InputEvent::MouseMoved(event) => Some(event),
1070 InputEvent::MouseExited(event) => Some(event),
1071 InputEvent::ScrollWheel(event) => Some(event),
1072 }
1073 }
1074
1075 pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1076 match self {
1077 InputEvent::KeyDown(event) => Some(event),
1078 InputEvent::KeyUp(event) => Some(event),
1079 InputEvent::ModifiersChanged(event) => Some(event),
1080 InputEvent::MouseDown(_) => None,
1081 InputEvent::MouseUp(_) => None,
1082 InputEvent::MouseMoved(_) => None,
1083 InputEvent::MouseExited(_) => None,
1084 InputEvent::ScrollWheel(_) => None,
1085 }
1086 }
1087}
1088
1089pub struct FocusEvent {
1090 pub blurred: Option<FocusHandle>,
1091 pub focused: Option<FocusHandle>,
1092}
1093
1094pub type MouseDownListener<V> = Arc<
1095 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1096 + Send
1097 + Sync
1098 + 'static,
1099>;
1100pub type MouseUpListener<V> = Arc<
1101 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1102 + Send
1103 + Sync
1104 + 'static,
1105>;
1106
1107pub type MouseMoveListener<V> = Arc<
1108 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1109 + Send
1110 + Sync
1111 + 'static,
1112>;
1113
1114pub type ScrollWheelListener<V> = Arc<
1115 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1116 + Send
1117 + Sync
1118 + 'static,
1119>;
1120
1121pub type ClickListener<V> =
1122 Arc<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
1123
1124pub(crate) type DragListener<V> =
1125 Arc<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + Send + Sync + 'static>;
1126
1127pub type KeyListener<V> = Arc<
1128 dyn Fn(
1129 &mut V,
1130 &dyn Any,
1131 &[&DispatchContext],
1132 DispatchPhase,
1133 &mut ViewContext<V>,
1134 ) -> Option<Box<dyn Action>>
1135 + Send
1136 + Sync
1137 + 'static,
1138>;