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