1use crate::PinchEvent;
19use crate::{
20 AbsoluteLength, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent,
21 DispatchPhase, Display, Element, ElementId, Entity, FocusHandle, Global, GlobalElementId,
22 Hitbox, HitboxBehavior, HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext,
23 KeyDownEvent, KeyUpEvent, KeyboardButton, KeyboardClickEvent, LayoutId, Length,
24 ModifiersChangedEvent, MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent,
25 MousePressureEvent, MouseUpEvent, Overflow, ParentElement, Pixels, Point, Position, Render,
26 ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId,
27 Visibility, Window, WindowControlArea, point, px, size,
28};
29use collections::HashMap;
30use gpui_util::ResultExt;
31use refineable::Refineable;
32use smallvec::SmallVec;
33use stacksafe::{StackSafe, stacksafe};
34use std::{
35 any::{Any, TypeId},
36 cell::RefCell,
37 cmp::Ordering,
38 fmt::Debug,
39 marker::PhantomData,
40 mem,
41 rc::Rc,
42 sync::Arc,
43 time::Duration,
44};
45
46use super::ImageCacheProvider;
47
48const DRAG_THRESHOLD: f64 = 2.;
49const TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);
50const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500);
51
52pub struct GroupStyle {
54 pub group: SharedString,
56
57 pub style: Box<StyleRefinement>,
60}
61
62pub struct DragMoveEvent<T> {
64 pub event: MouseMoveEvent,
66
67 pub bounds: Bounds<Pixels>,
69 drag: PhantomData<T>,
70 dragged_item: Arc<dyn Any>,
71}
72
73impl<T: 'static> DragMoveEvent<T> {
74 pub fn drag<'b>(&self, cx: &'b App) -> &'b T {
76 cx.active_drag
77 .as_ref()
78 .and_then(|drag| drag.value.downcast_ref::<T>())
79 .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
80 }
81
82 pub fn dragged_item(&self) -> &dyn Any {
84 self.dragged_item.as_ref()
85 }
86}
87
88impl Interactivity {
89 #[cfg(any(feature = "inspector", debug_assertions))]
91 #[track_caller]
92 pub fn new() -> Interactivity {
93 Interactivity {
94 source_location: Some(core::panic::Location::caller()),
95 ..Default::default()
96 }
97 }
98
99 #[cfg(not(any(feature = "inspector", debug_assertions)))]
101 pub fn new() -> Interactivity {
102 Interactivity::default()
103 }
104
105 pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
107 #[cfg(any(feature = "inspector", debug_assertions))]
108 {
109 self.source_location
110 }
111
112 #[cfg(not(any(feature = "inspector", debug_assertions)))]
113 {
114 None
115 }
116 }
117
118 pub fn on_mouse_down(
123 &mut self,
124 button: MouseButton,
125 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
126 ) {
127 self.mouse_down_listeners
128 .push(Box::new(move |event, phase, hitbox, window, cx| {
129 if phase == DispatchPhase::Bubble
130 && event.button == button
131 && hitbox.is_hovered(window)
132 {
133 (listener)(event, window, cx)
134 }
135 }));
136 }
137
138 pub fn capture_any_mouse_down(
143 &mut self,
144 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
145 ) {
146 self.mouse_down_listeners
147 .push(Box::new(move |event, phase, hitbox, window, cx| {
148 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
149 (listener)(event, window, cx)
150 }
151 }));
152 }
153
154 pub fn on_any_mouse_down(
159 &mut self,
160 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
161 ) {
162 self.mouse_down_listeners
163 .push(Box::new(move |event, phase, hitbox, window, cx| {
164 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
165 (listener)(event, window, cx)
166 }
167 }));
168 }
169
170 pub fn on_mouse_pressure(
175 &mut self,
176 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
177 ) {
178 self.mouse_pressure_listeners
179 .push(Box::new(move |event, phase, hitbox, window, cx| {
180 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
181 (listener)(event, window, cx)
182 }
183 }));
184 }
185
186 pub fn capture_mouse_pressure(
191 &mut self,
192 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
193 ) {
194 self.mouse_pressure_listeners
195 .push(Box::new(move |event, phase, hitbox, window, cx| {
196 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
197 (listener)(event, window, cx)
198 }
199 }));
200 }
201
202 pub fn on_mouse_up(
207 &mut self,
208 button: MouseButton,
209 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
210 ) {
211 self.mouse_up_listeners
212 .push(Box::new(move |event, phase, hitbox, window, cx| {
213 if phase == DispatchPhase::Bubble
214 && event.button == button
215 && hitbox.is_hovered(window)
216 {
217 (listener)(event, window, cx)
218 }
219 }));
220 }
221
222 pub fn capture_any_mouse_up(
227 &mut self,
228 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
229 ) {
230 self.mouse_up_listeners
231 .push(Box::new(move |event, phase, hitbox, window, cx| {
232 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
233 (listener)(event, window, cx)
234 }
235 }));
236 }
237
238 pub fn on_any_mouse_up(
243 &mut self,
244 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
245 ) {
246 self.mouse_up_listeners
247 .push(Box::new(move |event, phase, hitbox, window, cx| {
248 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
249 (listener)(event, window, cx)
250 }
251 }));
252 }
253
254 pub fn on_mouse_down_out(
260 &mut self,
261 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
262 ) {
263 self.mouse_down_listeners
264 .push(Box::new(move |event, phase, hitbox, window, cx| {
265 if phase == DispatchPhase::Capture && !hitbox.contains(&window.mouse_position()) {
266 (listener)(event, window, cx)
267 }
268 }));
269 }
270
271 pub fn on_mouse_up_out(
277 &mut self,
278 button: MouseButton,
279 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
280 ) {
281 self.mouse_up_listeners
282 .push(Box::new(move |event, phase, hitbox, window, cx| {
283 if phase == DispatchPhase::Capture
284 && event.button == button
285 && !hitbox.is_hovered(window)
286 {
287 (listener)(event, window, cx);
288 }
289 }));
290 }
291
292 pub fn on_mouse_move(
297 &mut self,
298 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
299 ) {
300 self.mouse_move_listeners
301 .push(Box::new(move |event, phase, hitbox, window, cx| {
302 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
303 (listener)(event, window, cx);
304 }
305 }));
306 }
307
308 pub fn on_drag_move<T>(
316 &mut self,
317 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
318 ) where
319 T: 'static,
320 {
321 self.mouse_move_listeners
322 .push(Box::new(move |event, phase, hitbox, window, cx| {
323 if phase == DispatchPhase::Capture
324 && let Some(drag) = &cx.active_drag
325 && drag.value.as_ref().type_id() == TypeId::of::<T>()
326 {
327 (listener)(
328 &DragMoveEvent {
329 event: event.clone(),
330 bounds: hitbox.bounds,
331 drag: PhantomData,
332 dragged_item: Arc::clone(&drag.value),
333 },
334 window,
335 cx,
336 );
337 }
338 }));
339 }
340
341 pub fn on_scroll_wheel(
346 &mut self,
347 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
348 ) {
349 self.scroll_wheel_listeners
350 .push(Box::new(move |event, phase, hitbox, window, cx| {
351 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
352 (listener)(event, window, cx);
353 }
354 }));
355 }
356
357 pub fn on_pinch(&mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) {
361 self.pinch_listeners
362 .push(Box::new(move |event, phase, hitbox, window, cx| {
363 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
364 (listener)(event, window, cx);
365 }
366 }));
367 }
368
369 pub fn capture_pinch(
373 &mut self,
374 listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
375 ) {
376 self.pinch_listeners
377 .push(Box::new(move |event, phase, _hitbox, window, cx| {
378 if phase == DispatchPhase::Capture {
379 (listener)(event, window, cx);
380 } else {
381 cx.propagate();
382 }
383 }));
384 }
385
386 pub fn capture_action<A: Action>(
391 &mut self,
392 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
393 ) {
394 self.action_listeners.push((
395 TypeId::of::<A>(),
396 Box::new(move |action, phase, window, cx| {
397 let action = action.downcast_ref().unwrap();
398 if phase == DispatchPhase::Capture {
399 (listener)(action, window, cx)
400 } else {
401 cx.propagate();
402 }
403 }),
404 ));
405 }
406
407 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {
412 self.action_listeners.push((
413 TypeId::of::<A>(),
414 Box::new(move |action, phase, window, cx| {
415 let action = action.downcast_ref().unwrap();
416 if phase == DispatchPhase::Bubble {
417 (listener)(action, window, cx)
418 }
419 }),
420 ));
421 }
422
423 pub fn on_boxed_action(
430 &mut self,
431 action: &dyn Action,
432 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
433 ) {
434 let action = action.boxed_clone();
435 self.action_listeners.push((
436 (*action).type_id(),
437 Box::new(move |_, phase, window, cx| {
438 if phase == DispatchPhase::Bubble {
439 (listener)(&*action, window, cx)
440 }
441 }),
442 ));
443 }
444
445 pub fn on_key_down(
450 &mut self,
451 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
452 ) {
453 self.key_down_listeners
454 .push(Box::new(move |event, phase, window, cx| {
455 if phase == DispatchPhase::Bubble {
456 (listener)(event, window, cx)
457 }
458 }));
459 }
460
461 pub fn capture_key_down(
466 &mut self,
467 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
468 ) {
469 self.key_down_listeners
470 .push(Box::new(move |event, phase, window, cx| {
471 if phase == DispatchPhase::Capture {
472 listener(event, window, cx)
473 }
474 }));
475 }
476
477 pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {
482 self.key_up_listeners
483 .push(Box::new(move |event, phase, window, cx| {
484 if phase == DispatchPhase::Bubble {
485 listener(event, window, cx)
486 }
487 }));
488 }
489
490 pub fn capture_key_up(
495 &mut self,
496 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
497 ) {
498 self.key_up_listeners
499 .push(Box::new(move |event, phase, window, cx| {
500 if phase == DispatchPhase::Capture {
501 listener(event, window, cx)
502 }
503 }));
504 }
505
506 pub fn on_modifiers_changed(
511 &mut self,
512 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
513 ) {
514 self.modifiers_changed_listeners
515 .push(Box::new(move |event, window, cx| {
516 listener(event, window, cx)
517 }));
518 }
519
520 pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {
525 self.drop_listeners.push((
526 TypeId::of::<T>(),
527 Box::new(move |dragged_value, window, cx| {
528 listener(dragged_value.downcast_ref().unwrap(), window, cx);
529 }),
530 ));
531 }
532
533 pub fn can_drop(
536 &mut self,
537 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
538 ) {
539 self.can_drop_predicate = Some(Box::new(predicate));
540 }
541
542 pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
547 where
548 Self: Sized,
549 {
550 self.click_listeners.push(Rc::new(move |event, window, cx| {
551 listener(event, window, cx)
552 }));
553 }
554
555 pub fn on_aux_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
560 where
561 Self: Sized,
562 {
563 self.aux_click_listeners
564 .push(Rc::new(move |event, window, cx| {
565 listener(event, window, cx)
566 }));
567 }
568
569 pub fn on_drag<T, W>(
576 &mut self,
577 value: T,
578 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
579 ) where
580 Self: Sized,
581 T: 'static,
582 W: 'static + Render,
583 {
584 debug_assert!(
585 self.drag_listener.is_none(),
586 "calling on_drag more than once on the same element is not supported"
587 );
588 self.drag_listener = Some((
589 Arc::new(value),
590 Box::new(move |value, offset, window, cx| {
591 constructor(value.downcast_ref().unwrap(), offset, window, cx).into()
592 }),
593 ));
594 }
595
596 pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static)
602 where
603 Self: Sized,
604 {
605 debug_assert!(
606 self.hover_listener.is_none(),
607 "calling on_hover more than once on the same element is not supported"
608 );
609 self.hover_listener = Some(Box::new(listener));
610 }
611
612 pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static)
615 where
616 Self: Sized,
617 {
618 debug_assert!(
619 self.tooltip_builder.is_none(),
620 "calling tooltip more than once on the same element is not supported"
621 );
622 self.tooltip_builder = Some(TooltipBuilder {
623 build: Rc::new(build_tooltip),
624 hoverable: false,
625 });
626 }
627
628 pub fn hoverable_tooltip(
632 &mut self,
633 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
634 ) where
635 Self: Sized,
636 {
637 debug_assert!(
638 self.tooltip_builder.is_none(),
639 "calling tooltip more than once on the same element is not supported"
640 );
641 self.tooltip_builder = Some(TooltipBuilder {
642 build: Rc::new(build_tooltip),
643 hoverable: true,
644 });
645 }
646
647 pub fn occlude_mouse(&mut self) {
652 self.hitbox_behavior = HitboxBehavior::BlockMouse;
653 }
654
655 pub fn window_control_area(&mut self, area: WindowControlArea) {
658 self.window_control = Some(area);
659 }
660
661 pub fn block_mouse_except_scroll(&mut self) {
666 self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll;
667 }
668
669 fn has_pinch_listeners(&self) -> bool {
670 !self.pinch_listeners.is_empty()
671 }
672}
673
674pub trait InteractiveElement: Sized {
677 fn interactivity(&mut self) -> &mut Interactivity;
679
680 fn group(mut self, group: impl Into<SharedString>) -> Self {
682 self.interactivity().group = Some(group.into());
683 self
684 }
685
686 fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
688 self.interactivity().element_id = Some(id.into());
689
690 Stateful { element: self }
691 }
692
693 fn track_focus(mut self, focus_handle: &FocusHandle) -> Self {
697 self.interactivity().focusable = true;
698 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
699 self
700 }
701
702 fn tab_stop(mut self, tab_stop: bool) -> Self {
709 self.interactivity().tab_stop = tab_stop;
710 self
711 }
712
713 fn tab_index(mut self, index: isize) -> Self {
718 self.interactivity().focusable = true;
719 self.interactivity().tab_index = Some(index);
720 self.interactivity().tab_stop = true;
721 self
722 }
723
724 fn tab_group(mut self) -> Self {
729 self.interactivity().tab_group = true;
730 if self.interactivity().tab_index.is_none() {
731 self.interactivity().tab_index = Some(0);
732 }
733 self
734 }
735
736 fn key_context<C, E>(mut self, key_context: C) -> Self
739 where
740 C: TryInto<KeyContext, Error = E>,
741 E: std::fmt::Display,
742 {
743 if let Some(key_context) = key_context.try_into().log_err() {
744 self.interactivity().key_context = Some(key_context);
745 }
746 self
747 }
748
749 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
751 debug_assert!(
752 self.interactivity().hover_style.is_none(),
753 "hover style already set"
754 );
755 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
756 self
757 }
758
759 fn group_hover(
761 mut self,
762 group_name: impl Into<SharedString>,
763 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
764 ) -> Self {
765 self.interactivity().group_hover_style = Some(GroupStyle {
766 group: group_name.into(),
767 style: Box::new(f(StyleRefinement::default())),
768 });
769 self
770 }
771
772 fn on_mouse_down(
777 mut self,
778 button: MouseButton,
779 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
780 ) -> Self {
781 self.interactivity().on_mouse_down(button, listener);
782 self
783 }
784
785 #[cfg(any(test, feature = "test-support"))]
786 fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
790 self.interactivity().debug_selector = Some(f());
791 self
792 }
793
794 #[cfg(not(any(test, feature = "test-support")))]
795 #[inline]
799 fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
800 self
801 }
802
803 fn capture_any_mouse_down(
808 mut self,
809 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
810 ) -> Self {
811 self.interactivity().capture_any_mouse_down(listener);
812 self
813 }
814
815 fn on_any_mouse_down(
820 mut self,
821 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
822 ) -> Self {
823 self.interactivity().on_any_mouse_down(listener);
824 self
825 }
826
827 fn on_mouse_up(
832 mut self,
833 button: MouseButton,
834 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
835 ) -> Self {
836 self.interactivity().on_mouse_up(button, listener);
837 self
838 }
839
840 fn capture_any_mouse_up(
845 mut self,
846 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
847 ) -> Self {
848 self.interactivity().capture_any_mouse_up(listener);
849 self
850 }
851
852 fn on_mouse_pressure(
857 mut self,
858 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
859 ) -> Self {
860 self.interactivity().on_mouse_pressure(listener);
861 self
862 }
863
864 fn capture_mouse_pressure(
869 mut self,
870 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
871 ) -> Self {
872 self.interactivity().capture_mouse_pressure(listener);
873 self
874 }
875
876 fn on_mouse_down_out(
882 mut self,
883 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
884 ) -> Self {
885 self.interactivity().on_mouse_down_out(listener);
886 self
887 }
888
889 fn on_mouse_up_out(
895 mut self,
896 button: MouseButton,
897 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
898 ) -> Self {
899 self.interactivity().on_mouse_up_out(button, listener);
900 self
901 }
902
903 fn on_mouse_move(
908 mut self,
909 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
910 ) -> Self {
911 self.interactivity().on_mouse_move(listener);
912 self
913 }
914
915 fn on_drag_move<T: 'static>(
923 mut self,
924 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
925 ) -> Self {
926 self.interactivity().on_drag_move(listener);
927 self
928 }
929
930 fn on_scroll_wheel(
935 mut self,
936 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
937 ) -> Self {
938 self.interactivity().on_scroll_wheel(listener);
939 self
940 }
941
942 fn on_pinch(mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) -> Self {
947 self.interactivity().on_pinch(listener);
948 self
949 }
950
951 fn capture_pinch(
956 mut self,
957 listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
958 ) -> Self {
959 self.interactivity().capture_pinch(listener);
960 self
961 }
962 fn capture_action<A: Action>(
967 mut self,
968 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
969 ) -> Self {
970 self.interactivity().capture_action(listener);
971 self
972 }
973
974 fn on_action<A: Action>(
979 mut self,
980 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
981 ) -> Self {
982 self.interactivity().on_action(listener);
983 self
984 }
985
986 fn on_boxed_action(
993 mut self,
994 action: &dyn Action,
995 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
996 ) -> Self {
997 self.interactivity().on_boxed_action(action, listener);
998 self
999 }
1000
1001 fn on_key_down(
1006 mut self,
1007 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
1008 ) -> Self {
1009 self.interactivity().on_key_down(listener);
1010 self
1011 }
1012
1013 fn capture_key_down(
1018 mut self,
1019 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
1020 ) -> Self {
1021 self.interactivity().capture_key_down(listener);
1022 self
1023 }
1024
1025 fn on_key_up(
1030 mut self,
1031 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1032 ) -> Self {
1033 self.interactivity().on_key_up(listener);
1034 self
1035 }
1036
1037 fn capture_key_up(
1042 mut self,
1043 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1044 ) -> Self {
1045 self.interactivity().capture_key_up(listener);
1046 self
1047 }
1048
1049 fn on_modifiers_changed(
1054 mut self,
1055 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
1056 ) -> Self {
1057 self.interactivity().on_modifiers_changed(listener);
1058 self
1059 }
1060
1061 fn drag_over<S: 'static>(
1063 mut self,
1064 f: impl 'static + Fn(StyleRefinement, &S, &mut Window, &mut App) -> StyleRefinement,
1065 ) -> Self {
1066 self.interactivity().drag_over_styles.push((
1067 TypeId::of::<S>(),
1068 Box::new(move |currently_dragged: &dyn Any, window, cx| {
1069 f(
1070 StyleRefinement::default(),
1071 currently_dragged.downcast_ref::<S>().unwrap(),
1072 window,
1073 cx,
1074 )
1075 }),
1076 ));
1077 self
1078 }
1079
1080 fn group_drag_over<S: 'static>(
1082 mut self,
1083 group_name: impl Into<SharedString>,
1084 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1085 ) -> Self {
1086 self.interactivity().group_drag_over_styles.push((
1087 TypeId::of::<S>(),
1088 GroupStyle {
1089 group: group_name.into(),
1090 style: Box::new(f(StyleRefinement::default())),
1091 },
1092 ));
1093 self
1094 }
1095
1096 fn on_drop<T: 'static>(
1101 mut self,
1102 listener: impl Fn(&T, &mut Window, &mut App) + 'static,
1103 ) -> Self {
1104 self.interactivity().on_drop(listener);
1105 self
1106 }
1107
1108 fn can_drop(
1111 mut self,
1112 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
1113 ) -> Self {
1114 self.interactivity().can_drop(predicate);
1115 self
1116 }
1117
1118 fn occlude(mut self) -> Self {
1122 self.interactivity().occlude_mouse();
1123 self
1124 }
1125
1126 fn window_control_area(mut self, area: WindowControlArea) -> Self {
1129 self.interactivity().window_control_area(area);
1130 self
1131 }
1132
1133 fn block_mouse_except_scroll(mut self) -> Self {
1138 self.interactivity().block_mouse_except_scroll();
1139 self
1140 }
1141
1142 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1145 where
1146 Self: Sized,
1147 {
1148 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
1149 self
1150 }
1151
1152 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1155 where
1156 Self: Sized,
1157 {
1158 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
1159 self
1160 }
1161
1162 fn focus_visible(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1167 where
1168 Self: Sized,
1169 {
1170 self.interactivity().focus_visible_style = Some(Box::new(f(StyleRefinement::default())));
1171 self
1172 }
1173}
1174
1175pub trait StatefulInteractiveElement: InteractiveElement {
1178 fn focusable(mut self) -> Self {
1180 self.interactivity().focusable = true;
1181 self
1182 }
1183
1184 fn overflow_scroll(mut self) -> Self {
1186 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1187 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1188 self
1189 }
1190
1191 fn overflow_x_scroll(mut self) -> Self {
1193 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1194 self
1195 }
1196
1197 fn overflow_y_scroll(mut self) -> Self {
1199 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1200 self
1201 }
1202
1203 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
1205 self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone());
1206 self
1207 }
1208
1209 fn anchor_scroll(mut self, scroll_anchor: Option<ScrollAnchor>) -> Self {
1211 self.interactivity().scroll_anchor = scroll_anchor;
1212 self
1213 }
1214
1215 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1217 where
1218 Self: Sized,
1219 {
1220 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
1221 self
1222 }
1223
1224 fn group_active(
1226 mut self,
1227 group_name: impl Into<SharedString>,
1228 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1229 ) -> Self
1230 where
1231 Self: Sized,
1232 {
1233 self.interactivity().group_active_style = Some(GroupStyle {
1234 group: group_name.into(),
1235 style: Box::new(f(StyleRefinement::default())),
1236 });
1237 self
1238 }
1239
1240 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self
1245 where
1246 Self: Sized,
1247 {
1248 self.interactivity().on_click(listener);
1249 self
1250 }
1251
1252 fn on_aux_click(
1257 mut self,
1258 listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
1259 ) -> Self
1260 where
1261 Self: Sized,
1262 {
1263 self.interactivity().on_aux_click(listener);
1264 self
1265 }
1266
1267 fn on_drag<T, W>(
1275 mut self,
1276 value: T,
1277 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
1278 ) -> Self
1279 where
1280 Self: Sized,
1281 T: 'static,
1282 W: 'static + Render,
1283 {
1284 self.interactivity().on_drag(value, constructor);
1285 self
1286 }
1287
1288 fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self
1294 where
1295 Self: Sized,
1296 {
1297 self.interactivity().on_hover(listener);
1298 self
1299 }
1300
1301 fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self
1304 where
1305 Self: Sized,
1306 {
1307 self.interactivity().tooltip(build_tooltip);
1308 self
1309 }
1310
1311 fn hoverable_tooltip(
1315 mut self,
1316 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
1317 ) -> Self
1318 where
1319 Self: Sized,
1320 {
1321 self.interactivity().hoverable_tooltip(build_tooltip);
1322 self
1323 }
1324}
1325
1326pub(crate) type MouseDownListener =
1327 Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1328pub(crate) type MouseUpListener =
1329 Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1330pub(crate) type MousePressureListener =
1331 Box<dyn Fn(&MousePressureEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1332pub(crate) type MouseMoveListener =
1333 Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1334
1335pub(crate) type ScrollWheelListener =
1336 Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1337
1338pub(crate) type PinchListener =
1339 Box<dyn Fn(&PinchEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1340
1341pub(crate) type ClickListener = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
1342
1343pub(crate) type DragListener =
1344 Box<dyn Fn(&dyn Any, Point<Pixels>, &mut Window, &mut App) -> AnyView + 'static>;
1345
1346type DropListener = Box<dyn Fn(&dyn Any, &mut Window, &mut App) + 'static>;
1347
1348type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>;
1349
1350pub(crate) struct TooltipBuilder {
1351 build: Rc<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>,
1352 hoverable: bool,
1353}
1354
1355pub(crate) type KeyDownListener =
1356 Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1357
1358pub(crate) type KeyUpListener =
1359 Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1360
1361pub(crate) type ModifiersChangedListener =
1362 Box<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static>;
1363
1364pub(crate) type ActionListener =
1365 Box<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
1366
1367#[track_caller]
1369pub fn div() -> Div {
1370 Div {
1371 interactivity: Interactivity::new(),
1372 children: SmallVec::default(),
1373 prepaint_listener: None,
1374 image_cache: None,
1375 prepaint_order_fn: None,
1376 }
1377}
1378
1379pub struct Div {
1381 interactivity: Interactivity,
1382 children: SmallVec<[StackSafe<AnyElement>; 2]>,
1383 prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static>>,
1384 image_cache: Option<Box<dyn ImageCacheProvider>>,
1385 prepaint_order_fn: Option<Box<dyn Fn(&mut Window, &mut App) -> SmallVec<[usize; 8]>>>,
1386}
1387
1388impl Div {
1389 pub fn on_children_prepainted(
1392 mut self,
1393 listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
1394 ) -> Self {
1395 self.prepaint_listener = Some(Box::new(listener));
1396 self
1397 }
1398
1399 pub fn image_cache(mut self, cache: impl ImageCacheProvider) -> Self {
1401 self.image_cache = Some(Box::new(cache));
1402 self
1403 }
1404
1405 pub fn with_dynamic_prepaint_order(
1414 mut self,
1415 order_fn: impl Fn(&mut Window, &mut App) -> SmallVec<[usize; 8]> + 'static,
1416 ) -> Self {
1417 self.prepaint_order_fn = Some(Box::new(order_fn));
1418 self
1419 }
1420}
1421
1422pub struct DivFrameState {
1429 child_layout_ids: SmallVec<[LayoutId; 2]>,
1430}
1431
1432#[derive(Clone)]
1434pub struct DivInspectorState {
1435 #[cfg(any(feature = "inspector", debug_assertions))]
1439 pub base_style: Box<StyleRefinement>,
1440 pub bounds: Bounds<Pixels>,
1442 pub content_size: Size<Pixels>,
1444}
1445
1446impl Styled for Div {
1447 fn style(&mut self) -> &mut StyleRefinement {
1448 &mut self.interactivity.base_style
1449 }
1450}
1451
1452impl InteractiveElement for Div {
1453 fn interactivity(&mut self) -> &mut Interactivity {
1454 &mut self.interactivity
1455 }
1456}
1457
1458impl ParentElement for Div {
1459 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1460 self.children
1461 .extend(elements.into_iter().map(StackSafe::new))
1462 }
1463}
1464
1465impl Element for Div {
1466 type RequestLayoutState = DivFrameState;
1467 type PrepaintState = Option<Hitbox>;
1468
1469 fn id(&self) -> Option<ElementId> {
1470 self.interactivity.element_id.clone()
1471 }
1472
1473 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
1474 self.interactivity.source_location()
1475 }
1476
1477 #[stacksafe]
1478 fn request_layout(
1479 &mut self,
1480 global_id: Option<&GlobalElementId>,
1481 inspector_id: Option<&InspectorElementId>,
1482 window: &mut Window,
1483 cx: &mut App,
1484 ) -> (LayoutId, Self::RequestLayoutState) {
1485 let mut child_layout_ids = SmallVec::new();
1486 let image_cache = self
1487 .image_cache
1488 .as_mut()
1489 .map(|provider| provider.provide(window, cx));
1490
1491 let layout_id = window.with_image_cache(image_cache, |window| {
1492 self.interactivity.request_layout(
1493 global_id,
1494 inspector_id,
1495 window,
1496 cx,
1497 |style, window, cx| {
1498 window.with_text_style(style.text_style().cloned(), |window| {
1499 child_layout_ids = self
1500 .children
1501 .iter_mut()
1502 .map(|child| child.request_layout(window, cx))
1503 .collect::<SmallVec<_>>();
1504 window.request_layout(style, child_layout_ids.iter().copied(), cx)
1505 })
1506 },
1507 )
1508 });
1509
1510 (layout_id, DivFrameState { child_layout_ids })
1511 }
1512
1513 #[stacksafe]
1514 fn prepaint(
1515 &mut self,
1516 global_id: Option<&GlobalElementId>,
1517 inspector_id: Option<&InspectorElementId>,
1518 bounds: Bounds<Pixels>,
1519 request_layout: &mut Self::RequestLayoutState,
1520 window: &mut Window,
1521 cx: &mut App,
1522 ) -> Option<Hitbox> {
1523 let image_cache = self
1524 .image_cache
1525 .as_mut()
1526 .map(|provider| provider.provide(window, cx));
1527
1528 let has_prepaint_listener = self.prepaint_listener.is_some();
1529 let mut children_bounds = Vec::with_capacity(if has_prepaint_listener {
1530 request_layout.child_layout_ids.len()
1531 } else {
1532 0
1533 });
1534
1535 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1536 let mut child_max = Point::default();
1537 if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1538 *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
1539 }
1540 let content_size = if request_layout.child_layout_ids.is_empty() {
1541 bounds.size
1542 } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1543 let mut state = scroll_handle.0.borrow_mut();
1544 state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1545 for child_layout_id in &request_layout.child_layout_ids {
1546 let child_bounds = window.layout_bounds(*child_layout_id);
1547 child_min = child_min.min(&child_bounds.origin);
1548 child_max = child_max.max(&child_bounds.bottom_right());
1549 state.child_bounds.push(child_bounds);
1550 }
1551 (child_max - child_min).into()
1552 } else {
1553 for child_layout_id in &request_layout.child_layout_ids {
1554 let child_bounds = window.layout_bounds(*child_layout_id);
1555 child_min = child_min.min(&child_bounds.origin);
1556 child_max = child_max.max(&child_bounds.bottom_right());
1557
1558 if has_prepaint_listener {
1559 children_bounds.push(child_bounds);
1560 }
1561 }
1562 (child_max - child_min).into()
1563 };
1564
1565 if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1566 scroll_handle.scroll_to_active_item();
1567 }
1568
1569 self.interactivity.prepaint(
1570 global_id,
1571 inspector_id,
1572 bounds,
1573 content_size,
1574 window,
1575 cx,
1576 |style, scroll_offset, hitbox, window, cx| {
1577 if style.display == Display::None {
1579 return hitbox;
1580 }
1581
1582 window.with_image_cache(image_cache, |window| {
1583 window.with_element_offset(scroll_offset, |window| {
1584 if let Some(order_fn) = &self.prepaint_order_fn {
1585 let order = order_fn(window, cx);
1586 for idx in order {
1587 if let Some(child) = self.children.get_mut(idx) {
1588 child.prepaint(window, cx);
1589 }
1590 }
1591 } else {
1592 for child in &mut self.children {
1593 child.prepaint(window, cx);
1594 }
1595 }
1596 });
1597
1598 if let Some(listener) = self.prepaint_listener.as_ref() {
1599 listener(children_bounds, window, cx);
1600 }
1601 });
1602
1603 hitbox
1604 },
1605 )
1606 }
1607
1608 #[stacksafe]
1609 fn paint(
1610 &mut self,
1611 global_id: Option<&GlobalElementId>,
1612 inspector_id: Option<&InspectorElementId>,
1613 bounds: Bounds<Pixels>,
1614 _request_layout: &mut Self::RequestLayoutState,
1615 hitbox: &mut Option<Hitbox>,
1616 window: &mut Window,
1617 cx: &mut App,
1618 ) {
1619 let image_cache = self
1620 .image_cache
1621 .as_mut()
1622 .map(|provider| provider.provide(window, cx));
1623
1624 window.with_image_cache(image_cache, |window| {
1625 self.interactivity.paint(
1626 global_id,
1627 inspector_id,
1628 bounds,
1629 hitbox.as_ref(),
1630 window,
1631 cx,
1632 |style, window, cx| {
1633 if style.display == Display::None {
1635 return;
1636 }
1637
1638 for child in &mut self.children {
1639 child.paint(window, cx);
1640 }
1641 },
1642 )
1643 });
1644 }
1645}
1646
1647impl IntoElement for Div {
1648 type Element = Self;
1649
1650 fn into_element(self) -> Self::Element {
1651 self
1652 }
1653}
1654
1655#[derive(Default)]
1658pub struct Interactivity {
1659 pub element_id: Option<ElementId>,
1661 pub active: Option<bool>,
1663 pub hovered: Option<bool>,
1666 pub(crate) tooltip_id: Option<TooltipId>,
1667 pub(crate) content_size: Size<Pixels>,
1668 pub(crate) key_context: Option<KeyContext>,
1669 pub(crate) focusable: bool,
1670 pub(crate) tracked_focus_handle: Option<FocusHandle>,
1671 pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
1672 pub(crate) scroll_anchor: Option<ScrollAnchor>,
1673 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1674 pub(crate) group: Option<SharedString>,
1675 pub base_style: Box<StyleRefinement>,
1678 pub(crate) focus_style: Option<Box<StyleRefinement>>,
1679 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1680 pub(crate) focus_visible_style: Option<Box<StyleRefinement>>,
1681 pub(crate) hover_style: Option<Box<StyleRefinement>>,
1682 pub(crate) group_hover_style: Option<GroupStyle>,
1683 pub(crate) active_style: Option<Box<StyleRefinement>>,
1684 pub(crate) group_active_style: Option<GroupStyle>,
1685 pub(crate) drag_over_styles: Vec<(
1686 TypeId,
1687 Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> StyleRefinement>,
1688 )>,
1689 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1690 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1691 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1692 pub(crate) mouse_pressure_listeners: Vec<MousePressureListener>,
1693 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1694 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1695 pub(crate) pinch_listeners: Vec<PinchListener>,
1696 pub(crate) key_down_listeners: Vec<KeyDownListener>,
1697 pub(crate) key_up_listeners: Vec<KeyUpListener>,
1698 pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
1699 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1700 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1701 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1702 pub(crate) click_listeners: Vec<ClickListener>,
1703 pub(crate) aux_click_listeners: Vec<ClickListener>,
1704 pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
1705 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
1706 pub(crate) tooltip_builder: Option<TooltipBuilder>,
1707 pub(crate) window_control: Option<WindowControlArea>,
1708 pub(crate) hitbox_behavior: HitboxBehavior,
1709 pub(crate) tab_index: Option<isize>,
1710 pub(crate) tab_group: bool,
1711 pub(crate) tab_stop: bool,
1712
1713 #[cfg(any(feature = "inspector", debug_assertions))]
1714 pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
1715
1716 #[cfg(any(test, feature = "test-support"))]
1717 pub(crate) debug_selector: Option<String>,
1718}
1719
1720impl Interactivity {
1721 pub fn request_layout(
1723 &mut self,
1724 global_id: Option<&GlobalElementId>,
1725 _inspector_id: Option<&InspectorElementId>,
1726 window: &mut Window,
1727 cx: &mut App,
1728 f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
1729 ) -> LayoutId {
1730 #[cfg(any(feature = "inspector", debug_assertions))]
1731 window.with_inspector_state(
1732 _inspector_id,
1733 cx,
1734 |inspector_state: &mut Option<DivInspectorState>, _window| {
1735 if let Some(inspector_state) = inspector_state {
1736 self.base_style = inspector_state.base_style.clone();
1737 } else {
1738 *inspector_state = Some(DivInspectorState {
1739 base_style: self.base_style.clone(),
1740 bounds: Default::default(),
1741 content_size: Default::default(),
1742 })
1743 }
1744 },
1745 );
1746
1747 window.with_optional_element_state::<InteractiveElementState, _>(
1748 global_id,
1749 |element_state, window| {
1750 let mut element_state =
1751 element_state.map(|element_state| element_state.unwrap_or_default());
1752
1753 if let Some(element_state) = element_state.as_ref()
1754 && cx.has_active_drag()
1755 {
1756 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
1757 *pending_mouse_down.borrow_mut() = None;
1758 }
1759 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1760 *clicked_state.borrow_mut() = ElementClickedState::default();
1761 }
1762 }
1763
1764 if self.focusable
1769 && self.tracked_focus_handle.is_none()
1770 && let Some(element_state) = element_state.as_mut()
1771 {
1772 let mut handle = element_state
1773 .focus_handle
1774 .get_or_insert_with(|| cx.focus_handle())
1775 .clone()
1776 .tab_stop(self.tab_stop);
1777
1778 if let Some(index) = self.tab_index {
1779 handle = handle.tab_index(index);
1780 }
1781
1782 self.tracked_focus_handle = Some(handle);
1783 }
1784
1785 if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
1786 self.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
1787 } else if (self.base_style.overflow.x == Some(Overflow::Scroll)
1788 || self.base_style.overflow.y == Some(Overflow::Scroll))
1789 && let Some(element_state) = element_state.as_mut()
1790 {
1791 self.scroll_offset = Some(
1792 element_state
1793 .scroll_offset
1794 .get_or_insert_with(Rc::default)
1795 .clone(),
1796 );
1797 }
1798
1799 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
1800 let layout_id = f(style, window, cx);
1801 (layout_id, element_state)
1802 },
1803 )
1804 }
1805
1806 pub fn prepaint<R>(
1808 &mut self,
1809 global_id: Option<&GlobalElementId>,
1810 _inspector_id: Option<&InspectorElementId>,
1811 bounds: Bounds<Pixels>,
1812 content_size: Size<Pixels>,
1813 window: &mut Window,
1814 cx: &mut App,
1815 f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
1816 ) -> R {
1817 self.content_size = content_size;
1818
1819 #[cfg(any(feature = "inspector", debug_assertions))]
1820 window.with_inspector_state(
1821 _inspector_id,
1822 cx,
1823 |inspector_state: &mut Option<DivInspectorState>, _window| {
1824 if let Some(inspector_state) = inspector_state {
1825 inspector_state.bounds = bounds;
1826 inspector_state.content_size = content_size;
1827 }
1828 },
1829 );
1830
1831 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1832 window.set_focus_handle(focus_handle, cx);
1833 }
1834 window.with_optional_element_state::<InteractiveElementState, _>(
1835 global_id,
1836 |element_state, window| {
1837 let mut element_state =
1838 element_state.map(|element_state| element_state.unwrap_or_default());
1839 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
1840 let bounds = Self::resolve_sticky_bounds(bounds, &style, window);
1841
1842 if let Some(element_state) = element_state.as_mut() {
1843 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1844 let clicked_state = clicked_state.borrow();
1845 self.active = Some(clicked_state.element);
1846 }
1847 if self.hover_style.is_some() || self.group_hover_style.is_some() {
1848 element_state
1849 .hover_state
1850 .get_or_insert_with(Default::default);
1851 }
1852 if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
1853 if self.tooltip_builder.is_some() {
1854 self.tooltip_id = set_tooltip_on_window(active_tooltip, window);
1855 } else {
1856 element_state.active_tooltip.take();
1858 }
1859 }
1860 }
1861
1862 window.with_text_style(style.text_style().cloned(), |window| {
1863 window.with_content_mask(
1864 style.overflow_mask(bounds, window.rem_size()),
1865 |window| {
1866 let hitbox = if self.should_insert_hitbox(&style, window, cx) {
1867 Some(window.insert_hitbox(bounds, self.hitbox_behavior))
1868 } else {
1869 None
1870 };
1871
1872 let scroll_offset =
1873 self.clamp_scroll_position(bounds, &style, window, cx);
1874 let result = if self.scroll_offset.is_some() {
1875 window.with_sticky_viewport(bounds, |window| {
1876 f(&style, scroll_offset, hitbox, window, cx)
1877 })
1878 } else {
1879 f(&style, scroll_offset, hitbox, window, cx)
1880 };
1881 (result, element_state)
1882 },
1883 )
1884 })
1885 },
1886 )
1887 }
1888
1889 fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
1890 self.hitbox_behavior != HitboxBehavior::Normal
1891 || self.window_control.is_some()
1892 || style.mouse_cursor.is_some()
1893 || self.group.is_some()
1894 || self.scroll_offset.is_some()
1895 || self.tracked_focus_handle.is_some()
1896 || self.hover_style.is_some()
1897 || self.group_hover_style.is_some()
1898 || self.hover_listener.is_some()
1899 || !self.mouse_up_listeners.is_empty()
1900 || !self.mouse_pressure_listeners.is_empty()
1901 || !self.mouse_down_listeners.is_empty()
1902 || !self.mouse_move_listeners.is_empty()
1903 || !self.click_listeners.is_empty()
1904 || !self.aux_click_listeners.is_empty()
1905 || !self.scroll_wheel_listeners.is_empty()
1906 || self.has_pinch_listeners()
1907 || self.drag_listener.is_some()
1908 || !self.drop_listeners.is_empty()
1909 || self.tooltip_builder.is_some()
1910 || window.is_inspector_picking(cx)
1911 }
1912
1913 fn resolve_sticky_bounds(
1926 bounds: Bounds<Pixels>,
1927 style: &Style,
1928 window: &Window,
1929 ) -> Bounds<Pixels> {
1930 if style.position != Position::Sticky {
1931 return bounds;
1932 }
1933 let Some(viewport) = window.sticky_viewport() else {
1934 return bounds;
1935 };
1936
1937 let rem_size = window.rem_size();
1938 let width = AbsoluteLength::Pixels(bounds.size.width);
1939 let height = AbsoluteLength::Pixels(bounds.size.height);
1940 let mut result = bounds;
1941
1942 if let Length::Definite(top) = style.inset.top {
1943 let top = top.to_pixels(height, rem_size);
1944 result.origin.y = result.origin.y.max(viewport.origin.y + top);
1945 }
1946 if let Length::Definite(left) = style.inset.left {
1947 let left = left.to_pixels(width, rem_size);
1948 result.origin.x = result.origin.x.max(viewport.origin.x + left);
1949 }
1950 if let Length::Definite(bottom) = style.inset.bottom {
1951 let bottom = bottom.to_pixels(height, rem_size);
1952 let max_y = viewport.origin.y + viewport.size.height - bottom - bounds.size.height;
1953 result.origin.y = result.origin.y.min(max_y);
1954 }
1955 if let Length::Definite(right) = style.inset.right {
1956 let right = right.to_pixels(width, rem_size);
1957 let max_x = viewport.origin.x + viewport.size.width - right - bounds.size.width;
1958 result.origin.x = result.origin.x.min(max_x);
1959 }
1960
1961 result
1962 }
1963
1964 fn clamp_scroll_position(
1965 &self,
1966 bounds: Bounds<Pixels>,
1967 style: &Style,
1968 window: &mut Window,
1969 _cx: &mut App,
1970 ) -> Point<Pixels> {
1971 fn round_to_two_decimals(pixels: Pixels) -> Pixels {
1972 const ROUNDING_FACTOR: f32 = 100.0;
1973 (pixels * ROUNDING_FACTOR).round() / ROUNDING_FACTOR
1974 }
1975
1976 if let Some(scroll_offset) = self.scroll_offset.as_ref() {
1977 let mut scroll_to_bottom = false;
1978 let mut tracked_scroll_handle = self
1979 .tracked_scroll_handle
1980 .as_ref()
1981 .map(|handle| handle.0.borrow_mut());
1982 if let Some(mut scroll_handle_state) = tracked_scroll_handle.as_deref_mut() {
1983 scroll_handle_state.overflow = style.overflow;
1984 scroll_to_bottom = mem::take(&mut scroll_handle_state.scroll_to_bottom);
1985 }
1986
1987 let rem_size = window.rem_size();
1988 let padding = style.padding.to_pixels(bounds.size.into(), rem_size);
1989 let padding_size = size(padding.left + padding.right, padding.top + padding.bottom);
1990 let padded_content_size = self.content_size + padding_size;
1997 let scroll_max = Point::from(padded_content_size - bounds.size)
1998 .map(round_to_two_decimals)
1999 .max(&Default::default());
2000 let mut scroll_offset = scroll_offset.borrow_mut();
2003
2004 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.x, px(0.));
2005 if scroll_to_bottom {
2006 scroll_offset.y = -scroll_max.y;
2007 } else {
2008 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.y, px(0.));
2009 }
2010
2011 if let Some(mut scroll_handle_state) = tracked_scroll_handle {
2012 scroll_handle_state.max_offset = scroll_max;
2013 scroll_handle_state.bounds = bounds;
2014 }
2015
2016 *scroll_offset
2017 } else {
2018 Point::default()
2019 }
2020 }
2021
2022 pub fn paint(
2031 &mut self,
2032 global_id: Option<&GlobalElementId>,
2033 _inspector_id: Option<&InspectorElementId>,
2034 bounds: Bounds<Pixels>,
2035 hitbox: Option<&Hitbox>,
2036 window: &mut Window,
2037 cx: &mut App,
2038 f: impl FnOnce(&Style, &mut Window, &mut App),
2039 ) {
2040 self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window));
2041 window.with_optional_element_state::<InteractiveElementState, _>(
2042 global_id,
2043 |element_state, window| {
2044 let mut element_state =
2045 element_state.map(|element_state| element_state.unwrap_or_default());
2046
2047 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2048 let bounds = Self::resolve_sticky_bounds(bounds, &style, window);
2049
2050 #[cfg(any(feature = "test-support", test))]
2051 if let Some(debug_selector) = &self.debug_selector {
2052 window
2053 .next_frame
2054 .debug_bounds
2055 .insert(debug_selector.clone(), bounds);
2056 }
2057
2058 self.paint_hover_group_handler(window, cx);
2059
2060 if style.visibility == Visibility::Hidden {
2061 return ((), element_state);
2062 }
2063
2064 let mut tab_group = None;
2065 if self.tab_group {
2066 tab_group = self.tab_index;
2067 }
2068 if let Some(focus_handle) = &self.tracked_focus_handle {
2069 window.next_frame.tab_stops.insert(focus_handle);
2070 }
2071
2072 window.with_element_opacity(style.opacity, |window| {
2073 style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
2074 window.with_text_style(style.text_style().cloned(), |window| {
2075 window.with_content_mask(
2076 style.overflow_mask(bounds, window.rem_size()),
2077 |window| {
2078 window.with_tab_group(tab_group, |window| {
2079 if let Some(hitbox) = hitbox {
2080 #[cfg(debug_assertions)]
2081 self.paint_debug_info(
2082 global_id, hitbox, &style, window, cx,
2083 );
2084
2085 if let Some(drag) = cx.active_drag.as_ref() {
2086 if let Some(mouse_cursor) = drag.cursor_style {
2087 window.set_window_cursor_style(mouse_cursor);
2088 }
2089 } else {
2090 if let Some(mouse_cursor) = style.mouse_cursor {
2091 window.set_cursor_style(mouse_cursor, hitbox);
2092 }
2093 }
2094
2095 if let Some(group) = self.group.clone() {
2096 GroupHitboxes::push(group, hitbox.id, cx);
2097 }
2098
2099 if let Some(area) = self.window_control {
2100 window.insert_window_control_hitbox(
2101 area,
2102 hitbox.clone(),
2103 );
2104 }
2105
2106 self.paint_mouse_listeners(
2107 hitbox,
2108 element_state.as_mut(),
2109 window,
2110 cx,
2111 );
2112 self.paint_scroll_listener(hitbox, &style, window, cx);
2113 }
2114
2115 self.paint_keyboard_listeners(window, cx);
2116 if self.scroll_offset.is_some() {
2117 window.with_sticky_viewport(bounds, |window| {
2118 f(&style, window, cx);
2119 });
2120 } else {
2121 f(&style, window, cx);
2122 }
2123
2124 if let Some(_hitbox) = hitbox {
2125 #[cfg(any(feature = "inspector", debug_assertions))]
2126 window.insert_inspector_hitbox(
2127 _hitbox.id,
2128 _inspector_id,
2129 cx,
2130 );
2131
2132 if let Some(group) = self.group.as_ref() {
2133 GroupHitboxes::pop(group, cx);
2134 }
2135 }
2136 })
2137 },
2138 );
2139 });
2140 });
2141 });
2142
2143 ((), element_state)
2144 },
2145 );
2146 }
2147
2148 #[cfg(debug_assertions)]
2149 fn paint_debug_info(
2150 &self,
2151 global_id: Option<&GlobalElementId>,
2152 hitbox: &Hitbox,
2153 style: &Style,
2154 window: &mut Window,
2155 cx: &mut App,
2156 ) {
2157 use crate::{BorderStyle, TextAlign};
2158
2159 if let Some(global_id) = global_id
2160 && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
2161 && hitbox.is_hovered(window)
2162 {
2163 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
2164 let element_id = format!("{global_id:?}");
2165 let str_len = element_id.len();
2166
2167 let render_debug_text = |window: &mut Window| {
2168 if let Some(text) = window
2169 .text_system()
2170 .shape_text(
2171 element_id.into(),
2172 FONT_SIZE,
2173 &[window.text_style().to_run(str_len)],
2174 None,
2175 None,
2176 )
2177 .ok()
2178 .and_then(|mut text| text.pop())
2179 {
2180 text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
2181 .ok();
2182
2183 let text_bounds = crate::Bounds {
2184 origin: hitbox.origin,
2185 size: text.size(FONT_SIZE),
2186 };
2187 if let Some(source_location) = self.source_location
2188 && text_bounds.contains(&window.mouse_position())
2189 && window.modifiers().secondary()
2190 {
2191 let secondary_held = window.modifiers().secondary();
2192 window.on_key_event({
2193 move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| {
2194 if e.modifiers.secondary() != secondary_held
2195 && text_bounds.contains(&window.mouse_position())
2196 {
2197 window.refresh();
2198 }
2199 }
2200 });
2201
2202 let was_hovered = hitbox.is_hovered(window);
2203 let current_view = window.current_view();
2204 window.on_mouse_event({
2205 let hitbox = hitbox.clone();
2206 move |_: &MouseMoveEvent, phase, window, cx| {
2207 if phase == DispatchPhase::Capture {
2208 let hovered = hitbox.is_hovered(window);
2209 if hovered != was_hovered {
2210 cx.notify(current_view)
2211 }
2212 }
2213 }
2214 });
2215
2216 window.on_mouse_event({
2217 let hitbox = hitbox.clone();
2218 move |e: &crate::MouseDownEvent, phase, window, cx| {
2219 if text_bounds.contains(&e.position)
2220 && phase.capture()
2221 && hitbox.is_hovered(window)
2222 {
2223 cx.stop_propagation();
2224 let Ok(dir) = std::env::current_dir() else {
2225 return;
2226 };
2227
2228 eprintln!(
2229 "This element was created at:\n{}:{}:{}",
2230 dir.join(source_location.file()).to_string_lossy(),
2231 source_location.line(),
2232 source_location.column()
2233 );
2234 }
2235 }
2236 });
2237 window.paint_quad(crate::outline(
2238 crate::Bounds {
2239 origin: hitbox.origin
2240 + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
2241 size: crate::Size {
2242 width: text_bounds.size.width,
2243 height: crate::px(1.),
2244 },
2245 },
2246 crate::red(),
2247 BorderStyle::default(),
2248 ))
2249 }
2250 }
2251 };
2252
2253 window.with_text_style(
2254 Some(crate::TextStyleRefinement {
2255 color: Some(crate::red()),
2256 line_height: Some(FONT_SIZE.into()),
2257 background_color: Some(crate::white()),
2258 ..Default::default()
2259 }),
2260 render_debug_text,
2261 )
2262 }
2263 }
2264
2265 fn paint_mouse_listeners(
2266 &mut self,
2267 hitbox: &Hitbox,
2268 element_state: Option<&mut InteractiveElementState>,
2269 window: &mut Window,
2270 cx: &mut App,
2271 ) {
2272 let is_focused = self
2273 .tracked_focus_handle
2274 .as_ref()
2275 .map(|handle| handle.is_focused(window))
2276 .unwrap_or(false);
2277
2278 if let Some(focus_handle) = self.tracked_focus_handle.clone() {
2282 let hitbox = hitbox.clone();
2283 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, cx| {
2284 if phase == DispatchPhase::Bubble
2285 && hitbox.is_hovered(window)
2286 && !window.default_prevented()
2287 {
2288 window.focus(&focus_handle, cx);
2289 window.prevent_default();
2292 }
2293 });
2294 }
2295
2296 for listener in self.mouse_down_listeners.drain(..) {
2297 let hitbox = hitbox.clone();
2298 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
2299 listener(event, phase, &hitbox, window, cx);
2300 })
2301 }
2302
2303 for listener in self.mouse_up_listeners.drain(..) {
2304 let hitbox = hitbox.clone();
2305 window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
2306 listener(event, phase, &hitbox, window, cx);
2307 })
2308 }
2309
2310 for listener in self.mouse_pressure_listeners.drain(..) {
2311 let hitbox = hitbox.clone();
2312 window.on_mouse_event(move |event: &MousePressureEvent, phase, window, cx| {
2313 listener(event, phase, &hitbox, window, cx);
2314 })
2315 }
2316
2317 for listener in self.mouse_move_listeners.drain(..) {
2318 let hitbox = hitbox.clone();
2319 window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
2320 listener(event, phase, &hitbox, window, cx);
2321 })
2322 }
2323
2324 for listener in self.scroll_wheel_listeners.drain(..) {
2325 let hitbox = hitbox.clone();
2326 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2327 listener(event, phase, &hitbox, window, cx);
2328 })
2329 }
2330
2331 for listener in self.pinch_listeners.drain(..) {
2332 let hitbox = hitbox.clone();
2333 window.on_mouse_event(move |event: &PinchEvent, phase, window, cx| {
2334 listener(event, phase, &hitbox, window, cx);
2335 })
2336 }
2337
2338 if self.hover_style.is_some()
2339 || self.base_style.mouse_cursor.is_some()
2340 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
2341 {
2342 let hitbox = hitbox.clone();
2343 let hover_state = self.hover_style.as_ref().and_then(|_| {
2344 element_state
2345 .as_ref()
2346 .and_then(|state| state.hover_state.as_ref())
2347 .cloned()
2348 });
2349 let current_view = window.current_view();
2350
2351 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2352 let hovered = hitbox.is_hovered(window);
2353 let was_hovered = hover_state
2354 .as_ref()
2355 .is_some_and(|state| state.borrow().element);
2356 if phase == DispatchPhase::Capture && hovered != was_hovered {
2357 if let Some(hover_state) = &hover_state {
2358 hover_state.borrow_mut().element = hovered;
2359 cx.notify(current_view);
2360 }
2361 }
2362 });
2363 }
2364
2365 if let Some(group_hover) = self.group_hover_style.as_ref() {
2366 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
2367 let hover_state = element_state
2368 .as_ref()
2369 .and_then(|element| element.hover_state.as_ref())
2370 .cloned();
2371 let current_view = window.current_view();
2372
2373 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2374 let group_hovered = group_hitbox_id.is_hovered(window);
2375 let was_group_hovered = hover_state
2376 .as_ref()
2377 .is_some_and(|state| state.borrow().group);
2378 if phase == DispatchPhase::Capture && group_hovered != was_group_hovered {
2379 if let Some(hover_state) = &hover_state {
2380 hover_state.borrow_mut().group = group_hovered;
2381 }
2382 cx.notify(current_view);
2383 }
2384 });
2385 }
2386 }
2387
2388 let drag_cursor_style = self.base_style.as_ref().mouse_cursor;
2389
2390 let mut drag_listener = mem::take(&mut self.drag_listener);
2391 let drop_listeners = mem::take(&mut self.drop_listeners);
2392 let click_listeners = mem::take(&mut self.click_listeners);
2393 let aux_click_listeners = mem::take(&mut self.aux_click_listeners);
2394 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
2395
2396 if !drop_listeners.is_empty() {
2397 let hitbox = hitbox.clone();
2398 window.on_mouse_event({
2399 move |_: &MouseUpEvent, phase, window, cx| {
2400 if let Some(drag) = &cx.active_drag
2401 && phase == DispatchPhase::Bubble
2402 && hitbox.is_hovered(window)
2403 {
2404 let drag_state_type = drag.value.as_ref().type_id();
2405 for (drop_state_type, listener) in &drop_listeners {
2406 if *drop_state_type == drag_state_type {
2407 let drag = cx
2408 .active_drag
2409 .take()
2410 .expect("checked for type drag state type above");
2411
2412 let mut can_drop = true;
2413 if let Some(predicate) = &can_drop_predicate {
2414 can_drop = predicate(drag.value.as_ref(), window, cx);
2415 }
2416
2417 if can_drop {
2418 listener(drag.value.as_ref(), window, cx);
2419 window.refresh();
2420 cx.stop_propagation();
2421 }
2422 }
2423 }
2424 }
2425 }
2426 });
2427 }
2428
2429 if let Some(element_state) = element_state {
2430 if !click_listeners.is_empty()
2431 || !aux_click_listeners.is_empty()
2432 || drag_listener.is_some()
2433 {
2434 let pending_mouse_down = element_state
2435 .pending_mouse_down
2436 .get_or_insert_with(Default::default)
2437 .clone();
2438
2439 let clicked_state = element_state
2440 .clicked_state
2441 .get_or_insert_with(Default::default)
2442 .clone();
2443
2444 window.on_mouse_event({
2445 let pending_mouse_down = pending_mouse_down.clone();
2446 let hitbox = hitbox.clone();
2447 let has_aux_click_listeners = !aux_click_listeners.is_empty();
2448 move |event: &MouseDownEvent, phase, window, _cx| {
2449 if phase == DispatchPhase::Bubble
2450 && (event.button == MouseButton::Left || has_aux_click_listeners)
2451 && hitbox.is_hovered(window)
2452 {
2453 *pending_mouse_down.borrow_mut() = Some(event.clone());
2454 window.refresh();
2455 }
2456 }
2457 });
2458
2459 window.on_mouse_event({
2460 let pending_mouse_down = pending_mouse_down.clone();
2461 let hitbox = hitbox.clone();
2462 move |event: &MouseMoveEvent, phase, window, cx| {
2463 if phase == DispatchPhase::Capture {
2464 return;
2465 }
2466
2467 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2468 if let Some(mouse_down) = pending_mouse_down.clone()
2469 && !cx.has_active_drag()
2470 && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
2471 && let Some((drag_value, drag_listener)) = drag_listener.take()
2472 && mouse_down.button == MouseButton::Left
2473 {
2474 *clicked_state.borrow_mut() = ElementClickedState::default();
2475 let cursor_offset = event.position - hitbox.origin;
2476 let drag =
2477 (drag_listener)(drag_value.as_ref(), cursor_offset, window, cx);
2478 cx.active_drag = Some(AnyDrag {
2479 view: drag,
2480 value: drag_value,
2481 cursor_offset,
2482 cursor_style: drag_cursor_style,
2483 });
2484 pending_mouse_down.take();
2485 window.refresh();
2486 cx.stop_propagation();
2487 }
2488 }
2489 });
2490
2491 if is_focused {
2492 window.on_key_event({
2494 let click_listeners = click_listeners.clone();
2495 let hitbox = hitbox.clone();
2496 move |event: &KeyUpEvent, phase, window, cx| {
2497 if phase.bubble() && !window.default_prevented() {
2498 let stroke = &event.keystroke;
2499 let keyboard_button = if stroke.key.eq("enter") {
2500 Some(KeyboardButton::Enter)
2501 } else if stroke.key.eq("space") {
2502 Some(KeyboardButton::Space)
2503 } else {
2504 None
2505 };
2506
2507 if let Some(button) = keyboard_button
2508 && !stroke.modifiers.modified()
2509 {
2510 let click_event = ClickEvent::Keyboard(KeyboardClickEvent {
2511 button,
2512 bounds: hitbox.bounds,
2513 });
2514
2515 for listener in &click_listeners {
2516 listener(&click_event, window, cx);
2517 }
2518 }
2519 }
2520 }
2521 });
2522 }
2523
2524 window.on_mouse_event({
2525 let mut captured_mouse_down = None;
2526 let hitbox = hitbox.clone();
2527 move |event: &MouseUpEvent, phase, window, cx| match phase {
2528 DispatchPhase::Capture => {
2532 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2533 if pending_mouse_down.is_some() && hitbox.is_hovered(window) {
2534 captured_mouse_down = pending_mouse_down.take();
2535 window.refresh();
2536 } else if pending_mouse_down.is_some() {
2537 pending_mouse_down.take();
2542 window.refresh();
2543 }
2544 }
2545 DispatchPhase::Bubble => {
2547 if let Some(mouse_down) = captured_mouse_down.take() {
2548 let btn = mouse_down.button;
2549
2550 let mouse_click = ClickEvent::Mouse(MouseClickEvent {
2551 down: mouse_down,
2552 up: event.clone(),
2553 });
2554
2555 match btn {
2556 MouseButton::Left => {
2557 for listener in &click_listeners {
2558 listener(&mouse_click, window, cx);
2559 }
2560 }
2561 _ => {
2562 for listener in &aux_click_listeners {
2563 listener(&mouse_click, window, cx);
2564 }
2565 }
2566 }
2567 }
2568 }
2569 }
2570 });
2571 }
2572
2573 if let Some(hover_listener) = self.hover_listener.take() {
2574 let hitbox = hitbox.clone();
2575 let was_hovered = element_state
2576 .hover_listener_state
2577 .get_or_insert_with(Default::default)
2578 .clone();
2579 let has_mouse_down = element_state
2580 .pending_mouse_down
2581 .get_or_insert_with(Default::default)
2582 .clone();
2583
2584 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2585 if phase != DispatchPhase::Bubble {
2586 return;
2587 }
2588 let is_hovered = has_mouse_down.borrow().is_none()
2589 && !cx.has_active_drag()
2590 && hitbox.is_hovered(window);
2591 let mut was_hovered = was_hovered.borrow_mut();
2592
2593 if is_hovered != *was_hovered {
2594 *was_hovered = is_hovered;
2595 drop(was_hovered);
2596
2597 hover_listener(&is_hovered, window, cx);
2598 }
2599 });
2600 }
2601
2602 if let Some(tooltip_builder) = self.tooltip_builder.take() {
2603 let active_tooltip = element_state
2604 .active_tooltip
2605 .get_or_insert_with(Default::default)
2606 .clone();
2607 let pending_mouse_down = element_state
2608 .pending_mouse_down
2609 .get_or_insert_with(Default::default)
2610 .clone();
2611
2612 let tooltip_is_hoverable = tooltip_builder.hoverable;
2613 let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| {
2614 Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable))
2615 });
2616 let check_is_hovered_during_prepaint = Rc::new({
2618 let pending_mouse_down = pending_mouse_down.clone();
2619 let source_bounds = hitbox.bounds;
2620 move |window: &Window| {
2621 !window.last_input_was_keyboard()
2622 && pending_mouse_down.borrow().is_none()
2623 && source_bounds.contains(&window.mouse_position())
2624 }
2625 });
2626 let check_is_hovered = Rc::new({
2627 let hitbox = hitbox.clone();
2628 move |window: &Window| {
2629 pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window)
2630 }
2631 });
2632 register_tooltip_mouse_handlers(
2633 &active_tooltip,
2634 self.tooltip_id,
2635 build_tooltip,
2636 check_is_hovered,
2637 check_is_hovered_during_prepaint,
2638 window,
2639 );
2640 }
2641
2642 let active_state = element_state
2645 .clicked_state
2646 .get_or_insert_with(Default::default)
2647 .clone();
2648
2649 {
2650 let active_state = active_state.clone();
2651 window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| {
2652 if phase == DispatchPhase::Capture && active_state.borrow().is_clicked() {
2653 *active_state.borrow_mut() = ElementClickedState::default();
2654 window.refresh();
2655 }
2656 });
2657 }
2658
2659 {
2660 let active_group_hitbox = self
2661 .group_active_style
2662 .as_ref()
2663 .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
2664 let hitbox = hitbox.clone();
2665 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| {
2666 if phase == DispatchPhase::Bubble && !window.default_prevented() {
2667 let group_hovered = active_group_hitbox
2668 .is_some_and(|group_hitbox_id| group_hitbox_id.is_hovered(window));
2669 let element_hovered = hitbox.is_hovered(window);
2670 if group_hovered || element_hovered {
2671 *active_state.borrow_mut() = ElementClickedState {
2672 group: group_hovered,
2673 element: element_hovered,
2674 };
2675 window.refresh();
2676 }
2677 }
2678 });
2679 }
2680 }
2681 }
2682
2683 fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) {
2684 let key_down_listeners = mem::take(&mut self.key_down_listeners);
2685 let key_up_listeners = mem::take(&mut self.key_up_listeners);
2686 let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
2687 let action_listeners = mem::take(&mut self.action_listeners);
2688 if let Some(context) = self.key_context.clone() {
2689 window.set_key_context(context);
2690 }
2691
2692 for listener in key_down_listeners {
2693 window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| {
2694 listener(event, phase, window, cx);
2695 })
2696 }
2697
2698 for listener in key_up_listeners {
2699 window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| {
2700 listener(event, phase, window, cx);
2701 })
2702 }
2703
2704 for listener in modifiers_changed_listeners {
2705 window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| {
2706 listener(event, window, cx);
2707 })
2708 }
2709
2710 for (action_type, listener) in action_listeners {
2711 window.on_action(action_type, listener)
2712 }
2713 }
2714
2715 fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) {
2716 let group_hitbox = self
2717 .group_hover_style
2718 .as_ref()
2719 .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
2720
2721 if let Some(group_hitbox) = group_hitbox {
2722 let was_hovered = group_hitbox.is_hovered(window);
2723 let current_view = window.current_view();
2724 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2725 let hovered = group_hitbox.is_hovered(window);
2726 if phase == DispatchPhase::Capture && hovered != was_hovered {
2727 cx.notify(current_view);
2728 }
2729 });
2730 }
2731 }
2732
2733 fn paint_scroll_listener(
2734 &self,
2735 hitbox: &Hitbox,
2736 style: &Style,
2737 window: &mut Window,
2738 _cx: &mut App,
2739 ) {
2740 if let Some(scroll_offset) = self.scroll_offset.clone() {
2741 let overflow = style.overflow;
2742 let allow_concurrent_scroll = style.allow_concurrent_scroll;
2743 let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
2744 let line_height = window.line_height();
2745 let hitbox = hitbox.clone();
2746 let current_view = window.current_view();
2747 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2748 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
2749 let mut scroll_offset = scroll_offset.borrow_mut();
2750 let old_scroll_offset = *scroll_offset;
2751 let delta = event.delta.pixel_delta(line_height);
2752
2753 let mut delta_x = Pixels::ZERO;
2754 if overflow.x == Overflow::Scroll {
2755 if !delta.x.is_zero() {
2756 delta_x = delta.x;
2757 } else if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll {
2758 delta_x = delta.y;
2759 }
2760 }
2761 let mut delta_y = Pixels::ZERO;
2762 if overflow.y == Overflow::Scroll {
2763 if !delta.y.is_zero() {
2764 delta_y = delta.y;
2765 } else if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll {
2766 delta_y = delta.x;
2767 }
2768 }
2769 if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
2770 if delta_x.abs() > delta_y.abs() {
2771 delta_y = Pixels::ZERO;
2772 } else {
2773 delta_x = Pixels::ZERO;
2774 }
2775 }
2776 scroll_offset.y += delta_y;
2777 scroll_offset.x += delta_x;
2778 if *scroll_offset != old_scroll_offset {
2779 cx.notify(current_view);
2780 }
2781 }
2782 });
2783 }
2784 }
2785
2786 pub fn compute_style(
2788 &self,
2789 global_id: Option<&GlobalElementId>,
2790 hitbox: Option<&Hitbox>,
2791 window: &mut Window,
2792 cx: &mut App,
2793 ) -> Style {
2794 window.with_optional_element_state(global_id, |element_state, window| {
2795 let mut element_state =
2796 element_state.map(|element_state| element_state.unwrap_or_default());
2797 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2798 (style, element_state)
2799 })
2800 }
2801
2802 fn compute_style_internal(
2804 &self,
2805 hitbox: Option<&Hitbox>,
2806 element_state: Option<&mut InteractiveElementState>,
2807 window: &mut Window,
2808 cx: &mut App,
2809 ) -> Style {
2810 let mut style = Style::default();
2811 style.refine(&self.base_style);
2812
2813 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2814 if let Some(in_focus_style) = self.in_focus_style.as_ref()
2815 && focus_handle.within_focused(window, cx)
2816 {
2817 style.refine(in_focus_style);
2818 }
2819
2820 if let Some(focus_style) = self.focus_style.as_ref()
2821 && focus_handle.is_focused(window)
2822 {
2823 style.refine(focus_style);
2824 }
2825
2826 if let Some(focus_visible_style) = self.focus_visible_style.as_ref()
2827 && focus_handle.is_focused(window)
2828 && window.last_input_was_keyboard()
2829 {
2830 style.refine(focus_visible_style);
2831 }
2832 }
2833
2834 if !cx.has_active_drag() {
2835 if let Some(group_hover) = self.group_hover_style.as_ref() {
2836 let is_group_hovered =
2837 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
2838 group_hitbox_id.is_hovered(window)
2839 } else if let Some(element_state) = element_state.as_ref() {
2840 element_state
2841 .hover_state
2842 .as_ref()
2843 .map(|state| state.borrow().group)
2844 .unwrap_or(false)
2845 } else {
2846 false
2847 };
2848
2849 if is_group_hovered {
2850 style.refine(&group_hover.style);
2851 }
2852 }
2853
2854 if let Some(hover_style) = self.hover_style.as_ref() {
2855 let is_hovered = if let Some(hitbox) = hitbox {
2856 hitbox.is_hovered(window)
2857 } else if let Some(element_state) = element_state.as_ref() {
2858 element_state
2859 .hover_state
2860 .as_ref()
2861 .map(|state| state.borrow().element)
2862 .unwrap_or(false)
2863 } else {
2864 false
2865 };
2866
2867 if is_hovered {
2868 style.refine(hover_style);
2869 }
2870 }
2871 }
2872
2873 if let Some(hitbox) = hitbox {
2874 if let Some(drag) = cx.active_drag.take() {
2875 let mut can_drop = true;
2876 if let Some(can_drop_predicate) = &self.can_drop_predicate {
2877 can_drop = can_drop_predicate(drag.value.as_ref(), window, cx);
2878 }
2879
2880 if can_drop {
2881 for (state_type, group_drag_style) in &self.group_drag_over_styles {
2882 if let Some(group_hitbox_id) =
2883 GroupHitboxes::get(&group_drag_style.group, cx)
2884 && *state_type == drag.value.as_ref().type_id()
2885 && group_hitbox_id.is_hovered(window)
2886 {
2887 style.refine(&group_drag_style.style);
2888 }
2889 }
2890
2891 for (state_type, build_drag_over_style) in &self.drag_over_styles {
2892 if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window)
2893 {
2894 style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx));
2895 }
2896 }
2897 }
2898
2899 style.mouse_cursor = drag.cursor_style;
2900 cx.active_drag = Some(drag);
2901 }
2902 }
2903
2904 if let Some(element_state) = element_state {
2905 let clicked_state = element_state
2906 .clicked_state
2907 .get_or_insert_with(Default::default)
2908 .borrow();
2909 if clicked_state.group
2910 && let Some(group) = self.group_active_style.as_ref()
2911 {
2912 style.refine(&group.style)
2913 }
2914
2915 if let Some(active_style) = self.active_style.as_ref()
2916 && clicked_state.element
2917 {
2918 style.refine(active_style)
2919 }
2920 }
2921
2922 style
2923 }
2924}
2925
2926#[derive(Default)]
2929pub struct InteractiveElementState {
2930 pub(crate) focus_handle: Option<FocusHandle>,
2931 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2932 pub(crate) hover_state: Option<Rc<RefCell<ElementHoverState>>>,
2933 pub(crate) hover_listener_state: Option<Rc<RefCell<bool>>>,
2934 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2935 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2936 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2937}
2938
2939#[derive(Copy, Clone, Default, Eq, PartialEq)]
2941pub struct ElementClickedState {
2942 pub group: bool,
2944
2945 pub element: bool,
2947}
2948
2949impl ElementClickedState {
2950 fn is_clicked(&self) -> bool {
2951 self.group || self.element
2952 }
2953}
2954
2955#[derive(Copy, Clone, Default, Eq, PartialEq)]
2957pub struct ElementHoverState {
2958 pub group: bool,
2960
2961 pub element: bool,
2963}
2964
2965pub(crate) enum ActiveTooltip {
2966 WaitingForShow { _task: Task<()> },
2968 Visible {
2970 tooltip: AnyTooltip,
2971 is_hoverable: bool,
2972 },
2973 WaitingForHide {
2976 tooltip: AnyTooltip,
2977 _task: Task<()>,
2978 },
2979}
2980
2981pub(crate) fn clear_active_tooltip(
2982 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2983 window: &mut Window,
2984) {
2985 match active_tooltip.borrow_mut().take() {
2986 None => {}
2987 Some(ActiveTooltip::WaitingForShow { .. }) => {}
2988 Some(ActiveTooltip::Visible { .. }) => window.refresh(),
2989 Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(),
2990 }
2991}
2992
2993pub(crate) fn clear_active_tooltip_if_not_hoverable(
2994 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2995 window: &mut Window,
2996) {
2997 let should_clear = match active_tooltip.borrow().as_ref() {
2998 None => false,
2999 Some(ActiveTooltip::WaitingForShow { .. }) => false,
3000 Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable,
3001 Some(ActiveTooltip::WaitingForHide { .. }) => false,
3002 };
3003 if should_clear {
3004 active_tooltip.borrow_mut().take();
3005 window.refresh();
3006 }
3007}
3008
3009pub(crate) fn set_tooltip_on_window(
3010 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3011 window: &mut Window,
3012) -> Option<TooltipId> {
3013 let tooltip = match active_tooltip.borrow().as_ref() {
3014 None => return None,
3015 Some(ActiveTooltip::WaitingForShow { .. }) => return None,
3016 Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(),
3017 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(),
3018 };
3019 Some(window.set_tooltip(tooltip))
3020}
3021
3022pub(crate) fn register_tooltip_mouse_handlers(
3023 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3024 tooltip_id: Option<TooltipId>,
3025 build_tooltip: Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3026 check_is_hovered: Rc<dyn Fn(&Window) -> bool>,
3027 check_is_hovered_during_prepaint: Rc<dyn Fn(&Window) -> bool>,
3028 window: &mut Window,
3029) {
3030 window.on_mouse_event({
3031 let active_tooltip = active_tooltip.clone();
3032 let build_tooltip = build_tooltip.clone();
3033 let check_is_hovered = check_is_hovered.clone();
3034 move |_: &MouseMoveEvent, phase, window, cx| {
3035 handle_tooltip_mouse_move(
3036 &active_tooltip,
3037 &build_tooltip,
3038 &check_is_hovered,
3039 &check_is_hovered_during_prepaint,
3040 phase,
3041 window,
3042 cx,
3043 )
3044 }
3045 });
3046
3047 window.on_mouse_event({
3048 let active_tooltip = active_tooltip.clone();
3049 move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| {
3050 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3051 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3052 }
3053 }
3054 });
3055
3056 window.on_mouse_event({
3057 let active_tooltip = active_tooltip.clone();
3058 move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| {
3059 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3060 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3061 }
3062 }
3063 });
3064}
3065
3066fn handle_tooltip_mouse_move(
3078 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3079 build_tooltip: &Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3080 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3081 check_is_hovered_during_prepaint: &Rc<dyn Fn(&Window) -> bool>,
3082 phase: DispatchPhase,
3083 window: &mut Window,
3084 cx: &mut App,
3085) {
3086 enum Action {
3089 None,
3090 CancelShow,
3091 ScheduleShow,
3092 }
3093
3094 let action = match active_tooltip.borrow().as_ref() {
3095 None => {
3096 let is_hovered = check_is_hovered(window);
3097 if is_hovered && phase.bubble() {
3098 Action::ScheduleShow
3099 } else {
3100 Action::None
3101 }
3102 }
3103 Some(ActiveTooltip::WaitingForShow { .. }) => {
3104 let is_hovered = check_is_hovered(window);
3105 if is_hovered {
3106 Action::None
3107 } else {
3108 Action::CancelShow
3109 }
3110 }
3111 Some(ActiveTooltip::Visible { .. }) | Some(ActiveTooltip::WaitingForHide { .. }) => {
3113 Action::None
3114 }
3115 };
3116
3117 match action {
3118 Action::None => {}
3119 Action::CancelShow => {
3120 active_tooltip.borrow_mut().take();
3122 }
3123 Action::ScheduleShow => {
3124 let delayed_show_task = window.spawn(cx, {
3125 let weak_active_tooltip = Rc::downgrade(active_tooltip);
3126 let build_tooltip = build_tooltip.clone();
3127 let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone();
3128 async move |cx| {
3129 cx.background_executor().timer(TOOLTIP_SHOW_DELAY).await;
3130 let Some(active_tooltip) = weak_active_tooltip.upgrade() else {
3131 return;
3132 };
3133 cx.update(|window, cx| {
3134 let new_tooltip =
3135 build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| {
3136 let weak_active_tooltip = Rc::downgrade(&active_tooltip);
3137 ActiveTooltip::Visible {
3138 tooltip: AnyTooltip {
3139 view,
3140 mouse_position: window.mouse_position(),
3141 check_visible_and_update: Rc::new(
3142 move |tooltip_bounds, window, cx| {
3143 let Some(active_tooltip) =
3144 weak_active_tooltip.upgrade()
3145 else {
3146 return false;
3147 };
3148 handle_tooltip_check_visible_and_update(
3149 &active_tooltip,
3150 tooltip_is_hoverable,
3151 &check_is_hovered_during_prepaint,
3152 tooltip_bounds,
3153 window,
3154 cx,
3155 )
3156 },
3157 ),
3158 },
3159 is_hoverable: tooltip_is_hoverable,
3160 }
3161 });
3162 *active_tooltip.borrow_mut() = new_tooltip;
3163 window.refresh();
3164 })
3165 .ok();
3166 }
3167 });
3168 active_tooltip
3169 .borrow_mut()
3170 .replace(ActiveTooltip::WaitingForShow {
3171 _task: delayed_show_task,
3172 });
3173 }
3174 }
3175}
3176
3177fn handle_tooltip_check_visible_and_update(
3181 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3182 tooltip_is_hoverable: bool,
3183 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3184 tooltip_bounds: Bounds<Pixels>,
3185 window: &mut Window,
3186 cx: &mut App,
3187) -> bool {
3188 enum Action {
3191 None,
3192 Hide,
3193 ScheduleHide(AnyTooltip),
3194 CancelHide(AnyTooltip),
3195 }
3196
3197 let is_hovered = check_is_hovered(window)
3198 || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position()));
3199 let action = match active_tooltip.borrow().as_ref() {
3200 Some(ActiveTooltip::Visible { tooltip, .. }) => {
3201 if is_hovered {
3202 Action::None
3203 } else {
3204 if tooltip_is_hoverable {
3205 Action::ScheduleHide(tooltip.clone())
3206 } else {
3207 Action::Hide
3208 }
3209 }
3210 }
3211 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => {
3212 if is_hovered {
3213 Action::CancelHide(tooltip.clone())
3214 } else {
3215 Action::None
3216 }
3217 }
3218 None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None,
3219 };
3220
3221 match action {
3222 Action::None => {}
3223 Action::Hide => clear_active_tooltip(active_tooltip, window),
3224 Action::ScheduleHide(tooltip) => {
3225 let delayed_hide_task = window.spawn(cx, {
3226 let weak_active_tooltip = Rc::downgrade(active_tooltip);
3227 async move |cx| {
3228 cx.background_executor()
3229 .timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
3230 .await;
3231 let Some(active_tooltip) = weak_active_tooltip.upgrade() else {
3232 return;
3233 };
3234 if active_tooltip.borrow_mut().take().is_some() {
3235 cx.update(|window, _cx| window.refresh()).ok();
3236 }
3237 }
3238 });
3239 active_tooltip
3240 .borrow_mut()
3241 .replace(ActiveTooltip::WaitingForHide {
3242 tooltip,
3243 _task: delayed_hide_task,
3244 });
3245 }
3246 Action::CancelHide(tooltip) => {
3247 active_tooltip.borrow_mut().replace(ActiveTooltip::Visible {
3249 tooltip,
3250 is_hoverable: true,
3251 });
3252 }
3253 }
3254
3255 active_tooltip.borrow().is_some()
3256}
3257
3258#[derive(Default)]
3259pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
3260
3261impl Global for GroupHitboxes {}
3262
3263impl GroupHitboxes {
3264 pub fn get(name: &SharedString, cx: &mut App) -> Option<HitboxId> {
3265 cx.default_global::<Self>()
3266 .0
3267 .get(name)
3268 .and_then(|bounds_stack| bounds_stack.last())
3269 .cloned()
3270 }
3271
3272 pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) {
3273 cx.default_global::<Self>()
3274 .0
3275 .entry(name)
3276 .or_default()
3277 .push(hitbox_id);
3278 }
3279
3280 pub fn pop(name: &SharedString, cx: &mut App) {
3281 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
3282 }
3283}
3284
3285pub struct Stateful<E> {
3287 pub(crate) element: E,
3288}
3289
3290impl<E> Styled for Stateful<E>
3291where
3292 E: Styled,
3293{
3294 fn style(&mut self) -> &mut StyleRefinement {
3295 self.element.style()
3296 }
3297}
3298
3299impl<E> StatefulInteractiveElement for Stateful<E>
3300where
3301 E: Element,
3302 Self: InteractiveElement,
3303{
3304}
3305
3306impl<E> InteractiveElement for Stateful<E>
3307where
3308 E: InteractiveElement,
3309{
3310 fn interactivity(&mut self) -> &mut Interactivity {
3311 self.element.interactivity()
3312 }
3313}
3314
3315impl<E> Element for Stateful<E>
3316where
3317 E: Element,
3318{
3319 type RequestLayoutState = E::RequestLayoutState;
3320 type PrepaintState = E::PrepaintState;
3321
3322 fn id(&self) -> Option<ElementId> {
3323 self.element.id()
3324 }
3325
3326 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
3327 self.element.source_location()
3328 }
3329
3330 fn request_layout(
3331 &mut self,
3332 id: Option<&GlobalElementId>,
3333 inspector_id: Option<&InspectorElementId>,
3334 window: &mut Window,
3335 cx: &mut App,
3336 ) -> (LayoutId, Self::RequestLayoutState) {
3337 self.element.request_layout(id, inspector_id, window, cx)
3338 }
3339
3340 fn prepaint(
3341 &mut self,
3342 id: Option<&GlobalElementId>,
3343 inspector_id: Option<&InspectorElementId>,
3344 bounds: Bounds<Pixels>,
3345 state: &mut Self::RequestLayoutState,
3346 window: &mut Window,
3347 cx: &mut App,
3348 ) -> E::PrepaintState {
3349 self.element
3350 .prepaint(id, inspector_id, bounds, state, window, cx)
3351 }
3352
3353 fn paint(
3354 &mut self,
3355 id: Option<&GlobalElementId>,
3356 inspector_id: Option<&InspectorElementId>,
3357 bounds: Bounds<Pixels>,
3358 request_layout: &mut Self::RequestLayoutState,
3359 prepaint: &mut Self::PrepaintState,
3360 window: &mut Window,
3361 cx: &mut App,
3362 ) {
3363 self.element.paint(
3364 id,
3365 inspector_id,
3366 bounds,
3367 request_layout,
3368 prepaint,
3369 window,
3370 cx,
3371 );
3372 }
3373}
3374
3375impl<E> IntoElement for Stateful<E>
3376where
3377 E: Element,
3378{
3379 type Element = Self;
3380
3381 fn into_element(self) -> Self::Element {
3382 self
3383 }
3384}
3385
3386impl<E> ParentElement for Stateful<E>
3387where
3388 E: ParentElement,
3389{
3390 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
3391 self.element.extend(elements)
3392 }
3393}
3394
3395#[derive(Clone)]
3398pub struct ScrollAnchor {
3399 handle: ScrollHandle,
3400 last_origin: Rc<RefCell<Point<Pixels>>>,
3401}
3402
3403impl ScrollAnchor {
3404 pub fn for_handle(handle: ScrollHandle) -> Self {
3406 Self {
3407 handle,
3408 last_origin: Default::default(),
3409 }
3410 }
3411 pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) {
3413 let this = self.clone();
3414
3415 window.on_next_frame(move |_, _| {
3416 let viewport_bounds = this.handle.bounds();
3417 let self_bounds = *this.last_origin.borrow();
3418 this.handle.set_offset(viewport_bounds.origin - self_bounds);
3419 });
3420 }
3421}
3422
3423#[derive(Default, Debug)]
3424struct ScrollHandleState {
3425 offset: Rc<RefCell<Point<Pixels>>>,
3426 bounds: Bounds<Pixels>,
3427 max_offset: Point<Pixels>,
3428 child_bounds: Vec<Bounds<Pixels>>,
3429 scroll_to_bottom: bool,
3430 overflow: Point<Overflow>,
3431 active_item: Option<ScrollActiveItem>,
3432}
3433
3434#[derive(Default, Debug, Clone, Copy)]
3435struct ScrollActiveItem {
3436 index: usize,
3437 strategy: ScrollStrategy,
3438}
3439
3440#[derive(Default, Debug, Clone, Copy)]
3441enum ScrollStrategy {
3442 #[default]
3443 FirstVisible,
3444 Top,
3445}
3446
3447#[derive(Clone, Debug)]
3451pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
3452
3453impl Default for ScrollHandle {
3454 fn default() -> Self {
3455 Self::new()
3456 }
3457}
3458
3459impl ScrollHandle {
3460 pub fn new() -> Self {
3462 Self(Rc::default())
3463 }
3464
3465 pub fn offset(&self) -> Point<Pixels> {
3467 *self.0.borrow().offset.borrow()
3468 }
3469
3470 pub fn max_offset(&self) -> Point<Pixels> {
3472 self.0.borrow().max_offset
3473 }
3474
3475 pub fn top_item(&self) -> usize {
3477 let state = self.0.borrow();
3478 let top = state.bounds.top() - state.offset.borrow().y;
3479
3480 match state.child_bounds.binary_search_by(|bounds| {
3481 if top < bounds.top() {
3482 Ordering::Greater
3483 } else if top > bounds.bottom() {
3484 Ordering::Less
3485 } else {
3486 Ordering::Equal
3487 }
3488 }) {
3489 Ok(ix) => ix,
3490 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
3491 }
3492 }
3493
3494 pub fn bottom_item(&self) -> usize {
3496 let state = self.0.borrow();
3497 let bottom = state.bounds.bottom() - state.offset.borrow().y;
3498
3499 match state.child_bounds.binary_search_by(|bounds| {
3500 if bottom < bounds.top() {
3501 Ordering::Greater
3502 } else if bottom > bounds.bottom() {
3503 Ordering::Less
3504 } else {
3505 Ordering::Equal
3506 }
3507 }) {
3508 Ok(ix) => ix,
3509 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
3510 }
3511 }
3512
3513 pub fn bounds(&self) -> Bounds<Pixels> {
3515 self.0.borrow().bounds
3516 }
3517
3518 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
3520 self.0.borrow().child_bounds.get(ix).cloned()
3521 }
3522
3523 pub fn scroll_to_item(&self, ix: usize) {
3525 let mut state = self.0.borrow_mut();
3526 state.active_item = Some(ScrollActiveItem {
3527 index: ix,
3528 strategy: ScrollStrategy::default(),
3529 });
3530 }
3531
3532 pub fn scroll_to_top_of_item(&self, ix: usize) {
3535 let mut state = self.0.borrow_mut();
3536 state.active_item = Some(ScrollActiveItem {
3537 index: ix,
3538 strategy: ScrollStrategy::Top,
3539 });
3540 }
3541
3542 fn scroll_to_active_item(&self) {
3546 let mut state = self.0.borrow_mut();
3547
3548 let Some(active_item) = state.active_item else {
3549 return;
3550 };
3551
3552 let active_item = match state.child_bounds.get(active_item.index) {
3553 Some(bounds) => {
3554 let mut scroll_offset = state.offset.borrow_mut();
3555
3556 match active_item.strategy {
3557 ScrollStrategy::FirstVisible => {
3558 if state.overflow.y == Overflow::Scroll {
3559 let child_height = bounds.size.height;
3560 let viewport_height = state.bounds.size.height;
3561 if child_height > viewport_height {
3562 scroll_offset.y = state.bounds.top() - bounds.top();
3563 } else if bounds.top() + scroll_offset.y < state.bounds.top() {
3564 scroll_offset.y = state.bounds.top() - bounds.top();
3565 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
3566 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
3567 }
3568 }
3569 }
3570 ScrollStrategy::Top => {
3571 scroll_offset.y = state.bounds.top() - bounds.top();
3572 }
3573 }
3574
3575 if state.overflow.x == Overflow::Scroll {
3576 let child_width = bounds.size.width;
3577 let viewport_width = state.bounds.size.width;
3578 if child_width > viewport_width {
3579 scroll_offset.x = state.bounds.left() - bounds.left();
3580 } else if bounds.left() + scroll_offset.x < state.bounds.left() {
3581 scroll_offset.x = state.bounds.left() - bounds.left();
3582 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
3583 scroll_offset.x = state.bounds.right() - bounds.right();
3584 }
3585 }
3586 None
3587 }
3588 None => Some(active_item),
3589 };
3590 state.active_item = active_item;
3591 }
3592
3593 pub fn scroll_to_bottom(&self) {
3595 let mut state = self.0.borrow_mut();
3596 state.scroll_to_bottom = true;
3597 }
3598
3599 pub fn set_offset(&self, mut position: Point<Pixels>) {
3603 let state = self.0.borrow();
3604 *state.offset.borrow_mut() = position;
3605 }
3606
3607 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
3609 let ix = self.top_item();
3610 let state = self.0.borrow();
3611
3612 if let Some(child_bounds) = state.child_bounds.get(ix) {
3613 (
3614 ix,
3615 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
3616 )
3617 } else {
3618 (ix, px(0.))
3619 }
3620 }
3621
3622 pub fn logical_scroll_bottom(&self) -> (usize, Pixels) {
3624 let ix = self.bottom_item();
3625 let state = self.0.borrow();
3626
3627 if let Some(child_bounds) = state.child_bounds.get(ix) {
3628 (
3629 ix,
3630 child_bounds.bottom() + state.offset.borrow().y - state.bounds.bottom(),
3631 )
3632 } else {
3633 (ix, px(0.))
3634 }
3635 }
3636
3637 pub fn children_count(&self) -> usize {
3639 self.0.borrow().child_bounds.len()
3640 }
3641}
3642
3643#[cfg(test)]
3644mod tests {
3645 use super::*;
3646 use crate::{AppContext as _, Context, InputEvent, MouseMoveEvent, TestAppContext};
3647 use std::rc::Weak;
3648
3649 struct TestTooltipView;
3650
3651 impl Render for TestTooltipView {
3652 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
3653 div().w(px(20.)).h(px(20.)).child("tooltip")
3654 }
3655 }
3656
3657 type CapturedActiveTooltip = Rc<RefCell<Option<Weak<RefCell<Option<ActiveTooltip>>>>>>;
3658
3659 struct TooltipCaptureElement {
3660 child: AnyElement,
3661 captured_active_tooltip: CapturedActiveTooltip,
3662 }
3663
3664 impl IntoElement for TooltipCaptureElement {
3665 type Element = Self;
3666
3667 fn into_element(self) -> Self::Element {
3668 self
3669 }
3670 }
3671
3672 impl Element for TooltipCaptureElement {
3673 type RequestLayoutState = ();
3674 type PrepaintState = ();
3675
3676 fn id(&self) -> Option<ElementId> {
3677 None
3678 }
3679
3680 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
3681 None
3682 }
3683
3684 fn request_layout(
3685 &mut self,
3686 _id: Option<&GlobalElementId>,
3687 _inspector_id: Option<&InspectorElementId>,
3688 window: &mut Window,
3689 cx: &mut App,
3690 ) -> (LayoutId, Self::RequestLayoutState) {
3691 (self.child.request_layout(window, cx), ())
3692 }
3693
3694 fn prepaint(
3695 &mut self,
3696 _id: Option<&GlobalElementId>,
3697 _inspector_id: Option<&InspectorElementId>,
3698 _bounds: Bounds<Pixels>,
3699 _request_layout: &mut Self::RequestLayoutState,
3700 window: &mut Window,
3701 cx: &mut App,
3702 ) -> Self::PrepaintState {
3703 self.child.prepaint(window, cx);
3704 }
3705
3706 fn paint(
3707 &mut self,
3708 _id: Option<&GlobalElementId>,
3709 _inspector_id: Option<&InspectorElementId>,
3710 _bounds: Bounds<Pixels>,
3711 _request_layout: &mut Self::RequestLayoutState,
3712 _prepaint: &mut Self::PrepaintState,
3713 window: &mut Window,
3714 cx: &mut App,
3715 ) {
3716 self.child.paint(window, cx);
3717 window.with_global_id("target".into(), |global_id, window| {
3718 window.with_element_state::<InteractiveElementState, _>(
3719 global_id,
3720 |state, _window| {
3721 let state = state.unwrap();
3722 *self.captured_active_tooltip.borrow_mut() =
3723 state.active_tooltip.as_ref().map(Rc::downgrade);
3724 ((), state)
3725 },
3726 )
3727 });
3728 }
3729 }
3730
3731 struct TooltipOwner {
3732 captured_active_tooltip: CapturedActiveTooltip,
3733 }
3734
3735 impl Render for TooltipOwner {
3736 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
3737 TooltipCaptureElement {
3738 child: div()
3739 .size_full()
3740 .child(
3741 div()
3742 .id("target")
3743 .w(px(50.))
3744 .h(px(50.))
3745 .tooltip(|_, cx| cx.new(|_| TestTooltipView).into()),
3746 )
3747 .into_any_element(),
3748 captured_active_tooltip: self.captured_active_tooltip.clone(),
3749 }
3750 }
3751 }
3752
3753 #[test]
3754 fn scroll_handle_aligns_wide_children_to_left_edge() {
3755 let handle = ScrollHandle::new();
3756 {
3757 let mut state = handle.0.borrow_mut();
3758 state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(80.), px(20.)));
3759 state.child_bounds = vec![Bounds::new(point(px(25.), px(0.)), size(px(200.), px(20.)))];
3760 state.overflow.x = Overflow::Scroll;
3761 state.active_item = Some(ScrollActiveItem {
3762 index: 0,
3763 strategy: ScrollStrategy::default(),
3764 });
3765 }
3766
3767 handle.scroll_to_active_item();
3768
3769 assert_eq!(handle.offset().x, px(-25.));
3770 }
3771
3772 #[test]
3773 fn scroll_handle_aligns_tall_children_to_top_edge() {
3774 let handle = ScrollHandle::new();
3775 {
3776 let mut state = handle.0.borrow_mut();
3777 state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(20.), px(80.)));
3778 state.child_bounds = vec![Bounds::new(point(px(0.), px(25.)), size(px(20.), px(200.)))];
3779 state.overflow.y = Overflow::Scroll;
3780 state.active_item = Some(ScrollActiveItem {
3781 index: 0,
3782 strategy: ScrollStrategy::default(),
3783 });
3784 }
3785
3786 handle.scroll_to_active_item();
3787
3788 assert_eq!(handle.offset().y, px(-25.));
3789 }
3790
3791 fn setup_tooltip_owner_test() -> (
3792 TestAppContext,
3793 crate::AnyWindowHandle,
3794 CapturedActiveTooltip,
3795 ) {
3796 let mut test_app = TestAppContext::single();
3797 let captured_active_tooltip: CapturedActiveTooltip = Rc::new(RefCell::new(None));
3798 let window = test_app.add_window({
3799 let captured_active_tooltip = captured_active_tooltip.clone();
3800 move |_, _| TooltipOwner {
3801 captured_active_tooltip,
3802 }
3803 });
3804 let any_window = window.into();
3805
3806 test_app
3807 .update_window(any_window, |_, window, cx| {
3808 window.draw(cx).clear();
3809 })
3810 .unwrap();
3811
3812 test_app
3813 .update_window(any_window, |_, window, cx| {
3814 window.dispatch_event(
3815 MouseMoveEvent {
3816 position: point(px(10.), px(10.)),
3817 modifiers: Default::default(),
3818 pressed_button: None,
3819 }
3820 .to_platform_input(),
3821 cx,
3822 );
3823 })
3824 .unwrap();
3825
3826 test_app
3827 .update_window(any_window, |_, window, cx| {
3828 window.draw(cx).clear();
3829 })
3830 .unwrap();
3831
3832 (test_app, any_window, captured_active_tooltip)
3833 }
3834
3835 #[test]
3836 fn tooltip_waiting_for_show_is_released_when_its_owner_disappears() {
3837 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test();
3838
3839 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
3840 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
3841 assert!(matches!(
3842 active_tooltip.borrow().as_ref(),
3843 Some(ActiveTooltip::WaitingForShow { .. })
3844 ));
3845
3846 test_app
3847 .update_window(any_window, |_, window, _| {
3848 window.remove_window();
3849 })
3850 .unwrap();
3851 test_app.run_until_parked();
3852 drop(active_tooltip);
3853
3854 assert!(weak_active_tooltip.upgrade().is_none());
3855 }
3856
3857 #[test]
3858 fn tooltip_is_released_when_its_owner_disappears() {
3859 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test();
3860
3861 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
3862 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
3863
3864 test_app.dispatcher.advance_clock(TOOLTIP_SHOW_DELAY);
3865 test_app.run_until_parked();
3866
3867 assert!(matches!(
3868 active_tooltip.borrow().as_ref(),
3869 Some(ActiveTooltip::Visible { .. })
3870 ));
3871
3872 test_app
3873 .update_window(any_window, |_, window, _| {
3874 window.remove_window();
3875 })
3876 .unwrap();
3877 test_app.run_until_parked();
3878 drop(active_tooltip);
3879
3880 assert!(weak_active_tooltip.upgrade().is_none());
3881 }
3882}