1use crate::{
2 point, px, view, Action, AnyBox, AnyDrag, AppContext, BorrowWindow, Bounds, Component,
3 DispatchContext, DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke,
4 Modifiers, Overflow, 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 mem,
16 ops::Deref,
17 path::PathBuf,
18 sync::Arc,
19};
20
21const DRAG_THRESHOLD: f64 = 2.;
22
23pub trait StatelessInteractive<V: 'static>: Element<V> {
24 fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V>;
25
26 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
27 where
28 Self: Sized,
29 {
30 self.stateless_interaction().hover_style = f(StyleRefinement::default());
31 self
32 }
33
34 fn group_hover(
35 mut self,
36 group_name: impl Into<SharedString>,
37 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
38 ) -> Self
39 where
40 Self: Sized,
41 {
42 self.stateless_interaction().group_hover_style = Some(GroupStyle {
43 group: group_name.into(),
44 style: f(StyleRefinement::default()),
45 });
46 self
47 }
48
49 fn on_mouse_down(
50 mut self,
51 button: MouseButton,
52 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + Send + 'static,
53 ) -> Self
54 where
55 Self: Sized,
56 {
57 self.stateless_interaction()
58 .mouse_down_listeners
59 .push(Box::new(move |view, event, bounds, phase, cx| {
60 if phase == DispatchPhase::Bubble
61 && event.button == button
62 && bounds.contains_point(&event.position)
63 {
64 handler(view, event, cx)
65 }
66 }));
67 self
68 }
69
70 fn on_mouse_up(
71 mut self,
72 button: MouseButton,
73 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + Send + 'static,
74 ) -> Self
75 where
76 Self: Sized,
77 {
78 self.stateless_interaction()
79 .mouse_up_listeners
80 .push(Box::new(move |view, event, bounds, phase, cx| {
81 if phase == DispatchPhase::Bubble
82 && event.button == button
83 && bounds.contains_point(&event.position)
84 {
85 handler(view, event, cx)
86 }
87 }));
88 self
89 }
90
91 fn on_mouse_down_out(
92 mut self,
93 button: MouseButton,
94 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + Send + 'static,
95 ) -> Self
96 where
97 Self: Sized,
98 {
99 self.stateless_interaction()
100 .mouse_down_listeners
101 .push(Box::new(move |view, event, bounds, phase, cx| {
102 if phase == DispatchPhase::Capture
103 && event.button == button
104 && !bounds.contains_point(&event.position)
105 {
106 handler(view, event, cx)
107 }
108 }));
109 self
110 }
111
112 fn on_mouse_up_out(
113 mut self,
114 button: MouseButton,
115 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + Send + 'static,
116 ) -> Self
117 where
118 Self: Sized,
119 {
120 self.stateless_interaction()
121 .mouse_up_listeners
122 .push(Box::new(move |view, event, bounds, phase, cx| {
123 if phase == DispatchPhase::Capture
124 && event.button == button
125 && !bounds.contains_point(&event.position)
126 {
127 handler(view, event, cx);
128 }
129 }));
130 self
131 }
132
133 fn on_mouse_move(
134 mut self,
135 handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + Send + 'static,
136 ) -> Self
137 where
138 Self: Sized,
139 {
140 self.stateless_interaction()
141 .mouse_move_listeners
142 .push(Box::new(move |view, event, bounds, phase, cx| {
143 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
144 handler(view, event, cx);
145 }
146 }));
147 self
148 }
149
150 fn on_scroll_wheel(
151 mut self,
152 handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + Send + 'static,
153 ) -> Self
154 where
155 Self: Sized,
156 {
157 self.stateless_interaction()
158 .scroll_wheel_listeners
159 .push(Box::new(move |view, event, bounds, phase, cx| {
160 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
161 handler(view, event, cx);
162 }
163 }));
164 self
165 }
166
167 fn context<C>(mut self, context: C) -> Self
168 where
169 Self: Sized,
170 C: TryInto<DispatchContext>,
171 C::Error: Debug,
172 {
173 self.stateless_interaction().dispatch_context =
174 context.try_into().expect("invalid dispatch context");
175 self
176 }
177
178 fn on_action<A: 'static>(
179 mut self,
180 listener: impl Fn(&mut V, &A, DispatchPhase, &mut ViewContext<V>) + Send + 'static,
181 ) -> Self
182 where
183 Self: Sized,
184 {
185 self.stateless_interaction().key_listeners.push((
186 TypeId::of::<A>(),
187 Box::new(move |view, event, _, phase, cx| {
188 let event = event.downcast_ref().unwrap();
189 listener(view, event, phase, cx);
190 None
191 }),
192 ));
193 self
194 }
195
196 fn on_key_down(
197 mut self,
198 listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + Send + 'static,
199 ) -> Self
200 where
201 Self: Sized,
202 {
203 self.stateless_interaction().key_listeners.push((
204 TypeId::of::<KeyDownEvent>(),
205 Box::new(move |view, event, _, phase, cx| {
206 let event = event.downcast_ref().unwrap();
207 listener(view, event, phase, cx);
208 None
209 }),
210 ));
211 self
212 }
213
214 fn on_key_up(
215 mut self,
216 listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + Send + 'static,
217 ) -> Self
218 where
219 Self: Sized,
220 {
221 self.stateless_interaction().key_listeners.push((
222 TypeId::of::<KeyUpEvent>(),
223 Box::new(move |view, event, _, phase, cx| {
224 let event = event.downcast_ref().unwrap();
225 listener(view, event, phase, cx);
226 None
227 }),
228 ));
229 self
230 }
231
232 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
233 where
234 Self: Sized,
235 {
236 self.stateless_interaction()
237 .drag_over_styles
238 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
239 self
240 }
241
242 fn group_drag_over<S: 'static>(
243 mut self,
244 group_name: impl Into<SharedString>,
245 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
246 ) -> Self
247 where
248 Self: Sized,
249 {
250 self.stateless_interaction().group_drag_over_styles.push((
251 TypeId::of::<S>(),
252 GroupStyle {
253 group: group_name.into(),
254 style: f(StyleRefinement::default()),
255 },
256 ));
257 self
258 }
259
260 fn on_drop<S: 'static>(
261 mut self,
262 listener: impl Fn(&mut V, S, &mut ViewContext<V>) + Send + 'static,
263 ) -> Self
264 where
265 Self: Sized,
266 {
267 self.stateless_interaction().drop_listeners.push((
268 TypeId::of::<S>(),
269 Box::new(move |view, drag_state, cx| {
270 listener(view, *drag_state.downcast().unwrap(), cx);
271 }),
272 ));
273 self
274 }
275}
276
277pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
278 fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V>;
279
280 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
281 where
282 Self: Sized,
283 {
284 self.stateful_interaction().active_style = f(StyleRefinement::default());
285 self
286 }
287
288 fn group_active(
289 mut self,
290 group_name: impl Into<SharedString>,
291 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
292 ) -> Self
293 where
294 Self: Sized,
295 {
296 self.stateful_interaction().group_active_style = Some(GroupStyle {
297 group: group_name.into(),
298 style: f(StyleRefinement::default()),
299 });
300 self
301 }
302
303 fn on_click(
304 mut self,
305 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + Send + 'static,
306 ) -> Self
307 where
308 Self: Sized,
309 {
310 self.stateful_interaction()
311 .click_listeners
312 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
313 self
314 }
315
316 fn on_drag<S, R, E>(
317 mut self,
318 listener: impl Fn(&mut V, &mut ViewContext<V>) -> Drag<S, R, V, E> + Send + 'static,
319 ) -> Self
320 where
321 Self: Sized,
322 S: Any + Send,
323 R: Fn(&mut V, &mut ViewContext<V>) -> E,
324 R: 'static + Send,
325 E: Component<V>,
326 {
327 debug_assert!(
328 self.stateful_interaction().drag_listener.is_none(),
329 "calling on_drag more than once on the same element is not supported"
330 );
331 self.stateful_interaction().drag_listener =
332 Some(Box::new(move |view_state, cursor_offset, cx| {
333 let drag = listener(view_state, cx);
334 let view_handle = cx.handle().upgrade().unwrap();
335 let drag_handle_view = Some(
336 view(view_handle, 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>;