1use crate::{
2 point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
3 BorrowWindow, Bounds, ClickEvent, Component, DispatchPhase, Element, ElementId, FocusEvent,
4 FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
5 MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, Point, Render, ScrollWheelEvent,
6 SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
7};
8use collections::HashMap;
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 sync::Arc,
18 time::Duration,
19};
20use taffy::style::Overflow;
21use util::ResultExt;
22
23const DRAG_THRESHOLD: f64 = 2.;
24const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
25const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
26
27pub struct GroupStyle {
28 pub group: SharedString,
29 pub style: StyleRefinement,
30}
31
32pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
33 fn interactivity(&mut self) -> &mut Interactivity<V>;
34
35 fn group(mut self, group: impl Into<SharedString>) -> Self {
36 self.interactivity().group = Some(group.into());
37 self
38 }
39
40 fn id(mut self, id: impl Into<ElementId>) -> Stateful<V, Self> {
41 self.interactivity().element_id = Some(id.into());
42
43 Stateful {
44 element: self,
45 view_type: PhantomData,
46 }
47 }
48
49 fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<V, Self> {
50 self.interactivity().focusable = true;
51 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
52 Focusable {
53 element: self,
54 view_type: PhantomData,
55 }
56 }
57
58 fn key_context<C, E>(mut self, key_context: C) -> Self
59 where
60 C: TryInto<KeyContext, Error = E>,
61 E: Debug,
62 {
63 if let Some(key_context) = key_context.try_into().log_err() {
64 self.interactivity().key_context = key_context;
65 }
66 self
67 }
68
69 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
70 self.interactivity().hover_style = f(StyleRefinement::default());
71 self
72 }
73
74 fn group_hover(
75 mut self,
76 group_name: impl Into<SharedString>,
77 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
78 ) -> Self {
79 self.interactivity().group_hover_style = Some(GroupStyle {
80 group: group_name.into(),
81 style: f(StyleRefinement::default()),
82 });
83 self
84 }
85
86 fn on_mouse_down(
87 mut self,
88 button: MouseButton,
89 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
90 ) -> Self {
91 self.interactivity().mouse_down_listeners.push(Box::new(
92 move |view, event, bounds, phase, cx| {
93 if phase == DispatchPhase::Bubble
94 && event.button == button
95 && bounds.contains_point(&event.position)
96 {
97 handler(view, event, cx)
98 }
99 },
100 ));
101 self
102 }
103
104 fn on_any_mouse_down(
105 mut self,
106 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
107 ) -> Self {
108 self.interactivity().mouse_down_listeners.push(Box::new(
109 move |view, event, bounds, phase, cx| {
110 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
111 handler(view, event, cx)
112 }
113 },
114 ));
115 self
116 }
117
118 fn on_mouse_up(
119 mut self,
120 button: MouseButton,
121 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
122 ) -> Self {
123 self.interactivity().mouse_up_listeners.push(Box::new(
124 move |view, event, bounds, phase, cx| {
125 if phase == DispatchPhase::Bubble
126 && event.button == button
127 && bounds.contains_point(&event.position)
128 {
129 handler(view, event, cx)
130 }
131 },
132 ));
133 self
134 }
135
136 fn on_any_mouse_up(
137 mut self,
138 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
139 ) -> Self {
140 self.interactivity().mouse_up_listeners.push(Box::new(
141 move |view, event, bounds, phase, cx| {
142 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
143 handler(view, event, cx)
144 }
145 },
146 ));
147 self
148 }
149
150 fn on_mouse_down_out(
151 mut self,
152 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
153 ) -> Self {
154 self.interactivity().mouse_down_listeners.push(Box::new(
155 move |view, event, bounds, phase, cx| {
156 if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
157 handler(view, event, cx)
158 }
159 },
160 ));
161 self
162 }
163
164 fn on_mouse_up_out(
165 mut self,
166 button: MouseButton,
167 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
168 ) -> Self {
169 self.interactivity().mouse_up_listeners.push(Box::new(
170 move |view, event, bounds, phase, cx| {
171 if phase == DispatchPhase::Capture
172 && event.button == button
173 && !bounds.contains_point(&event.position)
174 {
175 handler(view, event, cx);
176 }
177 },
178 ));
179 self
180 }
181
182 fn on_mouse_move(
183 mut self,
184 handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
185 ) -> Self {
186 self.interactivity().mouse_move_listeners.push(Box::new(
187 move |view, event, bounds, phase, cx| {
188 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
189 handler(view, event, cx);
190 }
191 },
192 ));
193 self
194 }
195
196 fn on_scroll_wheel(
197 mut self,
198 handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
199 ) -> Self {
200 self.interactivity().scroll_wheel_listeners.push(Box::new(
201 move |view, event, bounds, phase, cx| {
202 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
203 handler(view, event, cx);
204 }
205 },
206 ));
207 self
208 }
209
210 /// Capture the given action, fires during the capture phase
211 fn capture_action<A: Action>(
212 mut self,
213 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
214 ) -> Self {
215 self.interactivity().action_listeners.push((
216 TypeId::of::<A>(),
217 Box::new(move |view, action, phase, cx| {
218 let action = action.downcast_ref().unwrap();
219 if phase == DispatchPhase::Capture {
220 listener(view, action, cx)
221 }
222 }),
223 ));
224 self
225 }
226
227 /// Add a listener for the given action, fires during the bubble event phase
228 fn on_action<A: Action>(
229 mut self,
230 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
231 ) -> Self {
232 // NOTE: this debug assert has the side-effect of working around
233 // a bug where a crate consisting only of action definitions does
234 // not register the actions in debug builds:
235 //
236 // https://github.com/rust-lang/rust/issues/47384
237 // https://github.com/mmastrac/rust-ctor/issues/280
238 //
239 // if we are relying on this side-effect still, removing the debug_assert!
240 // likely breaks the command_palette tests.
241 debug_assert!(
242 A::is_registered(),
243 "{:?} is not registered as an action",
244 A::qualified_name()
245 );
246 self.interactivity().action_listeners.push((
247 TypeId::of::<A>(),
248 Box::new(move |view, action, phase, cx| {
249 let action = action.downcast_ref().unwrap();
250 if phase == DispatchPhase::Bubble {
251 listener(view, action, cx)
252 }
253 }),
254 ));
255 self
256 }
257
258 fn on_key_down(
259 mut self,
260 listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
261 ) -> Self {
262 self.interactivity()
263 .key_down_listeners
264 .push(Box::new(move |view, event, phase, cx| {
265 listener(view, event, phase, cx)
266 }));
267 self
268 }
269
270 fn on_key_up(
271 mut self,
272 listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
273 ) -> Self {
274 self.interactivity()
275 .key_up_listeners
276 .push(Box::new(move |view, event, phase, cx| {
277 listener(view, event, phase, cx)
278 }));
279 self
280 }
281
282 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
283 self.interactivity()
284 .drag_over_styles
285 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
286 self
287 }
288
289 fn group_drag_over<S: 'static>(
290 mut self,
291 group_name: impl Into<SharedString>,
292 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
293 ) -> Self {
294 self.interactivity().group_drag_over_styles.push((
295 TypeId::of::<S>(),
296 GroupStyle {
297 group: group_name.into(),
298 style: f(StyleRefinement::default()),
299 },
300 ));
301 self
302 }
303
304 fn on_drop<W: 'static>(
305 mut self,
306 listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
307 ) -> Self {
308 self.interactivity().drop_listeners.push((
309 TypeId::of::<W>(),
310 Box::new(move |view, dragged_view, cx| {
311 listener(view, dragged_view.downcast().unwrap(), cx);
312 }),
313 ));
314 self
315 }
316}
317
318pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveComponent<V> {
319 fn focusable(mut self) -> Focusable<V, Self> {
320 self.interactivity().focusable = true;
321 Focusable {
322 element: self,
323 view_type: PhantomData,
324 }
325 }
326
327 fn overflow_scroll(mut self) -> Self {
328 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
329 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
330 self
331 }
332
333 fn overflow_x_scroll(mut self) -> Self {
334 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
335 self
336 }
337
338 fn overflow_y_scroll(mut self) -> Self {
339 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
340 self
341 }
342
343 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
344 where
345 Self: Sized,
346 {
347 self.interactivity().active_style = f(StyleRefinement::default());
348 self
349 }
350
351 fn group_active(
352 mut self,
353 group_name: impl Into<SharedString>,
354 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
355 ) -> Self
356 where
357 Self: Sized,
358 {
359 self.interactivity().group_active_style = Some(GroupStyle {
360 group: group_name.into(),
361 style: f(StyleRefinement::default()),
362 });
363 self
364 }
365
366 fn on_click(
367 mut self,
368 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
369 ) -> Self
370 where
371 Self: Sized,
372 {
373 self.interactivity()
374 .click_listeners
375 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
376 self
377 }
378
379 fn on_drag<W>(
380 mut self,
381 listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
382 ) -> Self
383 where
384 Self: Sized,
385 W: 'static + Render,
386 {
387 debug_assert!(
388 self.interactivity().drag_listener.is_none(),
389 "calling on_drag more than once on the same element is not supported"
390 );
391 self.interactivity().drag_listener =
392 Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
393 view: listener(view_state, cx).into(),
394 cursor_offset,
395 }));
396 self
397 }
398
399 fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
400 where
401 Self: Sized,
402 {
403 debug_assert!(
404 self.interactivity().hover_listener.is_none(),
405 "calling on_hover more than once on the same element is not supported"
406 );
407 self.interactivity().hover_listener = Some(Box::new(listener));
408 self
409 }
410
411 fn tooltip<W>(
412 mut self,
413 build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
414 ) -> Self
415 where
416 Self: Sized,
417 W: 'static + Render,
418 {
419 debug_assert!(
420 self.interactivity().tooltip_builder.is_none(),
421 "calling tooltip more than once on the same element is not supported"
422 );
423 self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
424 build_tooltip(view_state, cx).into()
425 }));
426
427 self
428 }
429}
430
431pub trait FocusableComponent<V: 'static>: InteractiveComponent<V> {
432 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
433 where
434 Self: Sized,
435 {
436 self.interactivity().focus_style = f(StyleRefinement::default());
437 self
438 }
439
440 fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
441 where
442 Self: Sized,
443 {
444 self.interactivity().focus_in_style = f(StyleRefinement::default());
445 self
446 }
447
448 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
449 where
450 Self: Sized,
451 {
452 self.interactivity().in_focus_style = f(StyleRefinement::default());
453 self
454 }
455
456 fn on_focus(
457 mut self,
458 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
459 ) -> Self
460 where
461 Self: Sized,
462 {
463 self.interactivity().focus_listeners.push(Box::new(
464 move |view, focus_handle, event, cx| {
465 if event.focused.as_ref() == Some(focus_handle) {
466 listener(view, event, cx)
467 }
468 },
469 ));
470 self
471 }
472
473 fn on_blur(
474 mut self,
475 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
476 ) -> Self
477 where
478 Self: Sized,
479 {
480 self.interactivity().focus_listeners.push(Box::new(
481 move |view, focus_handle, event, cx| {
482 if event.blurred.as_ref() == Some(focus_handle) {
483 listener(view, event, cx)
484 }
485 },
486 ));
487 self
488 }
489
490 fn on_focus_in(
491 mut self,
492 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
493 ) -> Self
494 where
495 Self: Sized,
496 {
497 self.interactivity().focus_listeners.push(Box::new(
498 move |view, focus_handle, event, cx| {
499 let descendant_blurred = event
500 .blurred
501 .as_ref()
502 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
503 let descendant_focused = event
504 .focused
505 .as_ref()
506 .map_or(false, |focused| focus_handle.contains(focused, cx));
507
508 if !descendant_blurred && descendant_focused {
509 listener(view, event, cx)
510 }
511 },
512 ));
513 self
514 }
515
516 fn on_focus_out(
517 mut self,
518 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
519 ) -> Self
520 where
521 Self: Sized,
522 {
523 self.interactivity().focus_listeners.push(Box::new(
524 move |view, focus_handle, event, cx| {
525 let descendant_blurred = event
526 .blurred
527 .as_ref()
528 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
529 let descendant_focused = event
530 .focused
531 .as_ref()
532 .map_or(false, |focused| focus_handle.contains(focused, cx));
533 if descendant_blurred && !descendant_focused {
534 listener(view, event, cx)
535 }
536 },
537 ));
538 self
539 }
540}
541
542pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
543
544pub type FocusListener<V> =
545 Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
546
547pub type MouseDownListener<V> = Box<
548 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
549>;
550pub type MouseUpListener<V> = Box<
551 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
552>;
553
554pub type MouseMoveListener<V> = Box<
555 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
556>;
557
558pub type ScrollWheelListener<V> = Box<
559 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
560 + 'static,
561>;
562
563pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
564
565pub type DragListener<V> =
566 Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
567
568type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
569
570pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
571
572pub type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
573
574pub type KeyDownListener<V> =
575 Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
576
577pub type KeyUpListener<V> =
578 Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
579
580pub type ActionListener<V> =
581 Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
582
583pub fn div<V: 'static>() -> Div<V> {
584 Div {
585 interactivity: Interactivity::default(),
586 children: SmallVec::default(),
587 }
588}
589
590pub struct Div<V> {
591 interactivity: Interactivity<V>,
592 children: SmallVec<[AnyElement<V>; 2]>,
593}
594
595impl<V> Styled for Div<V> {
596 fn style(&mut self) -> &mut StyleRefinement {
597 &mut self.interactivity.base_style
598 }
599}
600
601impl<V: 'static> InteractiveComponent<V> for Div<V> {
602 fn interactivity(&mut self) -> &mut Interactivity<V> {
603 &mut self.interactivity
604 }
605}
606
607impl<V: 'static> ParentComponent<V> for Div<V> {
608 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
609 &mut self.children
610 }
611}
612
613impl<V: 'static> Element<V> for Div<V> {
614 type ElementState = DivState;
615
616 fn element_id(&self) -> Option<ElementId> {
617 self.interactivity.element_id.clone()
618 }
619
620 fn initialize(
621 &mut self,
622 view_state: &mut V,
623 element_state: Option<Self::ElementState>,
624 cx: &mut ViewContext<V>,
625 ) -> Self::ElementState {
626 let interactive_state = self
627 .interactivity
628 .initialize(element_state.map(|s| s.interactive_state), cx);
629
630 for child in &mut self.children {
631 child.initialize(view_state, cx);
632 }
633
634 DivState {
635 interactive_state,
636 child_layout_ids: SmallVec::new(),
637 }
638 }
639
640 fn layout(
641 &mut self,
642 view_state: &mut V,
643 element_state: &mut Self::ElementState,
644 cx: &mut ViewContext<V>,
645 ) -> crate::LayoutId {
646 let mut interactivity = mem::take(&mut self.interactivity);
647 let layout_id =
648 interactivity.layout(&mut element_state.interactive_state, cx, |style, cx| {
649 cx.with_text_style(style.text_style().cloned(), |cx| {
650 element_state.child_layout_ids = self
651 .children
652 .iter_mut()
653 .map(|child| child.layout(view_state, cx))
654 .collect::<SmallVec<_>>();
655 cx.request_layout(&style, element_state.child_layout_ids.iter().copied())
656 })
657 });
658 self.interactivity = interactivity;
659 layout_id
660 }
661
662 fn paint(
663 &mut self,
664 bounds: Bounds<Pixels>,
665 view_state: &mut V,
666 element_state: &mut Self::ElementState,
667 cx: &mut ViewContext<V>,
668 ) {
669 let mut child_min = point(Pixels::MAX, Pixels::MAX);
670 let mut child_max = Point::default();
671 let content_size = if element_state.child_layout_ids.is_empty() {
672 bounds.size
673 } else {
674 for child_layout_id in &element_state.child_layout_ids {
675 let child_bounds = cx.layout_bounds(*child_layout_id);
676 child_min = child_min.min(&child_bounds.origin);
677 child_max = child_max.max(&child_bounds.lower_right());
678 }
679 (child_max - child_min).into()
680 };
681
682 let mut interactivity = mem::take(&mut self.interactivity);
683 interactivity.paint(
684 bounds,
685 content_size,
686 &mut element_state.interactive_state,
687 cx,
688 |style, scroll_offset, cx| {
689 if style.visibility == Visibility::Hidden {
690 return;
691 }
692
693 let z_index = style.z_index.unwrap_or(0);
694
695 cx.with_z_index(z_index, |cx| {
696 cx.with_z_index(0, |cx| {
697 style.paint(bounds, cx);
698 });
699 cx.with_z_index(1, |cx| {
700 cx.with_text_style(style.text_style().cloned(), |cx| {
701 cx.with_content_mask(style.overflow_mask(bounds), |cx| {
702 cx.with_element_offset(scroll_offset, |cx| {
703 for child in &mut self.children {
704 child.paint(view_state, cx);
705 }
706 })
707 })
708 })
709 })
710 })
711 },
712 );
713 self.interactivity = interactivity;
714 }
715}
716
717impl<V: 'static> Component<V> for Div<V> {
718 fn render(self) -> AnyElement<V> {
719 AnyElement::new(self)
720 }
721}
722
723pub struct DivState {
724 child_layout_ids: SmallVec<[LayoutId; 4]>,
725 interactive_state: InteractiveElementState,
726}
727
728pub struct Interactivity<V> {
729 pub element_id: Option<ElementId>,
730 pub key_context: KeyContext,
731 pub focusable: bool,
732 pub tracked_focus_handle: Option<FocusHandle>,
733 pub focus_listeners: FocusListeners<V>,
734 pub group: Option<SharedString>,
735 pub base_style: StyleRefinement,
736 pub focus_style: StyleRefinement,
737 pub focus_in_style: StyleRefinement,
738 pub in_focus_style: StyleRefinement,
739 pub hover_style: StyleRefinement,
740 pub group_hover_style: Option<GroupStyle>,
741 pub active_style: StyleRefinement,
742 pub group_active_style: Option<GroupStyle>,
743 pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
744 pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
745 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
746 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
747 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
748 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
749 pub key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
750 pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
751 pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
752 pub drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
753 pub click_listeners: SmallVec<[ClickListener<V>; 2]>,
754 pub drag_listener: Option<DragListener<V>>,
755 pub hover_listener: Option<HoverListener<V>>,
756 pub tooltip_builder: Option<TooltipBuilder<V>>,
757}
758
759impl<V> Interactivity<V>
760where
761 V: 'static,
762{
763 pub fn initialize(
764 &mut self,
765 element_state: Option<InteractiveElementState>,
766 cx: &mut ViewContext<V>,
767 ) -> InteractiveElementState {
768 let mut element_state = element_state.unwrap_or_default();
769
770 // Ensure we store a focus handle in our element state if we're focusable.
771 // If there's an explicit focus handle we're tracking, use that. Otherwise
772 // create a new handle and store it in the element state, which lives for as
773 // as frames contain an element with this id.
774 if self.focusable {
775 element_state.focus_handle.get_or_insert_with(|| {
776 self.tracked_focus_handle
777 .clone()
778 .unwrap_or_else(|| cx.focus_handle())
779 });
780 }
781
782 element_state
783 }
784
785 pub fn layout(
786 &mut self,
787 element_state: &mut InteractiveElementState,
788 cx: &mut ViewContext<V>,
789 f: impl FnOnce(Style, &mut ViewContext<V>) -> LayoutId,
790 ) -> LayoutId {
791 let style = self.compute_style(None, element_state, cx);
792 f(style, cx)
793 }
794
795 pub fn paint(
796 &mut self,
797 bounds: Bounds<Pixels>,
798 content_size: Size<Pixels>,
799 element_state: &mut InteractiveElementState,
800 cx: &mut ViewContext<V>,
801 f: impl FnOnce(Style, Point<Pixels>, &mut ViewContext<V>),
802 ) {
803 let style = self.compute_style(Some(bounds), element_state, cx);
804
805 if let Some(mouse_cursor) = style.mouse_cursor {
806 let hovered = bounds.contains_point(&cx.mouse_position());
807 if hovered {
808 cx.set_cursor_style(mouse_cursor);
809 }
810 }
811
812 for listener in self.mouse_down_listeners.drain(..) {
813 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
814 listener(state, event, &bounds, phase, cx);
815 })
816 }
817
818 for listener in self.mouse_up_listeners.drain(..) {
819 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
820 listener(state, event, &bounds, phase, cx);
821 })
822 }
823
824 for listener in self.mouse_move_listeners.drain(..) {
825 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
826 listener(state, event, &bounds, phase, cx);
827 })
828 }
829
830 for listener in self.scroll_wheel_listeners.drain(..) {
831 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
832 listener(state, event, &bounds, phase, cx);
833 })
834 }
835
836 let hover_group_bounds = self
837 .group_hover_style
838 .as_ref()
839 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
840
841 if let Some(group_bounds) = hover_group_bounds {
842 let hovered = group_bounds.contains_point(&cx.mouse_position());
843 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
844 if phase == DispatchPhase::Capture {
845 if group_bounds.contains_point(&event.position) != hovered {
846 cx.notify();
847 }
848 }
849 });
850 }
851
852 if self.hover_style.is_some()
853 || (cx.active_drag.is_some() && !self.drag_over_styles.is_empty())
854 {
855 let hovered = bounds.contains_point(&cx.mouse_position());
856 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
857 if phase == DispatchPhase::Capture {
858 if bounds.contains_point(&event.position) != hovered {
859 cx.notify();
860 }
861 }
862 });
863 }
864
865 if cx.active_drag.is_some() {
866 let drop_listeners = mem::take(&mut self.drop_listeners);
867 cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
868 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
869 if let Some(drag_state_type) =
870 cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
871 {
872 for (drop_state_type, listener) in &drop_listeners {
873 if *drop_state_type == drag_state_type {
874 let drag = cx
875 .active_drag
876 .take()
877 .expect("checked for type drag state type above");
878 listener(view, drag.view.clone(), cx);
879 cx.notify();
880 cx.stop_propagation();
881 }
882 }
883 }
884 }
885 });
886 }
887
888 let click_listeners = mem::take(&mut self.click_listeners);
889 let drag_listener = mem::take(&mut self.drag_listener);
890
891 if !click_listeners.is_empty() || drag_listener.is_some() {
892 let pending_mouse_down = element_state.pending_mouse_down.clone();
893 let mouse_down = pending_mouse_down.lock().clone();
894 if let Some(mouse_down) = mouse_down {
895 if let Some(drag_listener) = drag_listener {
896 let active_state = element_state.clicked_state.clone();
897
898 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
899 if cx.active_drag.is_some() {
900 if phase == DispatchPhase::Capture {
901 cx.notify();
902 }
903 } else if phase == DispatchPhase::Bubble
904 && bounds.contains_point(&event.position)
905 && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
906 {
907 *active_state.lock() = ElementClickedState::default();
908 let cursor_offset = event.position - bounds.origin;
909 let drag = drag_listener(view_state, cursor_offset, cx);
910 cx.active_drag = Some(drag);
911 cx.notify();
912 cx.stop_propagation();
913 }
914 });
915 }
916
917 cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
918 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
919 let mouse_click = ClickEvent {
920 down: mouse_down.clone(),
921 up: event.clone(),
922 };
923 for listener in &click_listeners {
924 listener(view_state, &mouse_click, cx);
925 }
926 }
927 *pending_mouse_down.lock() = None;
928 cx.notify();
929 });
930 } else {
931 cx.on_mouse_event(move |_view_state, event: &MouseDownEvent, phase, cx| {
932 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
933 *pending_mouse_down.lock() = Some(event.clone());
934 cx.notify();
935 }
936 });
937 }
938 }
939
940 if let Some(hover_listener) = self.hover_listener.take() {
941 let was_hovered = element_state.hover_state.clone();
942 let has_mouse_down = element_state.pending_mouse_down.clone();
943
944 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
945 if phase != DispatchPhase::Bubble {
946 return;
947 }
948 let is_hovered =
949 bounds.contains_point(&event.position) && has_mouse_down.lock().is_none();
950 let mut was_hovered = was_hovered.lock();
951
952 if is_hovered != was_hovered.clone() {
953 *was_hovered = is_hovered;
954 drop(was_hovered);
955
956 hover_listener(view_state, is_hovered, cx);
957 }
958 });
959 }
960
961 if let Some(tooltip_builder) = self.tooltip_builder.take() {
962 let active_tooltip = element_state.active_tooltip.clone();
963 let pending_mouse_down = element_state.pending_mouse_down.clone();
964
965 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
966 if phase != DispatchPhase::Bubble {
967 return;
968 }
969
970 let is_hovered =
971 bounds.contains_point(&event.position) && pending_mouse_down.lock().is_none();
972 if !is_hovered {
973 active_tooltip.lock().take();
974 return;
975 }
976
977 if active_tooltip.lock().is_none() {
978 let task = cx.spawn({
979 let active_tooltip = active_tooltip.clone();
980 let tooltip_builder = tooltip_builder.clone();
981
982 move |view, mut cx| async move {
983 cx.background_executor().timer(TOOLTIP_DELAY).await;
984 view.update(&mut cx, move |view_state, cx| {
985 active_tooltip.lock().replace(ActiveTooltip {
986 waiting: None,
987 tooltip: Some(AnyTooltip {
988 view: tooltip_builder(view_state, cx),
989 cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
990 }),
991 });
992 cx.notify();
993 })
994 .ok();
995 }
996 });
997 active_tooltip.lock().replace(ActiveTooltip {
998 waiting: Some(task),
999 tooltip: None,
1000 });
1001 }
1002 });
1003
1004 if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
1005 if active_tooltip.tooltip.is_some() {
1006 cx.active_tooltip = active_tooltip.tooltip.clone()
1007 }
1008 }
1009 }
1010
1011 let active_state = element_state.clicked_state.clone();
1012 if !active_state.lock().is_clicked() {
1013 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
1014 if phase == DispatchPhase::Capture {
1015 *active_state.lock() = ElementClickedState::default();
1016 cx.notify();
1017 }
1018 });
1019 } else {
1020 let active_group_bounds = self
1021 .group_active_style
1022 .as_ref()
1023 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
1024 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
1025 if phase == DispatchPhase::Bubble {
1026 let group = active_group_bounds
1027 .map_or(false, |bounds| bounds.contains_point(&down.position));
1028 let element = bounds.contains_point(&down.position);
1029 if group || element {
1030 *active_state.lock() = ElementClickedState { group, element };
1031 cx.notify();
1032 }
1033 }
1034 });
1035 }
1036
1037 let overflow = style.overflow;
1038 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
1039 let scroll_offset = element_state
1040 .scroll_offset
1041 .get_or_insert_with(Arc::default)
1042 .clone();
1043 let line_height = cx.line_height();
1044 let scroll_max = (content_size - bounds.size).max(&Size::default());
1045
1046 cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
1047 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
1048 let mut scroll_offset = scroll_offset.lock();
1049 let old_scroll_offset = *scroll_offset;
1050 let delta = event.delta.pixel_delta(line_height);
1051
1052 if overflow.x == Overflow::Scroll {
1053 scroll_offset.x =
1054 (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
1055 }
1056
1057 if overflow.y == Overflow::Scroll {
1058 scroll_offset.y =
1059 (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
1060 }
1061
1062 if *scroll_offset != old_scroll_offset {
1063 cx.notify();
1064 cx.stop_propagation();
1065 }
1066 }
1067 });
1068 }
1069
1070 if let Some(group) = self.group.clone() {
1071 GroupBounds::push(group, bounds, cx);
1072 }
1073
1074 let scroll_offset = element_state
1075 .scroll_offset
1076 .as_ref()
1077 .map(|scroll_offset| *scroll_offset.lock());
1078
1079 cx.with_key_dispatch(
1080 self.key_context.clone(),
1081 element_state.focus_handle.clone(),
1082 |_, cx| {
1083 for listener in self.key_down_listeners.drain(..) {
1084 cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
1085 listener(state, event, phase, cx);
1086 })
1087 }
1088
1089 for listener in self.key_up_listeners.drain(..) {
1090 cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
1091 listener(state, event, phase, cx);
1092 })
1093 }
1094
1095 for (action_type, listener) in self.action_listeners.drain(..) {
1096 cx.on_action(action_type, listener)
1097 }
1098
1099 if let Some(focus_handle) = element_state.focus_handle.as_ref() {
1100 for listener in self.focus_listeners.drain(..) {
1101 let focus_handle = focus_handle.clone();
1102 cx.on_focus_changed(move |view, event, cx| {
1103 listener(view, &focus_handle, event, cx)
1104 });
1105 }
1106 }
1107
1108 f(style, scroll_offset.unwrap_or_default(), cx)
1109 },
1110 );
1111
1112 if let Some(group) = self.group.as_ref() {
1113 GroupBounds::pop(group, cx);
1114 }
1115 }
1116
1117 pub fn compute_style(
1118 &self,
1119 bounds: Option<Bounds<Pixels>>,
1120 element_state: &mut InteractiveElementState,
1121 cx: &mut ViewContext<V>,
1122 ) -> Style {
1123 let mut style = Style::default();
1124 style.refine(&self.base_style);
1125
1126 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1127 if focus_handle.contains_focused(cx) {
1128 style.refine(&self.focus_in_style);
1129 }
1130
1131 if focus_handle.within_focused(cx) {
1132 style.refine(&self.in_focus_style);
1133 }
1134
1135 if focus_handle.is_focused(cx) {
1136 style.refine(&self.focus_style);
1137 }
1138 }
1139
1140 if let Some(bounds) = bounds {
1141 let mouse_position = cx.mouse_position();
1142 if let Some(group_hover) = self.group_hover_style.as_ref() {
1143 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
1144 if group_bounds.contains_point(&mouse_position) {
1145 style.refine(&group_hover.style);
1146 }
1147 }
1148 }
1149 if bounds.contains_point(&mouse_position) {
1150 style.refine(&self.hover_style);
1151 }
1152
1153 if let Some(drag) = cx.active_drag.take() {
1154 for (state_type, group_drag_style) in &self.group_drag_over_styles {
1155 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
1156 if *state_type == drag.view.entity_type()
1157 && group_bounds.contains_point(&mouse_position)
1158 {
1159 style.refine(&group_drag_style.style);
1160 }
1161 }
1162 }
1163
1164 for (state_type, drag_over_style) in &self.drag_over_styles {
1165 if *state_type == drag.view.entity_type()
1166 && bounds.contains_point(&mouse_position)
1167 {
1168 style.refine(drag_over_style);
1169 }
1170 }
1171
1172 cx.active_drag = Some(drag);
1173 }
1174 }
1175
1176 let clicked_state = element_state.clicked_state.lock();
1177 if clicked_state.group {
1178 if let Some(group) = self.group_active_style.as_ref() {
1179 style.refine(&group.style)
1180 }
1181 }
1182
1183 if clicked_state.element {
1184 style.refine(&self.active_style)
1185 }
1186
1187 style
1188 }
1189}
1190
1191impl<V: 'static> Default for Interactivity<V> {
1192 fn default() -> Self {
1193 Self {
1194 element_id: None,
1195 key_context: KeyContext::default(),
1196 focusable: false,
1197 tracked_focus_handle: None,
1198 focus_listeners: SmallVec::default(),
1199 // scroll_offset: Point::default(),
1200 group: None,
1201 base_style: StyleRefinement::default(),
1202 focus_style: StyleRefinement::default(),
1203 focus_in_style: StyleRefinement::default(),
1204 in_focus_style: StyleRefinement::default(),
1205 hover_style: StyleRefinement::default(),
1206 group_hover_style: None,
1207 active_style: StyleRefinement::default(),
1208 group_active_style: None,
1209 drag_over_styles: SmallVec::new(),
1210 group_drag_over_styles: SmallVec::new(),
1211 mouse_down_listeners: SmallVec::new(),
1212 mouse_up_listeners: SmallVec::new(),
1213 mouse_move_listeners: SmallVec::new(),
1214 scroll_wheel_listeners: SmallVec::new(),
1215 key_down_listeners: SmallVec::new(),
1216 key_up_listeners: SmallVec::new(),
1217 action_listeners: SmallVec::new(),
1218 drop_listeners: SmallVec::new(),
1219 click_listeners: SmallVec::new(),
1220 drag_listener: None,
1221 hover_listener: None,
1222 tooltip_builder: None,
1223 }
1224 }
1225}
1226
1227#[derive(Default)]
1228pub struct InteractiveElementState {
1229 pub focus_handle: Option<FocusHandle>,
1230 pub clicked_state: Arc<Mutex<ElementClickedState>>,
1231 pub hover_state: Arc<Mutex<bool>>,
1232 pub pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
1233 pub scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
1234 pub active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
1235}
1236
1237pub struct ActiveTooltip {
1238 #[allow(unused)] // used to drop the task
1239 waiting: Option<Task<()>>,
1240 tooltip: Option<AnyTooltip>,
1241}
1242
1243/// Whether or not the element or a group that contains it is clicked by the mouse.
1244#[derive(Copy, Clone, Default, Eq, PartialEq)]
1245pub struct ElementClickedState {
1246 pub group: bool,
1247 pub element: bool,
1248}
1249
1250impl ElementClickedState {
1251 fn is_clicked(&self) -> bool {
1252 self.group || self.element
1253 }
1254}
1255
1256#[derive(Default)]
1257pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
1258
1259impl GroupBounds {
1260 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
1261 cx.default_global::<Self>()
1262 .0
1263 .get(name)
1264 .and_then(|bounds_stack| bounds_stack.last())
1265 .cloned()
1266 }
1267
1268 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
1269 cx.default_global::<Self>()
1270 .0
1271 .entry(name)
1272 .or_default()
1273 .push(bounds);
1274 }
1275
1276 pub fn pop(name: &SharedString, cx: &mut AppContext) {
1277 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
1278 }
1279}
1280
1281pub struct Focusable<V, E> {
1282 element: E,
1283 view_type: PhantomData<V>,
1284}
1285
1286impl<V: 'static, E: InteractiveComponent<V>> FocusableComponent<V> for Focusable<V, E> {}
1287
1288impl<V, E> InteractiveComponent<V> for Focusable<V, E>
1289where
1290 V: 'static,
1291 E: InteractiveComponent<V>,
1292{
1293 fn interactivity(&mut self) -> &mut Interactivity<V> {
1294 self.element.interactivity()
1295 }
1296}
1297
1298impl<V: 'static, E: StatefulInteractiveComponent<V, E>> StatefulInteractiveComponent<V, E>
1299 for Focusable<V, E>
1300{
1301}
1302
1303impl<V, E> Styled for Focusable<V, E>
1304where
1305 V: 'static,
1306 E: Styled,
1307{
1308 fn style(&mut self) -> &mut StyleRefinement {
1309 self.element.style()
1310 }
1311}
1312
1313impl<V, E> Element<V> for Focusable<V, E>
1314where
1315 V: 'static,
1316 E: Element<V>,
1317{
1318 type ElementState = E::ElementState;
1319
1320 fn element_id(&self) -> Option<ElementId> {
1321 self.element.element_id()
1322 }
1323
1324 fn initialize(
1325 &mut self,
1326 view_state: &mut V,
1327 element_state: Option<Self::ElementState>,
1328 cx: &mut ViewContext<V>,
1329 ) -> Self::ElementState {
1330 self.element.initialize(view_state, element_state, cx)
1331 }
1332
1333 fn layout(
1334 &mut self,
1335 view_state: &mut V,
1336 element_state: &mut Self::ElementState,
1337 cx: &mut ViewContext<V>,
1338 ) -> LayoutId {
1339 self.element.layout(view_state, element_state, cx)
1340 }
1341
1342 fn paint(
1343 &mut self,
1344 bounds: Bounds<Pixels>,
1345 view_state: &mut V,
1346 element_state: &mut Self::ElementState,
1347 cx: &mut ViewContext<V>,
1348 ) {
1349 self.element.paint(bounds, view_state, element_state, cx);
1350 }
1351}
1352
1353impl<V, E> Component<V> for Focusable<V, E>
1354where
1355 V: 'static,
1356 E: 'static + Element<V>,
1357{
1358 fn render(self) -> AnyElement<V> {
1359 AnyElement::new(self)
1360 }
1361}
1362
1363impl<V, E> ParentComponent<V> for Focusable<V, E>
1364where
1365 V: 'static,
1366 E: ParentComponent<V>,
1367{
1368 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
1369 self.element.children_mut()
1370 }
1371}
1372
1373pub struct Stateful<V, E> {
1374 element: E,
1375 view_type: PhantomData<V>,
1376}
1377
1378impl<V, E> Styled for Stateful<V, E>
1379where
1380 V: 'static,
1381 E: Styled,
1382{
1383 fn style(&mut self) -> &mut StyleRefinement {
1384 self.element.style()
1385 }
1386}
1387
1388impl<V, E> StatefulInteractiveComponent<V, E> for Stateful<V, E>
1389where
1390 V: 'static,
1391 E: Element<V>,
1392 Self: InteractiveComponent<V>,
1393{
1394}
1395
1396impl<V, E> InteractiveComponent<V> for Stateful<V, E>
1397where
1398 V: 'static,
1399 E: InteractiveComponent<V>,
1400{
1401 fn interactivity(&mut self) -> &mut Interactivity<V> {
1402 self.element.interactivity()
1403 }
1404}
1405
1406impl<V: 'static, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {}
1407
1408impl<V, E> Element<V> for Stateful<V, E>
1409where
1410 V: 'static,
1411 E: Element<V>,
1412{
1413 type ElementState = E::ElementState;
1414
1415 fn element_id(&self) -> Option<ElementId> {
1416 self.element.element_id()
1417 }
1418
1419 fn initialize(
1420 &mut self,
1421 view_state: &mut V,
1422 element_state: Option<Self::ElementState>,
1423 cx: &mut ViewContext<V>,
1424 ) -> Self::ElementState {
1425 self.element.initialize(view_state, element_state, cx)
1426 }
1427
1428 fn layout(
1429 &mut self,
1430 view_state: &mut V,
1431 element_state: &mut Self::ElementState,
1432 cx: &mut ViewContext<V>,
1433 ) -> LayoutId {
1434 self.element.layout(view_state, element_state, cx)
1435 }
1436
1437 fn paint(
1438 &mut self,
1439 bounds: Bounds<Pixels>,
1440 view_state: &mut V,
1441 element_state: &mut Self::ElementState,
1442 cx: &mut ViewContext<V>,
1443 ) {
1444 self.element.paint(bounds, view_state, element_state, cx)
1445 }
1446}
1447
1448impl<V, E> Component<V> for Stateful<V, E>
1449where
1450 V: 'static,
1451 E: 'static + Element<V>,
1452{
1453 fn render(self) -> AnyElement<V> {
1454 AnyElement::new(self)
1455 }
1456}
1457
1458impl<V, E> ParentComponent<V> for Stateful<V, E>
1459where
1460 V: 'static,
1461 E: ParentComponent<V>,
1462{
1463 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
1464 self.element.children_mut()
1465 }
1466}