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