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