1use crate::{
19 Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, DispatchPhase,
20 Display, Element, ElementId, Entity, EntityId, ExternalDragPayload, ExternalDragPayloadSource,
21 FocusHandle, Global, GlobalElementId, Hitbox, HitboxBehavior, HitboxId, InspectorElementId,
22 IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, KeyboardButton, KeyboardClickEvent,
23 LayoutId, ModifiersChangedEvent, MouseButton, MouseClickEvent, MouseDownEvent, MouseExitEvent,
24 MouseMoveEvent, MousePressureEvent, MouseUpEvent, OngoingScroll, Overflow, ParentElement,
25 PinchEvent, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
26 StyleRefinement, Styled, Task, TooltipId, Visibility, Window, WindowControlArea, point, px,
27 size,
28};
29use collections::HashMap;
30use gpui_util::ResultExt;
31use refineable::Refineable;
32use smallvec::SmallVec;
33use std::{
34 any::{Any, TypeId},
35 cell::RefCell,
36 cmp::Ordering,
37 fmt::Debug,
38 marker::PhantomData,
39 mem,
40 rc::Rc,
41 sync::Arc,
42 time::Duration,
43};
44
45use super::ImageCacheProvider;
46
47#[cfg(feature = "stacker")]
48type StackSafe<T> = stacksafe::StackSafe<T>;
49#[cfg(not(feature = "stacker"))]
50type StackSafe<T> = T;
51
52const DRAG_THRESHOLD: f64 = 2.;
53const DEFAULT_TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);
54const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500);
55
56pub struct GroupStyle {
58 pub group: SharedString,
60
61 pub style: Box<StyleRefinement>,
64}
65
66pub struct DragMoveEvent<T> {
68 pub event: MouseMoveEvent,
70
71 pub bounds: Bounds<Pixels>,
73 drag: PhantomData<T>,
74 dragged_item: Arc<dyn Any>,
75}
76
77impl<T: 'static> DragMoveEvent<T> {
78 pub fn drag<'b>(&self, cx: &'b App) -> &'b T {
80 cx.active_drag
81 .as_ref()
82 .and_then(|drag| drag.value.downcast_ref::<T>())
83 .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
84 }
85
86 pub fn dragged_item(&self) -> &dyn Any {
88 self.dragged_item.as_ref()
89 }
90}
91
92impl Interactivity {
93 #[cfg(any(feature = "inspector", debug_assertions))]
95 #[track_caller]
96 pub fn new() -> Interactivity {
97 Interactivity {
98 source_location: Some(core::panic::Location::caller()),
99 ..Default::default()
100 }
101 }
102
103 #[cfg(not(any(feature = "inspector", debug_assertions)))]
105 pub fn new() -> Interactivity {
106 Interactivity::default()
107 }
108
109 pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
111 #[cfg(any(feature = "inspector", debug_assertions))]
112 {
113 self.source_location
114 }
115
116 #[cfg(not(any(feature = "inspector", debug_assertions)))]
117 {
118 None
119 }
120 }
121
122 pub fn on_mouse_down(
127 &mut self,
128 button: MouseButton,
129 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
130 ) {
131 self.mouse_down_listeners
132 .push(Box::new(move |event, phase, hitbox, window, cx| {
133 if phase == DispatchPhase::Bubble
134 && event.button == button
135 && hitbox.is_hovered(window)
136 {
137 (listener)(event, window, cx)
138 }
139 }));
140 }
141
142 pub fn capture_any_mouse_down(
147 &mut self,
148 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
149 ) {
150 self.mouse_down_listeners
151 .push(Box::new(move |event, phase, hitbox, window, cx| {
152 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
153 (listener)(event, window, cx)
154 }
155 }));
156 }
157
158 pub fn on_any_mouse_down(
163 &mut self,
164 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
165 ) {
166 self.mouse_down_listeners
167 .push(Box::new(move |event, phase, hitbox, window, cx| {
168 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
169 (listener)(event, window, cx)
170 }
171 }));
172 }
173
174 pub fn on_mouse_pressure(
179 &mut self,
180 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
181 ) {
182 self.mouse_pressure_listeners
183 .push(Box::new(move |event, phase, hitbox, window, cx| {
184 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
185 (listener)(event, window, cx)
186 }
187 }));
188 }
189
190 pub fn capture_mouse_pressure(
195 &mut self,
196 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
197 ) {
198 self.mouse_pressure_listeners
199 .push(Box::new(move |event, phase, hitbox, window, cx| {
200 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
201 (listener)(event, window, cx)
202 }
203 }));
204 }
205
206 pub fn on_mouse_up(
211 &mut self,
212 button: MouseButton,
213 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
214 ) {
215 self.mouse_up_listeners
216 .push(Box::new(move |event, phase, hitbox, window, cx| {
217 if phase == DispatchPhase::Bubble
218 && event.button == button
219 && hitbox.is_hovered(window)
220 {
221 (listener)(event, window, cx)
222 }
223 }));
224 }
225
226 pub fn capture_any_mouse_up(
231 &mut self,
232 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
233 ) {
234 self.mouse_up_listeners
235 .push(Box::new(move |event, phase, hitbox, window, cx| {
236 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
237 (listener)(event, window, cx)
238 }
239 }));
240 }
241
242 pub fn on_any_mouse_up(
247 &mut self,
248 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
249 ) {
250 self.mouse_up_listeners
251 .push(Box::new(move |event, phase, hitbox, window, cx| {
252 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
253 (listener)(event, window, cx)
254 }
255 }));
256 }
257
258 pub fn on_mouse_down_out(
264 &mut self,
265 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
266 ) {
267 self.mouse_down_listeners
268 .push(Box::new(move |event, phase, hitbox, window, cx| {
269 if phase == DispatchPhase::Capture
270 && !window.has_active_prompt()
271 && !hitbox.contains(&window.mouse_position())
272 {
273 (listener)(event, window, cx)
274 }
275 }));
276 }
277
278 pub fn on_mouse_up_out(
284 &mut self,
285 button: MouseButton,
286 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
287 ) {
288 self.mouse_up_listeners
289 .push(Box::new(move |event, phase, hitbox, window, cx| {
290 if phase == DispatchPhase::Capture
291 && event.button == button
292 && !hitbox.is_hovered(window)
293 {
294 (listener)(event, window, cx);
295 }
296 }));
297 }
298
299 pub fn on_mouse_move(
304 &mut self,
305 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
306 ) {
307 self.mouse_move_listeners
308 .push(Box::new(move |event, phase, hitbox, window, cx| {
309 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
310 (listener)(event, window, cx);
311 }
312 }));
313 }
314
315 pub fn on_mouse_exit(
320 &mut self,
321 listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static,
322 ) {
323 self.mouse_exit_listeners
324 .push(Box::new(move |event, phase, hitbox, window, cx| {
325 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
326 (listener)(event, window, cx);
327 }
328 }));
329 }
330
331 pub fn on_drag_move<T>(
339 &mut self,
340 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
341 ) where
342 T: 'static,
343 {
344 self.mouse_move_listeners
345 .push(Box::new(move |event, phase, hitbox, window, cx| {
346 if phase == DispatchPhase::Capture
347 && let Some(drag) = &cx.active_drag
348 && drag.value.as_ref().type_id() == TypeId::of::<T>()
349 {
350 (listener)(
351 &DragMoveEvent {
352 event: event.clone(),
353 bounds: hitbox.bounds,
354 drag: PhantomData,
355 dragged_item: Arc::clone(&drag.value),
356 },
357 window,
358 cx,
359 );
360 }
361 }));
362 }
363
364 pub fn on_scroll_wheel(
369 &mut self,
370 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
371 ) {
372 self.scroll_wheel_listeners
373 .push(Box::new(move |event, phase, hitbox, window, cx| {
374 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
375 (listener)(event, window, cx);
376 }
377 }));
378 }
379
380 pub fn on_pinch(&mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) {
384 self.pinch_listeners
385 .push(Box::new(move |event, phase, hitbox, window, cx| {
386 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
387 (listener)(event, window, cx);
388 }
389 }));
390 }
391
392 pub fn capture_pinch(
396 &mut self,
397 listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
398 ) {
399 self.pinch_listeners
400 .push(Box::new(move |event, phase, _hitbox, window, cx| {
401 if phase == DispatchPhase::Capture {
402 (listener)(event, window, cx);
403 } else {
404 cx.propagate();
405 }
406 }));
407 }
408
409 pub fn capture_action<A: Action>(
414 &mut self,
415 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
416 ) {
417 self.action_listeners.push((
418 TypeId::of::<A>(),
419 Box::new(move |action, phase, window, cx| {
420 let action = action.downcast_ref().unwrap();
421 if phase == DispatchPhase::Capture {
422 (listener)(action, window, cx)
423 } else {
424 cx.propagate();
425 }
426 }),
427 ));
428 }
429
430 #[track_caller]
435 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {
436 self.action_listeners.push((
437 TypeId::of::<A>(),
438 Box::new(move |action, phase, window, cx| {
439 let action = action.downcast_ref().unwrap();
440 if phase == DispatchPhase::Bubble {
441 (listener)(action, window, cx)
442 }
443 }),
444 ));
445 }
446
447 pub fn on_boxed_action(
454 &mut self,
455 action: &dyn Action,
456 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
457 ) {
458 let action = action.boxed_clone();
459 self.action_listeners.push((
460 (*action).type_id(),
461 Box::new(move |_, phase, window, cx| {
462 if phase == DispatchPhase::Bubble {
463 (listener)(&*action, window, cx)
464 }
465 }),
466 ));
467 }
468
469 pub fn on_key_down(
474 &mut self,
475 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
476 ) {
477 self.key_down_listeners
478 .push(Box::new(move |event, phase, window, cx| {
479 if phase == DispatchPhase::Bubble {
480 (listener)(event, window, cx)
481 }
482 }));
483 }
484
485 pub fn capture_key_down(
490 &mut self,
491 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
492 ) {
493 self.key_down_listeners
494 .push(Box::new(move |event, phase, window, cx| {
495 if phase == DispatchPhase::Capture {
496 listener(event, window, cx)
497 }
498 }));
499 }
500
501 pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {
506 self.key_up_listeners
507 .push(Box::new(move |event, phase, window, cx| {
508 if phase == DispatchPhase::Bubble {
509 listener(event, window, cx)
510 }
511 }));
512 }
513
514 pub fn capture_key_up(
519 &mut self,
520 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
521 ) {
522 self.key_up_listeners
523 .push(Box::new(move |event, phase, window, cx| {
524 if phase == DispatchPhase::Capture {
525 listener(event, window, cx)
526 }
527 }));
528 }
529
530 pub fn on_modifiers_changed(
535 &mut self,
536 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
537 ) {
538 self.modifiers_changed_listeners
539 .push(Box::new(move |event, window, cx| {
540 listener(event, window, cx)
541 }));
542 }
543
544 pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {
549 self.drop_listeners.push((
550 TypeId::of::<T>(),
551 Box::new(move |dragged_value, window, cx| {
552 listener(dragged_value.downcast_ref().unwrap(), window, cx);
553 }),
554 ));
555 }
556
557 pub fn can_drop(
560 &mut self,
561 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
562 ) {
563 self.can_drop_predicate = Some(Box::new(predicate));
564 }
565
566 pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
571 where
572 Self: Sized,
573 {
574 self.click_listeners.push(Rc::new(move |event, window, cx| {
575 listener(event, window, cx)
576 }));
577 }
578
579 pub fn on_aux_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
584 where
585 Self: Sized,
586 {
587 self.aux_click_listeners
588 .push(Rc::new(move |event, window, cx| {
589 listener(event, window, cx)
590 }));
591 }
592
593 pub fn on_drag<T, W>(
600 &mut self,
601 value: T,
602 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
603 ) where
604 Self: Sized,
605 T: 'static,
606 W: 'static + Render,
607 {
608 debug_assert!(
609 self.drag_listener.is_none(),
610 "calling on_drag more than once on the same element is not supported"
611 );
612 self.drag_listener = Some(DragListener {
613 value: Arc::new(value),
614 render: Box::new(move |value, offset, window, cx| {
615 constructor(value.downcast_ref().unwrap(), offset, window, cx).into()
616 }),
617 external_payload: None,
618 });
619 }
620
621 pub fn external_drag_payload<T>(
626 &mut self,
627 resolver: impl Fn(&T, &mut Window, &mut App) -> Option<ExternalDragPayload> + 'static,
628 ) where
629 Self: Sized,
630 T: 'static,
631 {
632 let Some(drag_listener) = self.drag_listener.as_mut() else {
633 debug_assert!(false, "external_drag_payload must be called after on_drag");
634 return;
635 };
636 debug_assert!(
637 drag_listener.value.as_ref().type_id() == TypeId::of::<T>(),
638 "external_drag_payload must use the same dragged value type as on_drag"
639 );
640 debug_assert!(
641 drag_listener.external_payload.is_none(),
642 "calling external_drag_payload more than once on the same element is not supported"
643 );
644 drag_listener.external_payload = Some(Box::new(move |value, window, cx| {
645 resolver(value.downcast_ref::<T>()?, window, cx)
646 }));
647 }
648
649 pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static)
660 where
661 Self: Sized,
662 {
663 debug_assert!(
664 self.hover_listener.is_none(),
665 "calling on_hover more than once on the same element is not supported"
666 );
667 self.hover_listener = Some(Box::new(listener));
668 }
669
670 pub fn hover_listener_mode(&mut self, mode: HoverListenerMode)
674 where
675 Self: Sized,
676 {
677 self.hover_listener_mode = mode;
678 }
679
680 pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static)
683 where
684 Self: Sized,
685 {
686 debug_assert!(
687 self.tooltip_builder.is_none(),
688 "calling tooltip more than once on the same element is not supported"
689 );
690 self.tooltip_builder = Some(TooltipBuilder {
691 build: Rc::new(build_tooltip),
692 hoverable: false,
693 });
694 }
695
696 pub fn hoverable_tooltip(
700 &mut self,
701 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
702 ) where
703 Self: Sized,
704 {
705 debug_assert!(
706 self.tooltip_builder.is_none(),
707 "calling tooltip more than once on the same element is not supported"
708 );
709 self.tooltip_builder = Some(TooltipBuilder {
710 build: Rc::new(build_tooltip),
711 hoverable: true,
712 });
713 }
714
715 pub fn tooltip_show_delay(&mut self, delay: Duration) {
718 self.tooltip_show_delay = Some(delay);
719 }
720
721 pub fn occlude_mouse(&mut self) {
726 self.hitbox_behavior = HitboxBehavior::BlockMouse;
727 }
728
729 pub fn window_control_area(&mut self, area: WindowControlArea) {
732 self.window_control = Some(area);
733 }
734
735 pub fn block_mouse_except_scroll(&mut self) {
740 self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll;
741 }
742
743 fn has_pinch_listeners(&self) -> bool {
744 !self.pinch_listeners.is_empty()
745 }
746}
747
748pub trait InteractiveElement: Sized {
751 fn interactivity(&mut self) -> &mut Interactivity;
753
754 fn group(mut self, group: impl Into<SharedString>) -> Self {
756 self.interactivity().group = Some(group.into());
757 self
758 }
759
760 fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
762 self.interactivity().element_id = Some(id.into());
763
764 Stateful { element: self }
765 }
766
767 fn track_focus(mut self, focus_handle: &FocusHandle) -> Self {
771 self.interactivity().focusable = true;
772 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
773 self
774 }
775
776 fn tab_stop(mut self, tab_stop: bool) -> Self {
783 self.interactivity().tab_stop = tab_stop;
784 self
785 }
786
787 fn tab_index(mut self, index: isize) -> Self {
792 self.interactivity().focusable = true;
793 self.interactivity().tab_index = Some(index);
794 self.interactivity().tab_stop = true;
795 self
796 }
797
798 fn tab_group(mut self) -> Self {
803 self.interactivity().tab_group = true;
804 if self.interactivity().tab_index.is_none() {
805 self.interactivity().tab_index = Some(0);
806 }
807 self
808 }
809
810 fn key_context<C, E>(mut self, key_context: C) -> Self
813 where
814 C: TryInto<KeyContext, Error = E>,
815 E: std::fmt::Display,
816 {
817 if let Some(key_context) = key_context.try_into().log_err() {
818 self.interactivity().key_context = Some(key_context);
819 }
820 self
821 }
822
823 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
825 debug_assert!(
826 self.interactivity().hover_style.is_none(),
827 "hover style already set"
828 );
829 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
830 self
831 }
832
833 fn group_hover(
835 mut self,
836 group_name: impl Into<SharedString>,
837 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
838 ) -> Self {
839 self.interactivity().group_hover_style = Some(GroupStyle {
840 group: group_name.into(),
841 style: Box::new(f(StyleRefinement::default())),
842 });
843 self
844 }
845
846 fn on_mouse_down(
851 mut self,
852 button: MouseButton,
853 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
854 ) -> Self {
855 self.interactivity().on_mouse_down(button, listener);
856 self
857 }
858
859 #[cfg(any(test, feature = "test-support"))]
860 fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
864 self.interactivity().debug_selector = Some(f());
865 self
866 }
867
868 #[cfg(not(any(test, feature = "test-support")))]
869 #[inline]
873 fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
874 self
875 }
876
877 fn capture_any_mouse_down(
882 mut self,
883 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
884 ) -> Self {
885 self.interactivity().capture_any_mouse_down(listener);
886 self
887 }
888
889 fn on_any_mouse_down(
894 mut self,
895 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
896 ) -> Self {
897 self.interactivity().on_any_mouse_down(listener);
898 self
899 }
900
901 fn on_mouse_up(
906 mut self,
907 button: MouseButton,
908 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
909 ) -> Self {
910 self.interactivity().on_mouse_up(button, listener);
911 self
912 }
913
914 fn capture_any_mouse_up(
919 mut self,
920 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
921 ) -> Self {
922 self.interactivity().capture_any_mouse_up(listener);
923 self
924 }
925
926 fn on_mouse_pressure(
931 mut self,
932 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
933 ) -> Self {
934 self.interactivity().on_mouse_pressure(listener);
935 self
936 }
937
938 fn capture_mouse_pressure(
943 mut self,
944 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
945 ) -> Self {
946 self.interactivity().capture_mouse_pressure(listener);
947 self
948 }
949
950 fn on_mouse_down_out(
956 mut self,
957 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
958 ) -> Self {
959 self.interactivity().on_mouse_down_out(listener);
960 self
961 }
962
963 fn on_mouse_up_out(
969 mut self,
970 button: MouseButton,
971 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
972 ) -> Self {
973 self.interactivity().on_mouse_up_out(button, listener);
974 self
975 }
976
977 fn on_mouse_move(
982 mut self,
983 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
984 ) -> Self {
985 self.interactivity().on_mouse_move(listener);
986 self
987 }
988
989 fn on_mouse_exit(
994 mut self,
995 listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static,
996 ) -> Self {
997 self.interactivity().on_mouse_exit(listener);
998 self
999 }
1000
1001 fn on_drag_move<T: 'static>(
1009 mut self,
1010 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
1011 ) -> Self {
1012 self.interactivity().on_drag_move(listener);
1013 self
1014 }
1015
1016 fn on_scroll_wheel(
1021 mut self,
1022 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
1023 ) -> Self {
1024 self.interactivity().on_scroll_wheel(listener);
1025 self
1026 }
1027
1028 fn on_pinch(mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) -> Self {
1033 self.interactivity().on_pinch(listener);
1034 self
1035 }
1036
1037 fn capture_pinch(
1042 mut self,
1043 listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
1044 ) -> Self {
1045 self.interactivity().capture_pinch(listener);
1046 self
1047 }
1048 fn capture_action<A: Action>(
1053 mut self,
1054 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
1055 ) -> Self {
1056 self.interactivity().capture_action(listener);
1057 self
1058 }
1059
1060 #[track_caller]
1065 fn on_action<A: Action>(
1066 mut self,
1067 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
1068 ) -> Self {
1069 self.interactivity().on_action(listener);
1070 self
1071 }
1072
1073 fn on_boxed_action(
1080 mut self,
1081 action: &dyn Action,
1082 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
1083 ) -> Self {
1084 self.interactivity().on_boxed_action(action, listener);
1085 self
1086 }
1087
1088 fn on_key_down(
1093 mut self,
1094 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
1095 ) -> Self {
1096 self.interactivity().on_key_down(listener);
1097 self
1098 }
1099
1100 fn capture_key_down(
1105 mut self,
1106 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
1107 ) -> Self {
1108 self.interactivity().capture_key_down(listener);
1109 self
1110 }
1111
1112 fn on_key_up(
1117 mut self,
1118 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1119 ) -> Self {
1120 self.interactivity().on_key_up(listener);
1121 self
1122 }
1123
1124 fn capture_key_up(
1129 mut self,
1130 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1131 ) -> Self {
1132 self.interactivity().capture_key_up(listener);
1133 self
1134 }
1135
1136 fn on_modifiers_changed(
1141 mut self,
1142 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
1143 ) -> Self {
1144 self.interactivity().on_modifiers_changed(listener);
1145 self
1146 }
1147
1148 fn drag_over<S: 'static>(
1150 mut self,
1151 f: impl 'static + Fn(StyleRefinement, &S, &mut Window, &mut App) -> StyleRefinement,
1152 ) -> Self {
1153 self.interactivity().drag_over_styles.push((
1154 TypeId::of::<S>(),
1155 Box::new(move |currently_dragged: &dyn Any, window, cx| {
1156 f(
1157 StyleRefinement::default(),
1158 currently_dragged.downcast_ref::<S>().unwrap(),
1159 window,
1160 cx,
1161 )
1162 }),
1163 ));
1164 self
1165 }
1166
1167 fn group_drag_over<S: 'static>(
1169 mut self,
1170 group_name: impl Into<SharedString>,
1171 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1172 ) -> Self {
1173 self.interactivity().group_drag_over_styles.push((
1174 TypeId::of::<S>(),
1175 GroupStyle {
1176 group: group_name.into(),
1177 style: Box::new(f(StyleRefinement::default())),
1178 },
1179 ));
1180 self
1181 }
1182
1183 fn on_drop<T: 'static>(
1188 mut self,
1189 listener: impl Fn(&T, &mut Window, &mut App) + 'static,
1190 ) -> Self {
1191 self.interactivity().on_drop(listener);
1192 self
1193 }
1194
1195 fn can_drop(
1198 mut self,
1199 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
1200 ) -> Self {
1201 self.interactivity().can_drop(predicate);
1202 self
1203 }
1204
1205 fn occlude(mut self) -> Self {
1209 self.interactivity().occlude_mouse();
1210 self
1211 }
1212
1213 fn window_control_area(mut self, area: WindowControlArea) -> Self {
1216 self.interactivity().window_control_area(area);
1217 self
1218 }
1219
1220 fn block_mouse_except_scroll(mut self) -> Self {
1225 self.interactivity().block_mouse_except_scroll();
1226 self
1227 }
1228
1229 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1232 where
1233 Self: Sized,
1234 {
1235 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
1236 self
1237 }
1238
1239 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1242 where
1243 Self: Sized,
1244 {
1245 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
1246 self
1247 }
1248
1249 fn focus_visible(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1254 where
1255 Self: Sized,
1256 {
1257 self.interactivity().focus_visible_style = Some(Box::new(f(StyleRefinement::default())));
1258 self
1259 }
1260}
1261
1262pub trait StatefulInteractiveElement: InteractiveElement {
1265 fn role(mut self, role: accesskit::Role) -> Self {
1269 debug_assert!(
1270 role != accesskit::Role::GenericContainer,
1271 "GenericContainer is filtered out of the a11y tree and has no effect"
1272 );
1273 self.interactivity().override_role = Some(role);
1274 self
1275 }
1276
1277 fn accessibility_id(mut self, id: impl Into<SharedString>) -> Self {
1285 self.interactivity().aria.author_id = Some(id.into());
1286 self
1287 }
1288
1289 fn aria_label(mut self, label: impl Into<SharedString>) -> Self {
1291 self.interactivity().aria.label = Some(label.into());
1292 self
1293 }
1294
1295 fn aria_description(mut self, description: impl Into<SharedString>) -> Self {
1300 self.interactivity().aria.description = Some(description.into());
1301 self
1302 }
1303
1304 fn aria_keyshortcuts(mut self, keyshortcuts: impl Into<SharedString>) -> Self {
1310 self.interactivity().aria.keyshortcuts = Some(keyshortcuts.into());
1311 self
1312 }
1313
1314 fn aria_active_descendant(mut self) -> Self {
1330 self.interactivity().report_active_descendant_focus = true;
1331 self
1332 }
1333
1334 fn a11y_synthetic_children(
1344 mut self,
1345 f: impl FnOnce(&mut crate::A11ySubtreeBuilder) + 'static,
1346 ) -> Self {
1347 self.interactivity().a11y_synthetic_children = Some(Box::new(f));
1348 self
1349 }
1350
1351 fn aria_selected(mut self, selected: bool) -> Self {
1353 self.interactivity().aria.selected = Some(selected);
1354 self
1355 }
1356
1357 fn aria_expanded(mut self, expanded: bool) -> Self {
1359 self.interactivity().aria.expanded = Some(expanded);
1360 self
1361 }
1362
1363 fn aria_toggled(mut self, toggled: accesskit::Toggled) -> Self {
1365 self.interactivity().aria.toggled = Some(toggled);
1366 self
1367 }
1368
1369 fn aria_numeric_value(mut self, value: f64) -> Self {
1371 self.interactivity().aria.numeric_value = Some(value);
1372 self
1373 }
1374
1375 fn aria_numeric_value_step(mut self, step: f64) -> Self {
1378 self.interactivity().aria.numeric_value_step = Some(step);
1379 self
1380 }
1381
1382 fn aria_value(mut self, value: impl Into<SharedString>) -> Self {
1385 self.interactivity().aria.value = Some(value.into());
1386 self
1387 }
1388
1389 fn aria_placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
1392 self.interactivity().aria.placeholder = Some(placeholder.into());
1393 self
1394 }
1395
1396 fn aria_min_numeric_value(mut self, value: f64) -> Self {
1398 self.interactivity().aria.min_numeric_value = Some(value);
1399 self
1400 }
1401
1402 fn aria_max_numeric_value(mut self, value: f64) -> Self {
1404 self.interactivity().aria.max_numeric_value = Some(value);
1405 self
1406 }
1407
1408 fn aria_orientation(mut self, orientation: accesskit::Orientation) -> Self {
1410 self.interactivity().aria.orientation = Some(orientation);
1411 self
1412 }
1413
1414 fn aria_level(mut self, level: usize) -> Self {
1416 self.interactivity().aria.level = Some(level);
1417 self
1418 }
1419
1420 fn aria_position_in_set(mut self, position: usize) -> Self {
1422 self.interactivity().aria.position_in_set = Some(position);
1423 self
1424 }
1425
1426 fn aria_size_of_set(mut self, size: usize) -> Self {
1428 self.interactivity().aria.size_of_set = Some(size);
1429 self
1430 }
1431
1432 fn aria_row_index(mut self, index: usize) -> Self {
1434 self.interactivity().aria.row_index = Some(index);
1435 self
1436 }
1437
1438 fn aria_column_index(mut self, index: usize) -> Self {
1440 self.interactivity().aria.column_index = Some(index);
1441 self
1442 }
1443
1444 fn aria_row_count(mut self, count: usize) -> Self {
1446 self.interactivity().aria.row_count = Some(count);
1447 self
1448 }
1449
1450 fn aria_column_count(mut self, count: usize) -> Self {
1452 self.interactivity().aria.column_count = Some(count);
1453 self
1454 }
1455
1456 fn on_a11y_action(
1461 mut self,
1462 action: accesskit::Action,
1463 listener: impl FnMut(Option<&accesskit::ActionData>, &mut crate::Window, &mut crate::App)
1464 + 'static,
1465 ) -> Self {
1466 self.interactivity()
1467 .a11y_action_listeners
1468 .push((action, Box::new(listener)));
1469 self
1470 }
1471
1472 fn focusable(mut self) -> Self {
1474 self.interactivity().focusable = true;
1475 self
1476 }
1477
1478 fn overflow_scroll(mut self) -> Self {
1480 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1481 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1482 self
1483 }
1484
1485 fn overflow_x_scroll(mut self) -> Self {
1487 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1488 self
1489 }
1490
1491 fn overflow_y_scroll(mut self) -> Self {
1493 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1494 self
1495 }
1496
1497 fn restrict_scroll_to_axis(mut self) -> Self {
1501 self.interactivity().base_style.restrict_scroll_to_axis = Some(true);
1502 self
1503 }
1504
1505 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
1507 self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone());
1508 self
1509 }
1510
1511 fn anchor_scroll(mut self, scroll_anchor: Option<ScrollAnchor>) -> Self {
1513 self.interactivity().scroll_anchor = scroll_anchor;
1514 self
1515 }
1516
1517 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1519 where
1520 Self: Sized,
1521 {
1522 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
1523 self
1524 }
1525
1526 fn group_active(
1528 mut self,
1529 group_name: impl Into<SharedString>,
1530 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1531 ) -> Self
1532 where
1533 Self: Sized,
1534 {
1535 self.interactivity().group_active_style = Some(GroupStyle {
1536 group: group_name.into(),
1537 style: Box::new(f(StyleRefinement::default())),
1538 });
1539 self
1540 }
1541
1542 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self
1547 where
1548 Self: Sized,
1549 {
1550 self.interactivity().on_click(listener);
1551 self
1552 }
1553
1554 fn on_aux_click(
1559 mut self,
1560 listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
1561 ) -> Self
1562 where
1563 Self: Sized,
1564 {
1565 self.interactivity().on_aux_click(listener);
1566 self
1567 }
1568
1569 fn on_drag<T, W>(
1577 mut self,
1578 value: T,
1579 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
1580 ) -> Self
1581 where
1582 Self: Sized,
1583 T: 'static,
1584 W: 'static + Render,
1585 {
1586 self.interactivity().on_drag(value, constructor);
1587 self
1588 }
1589
1590 fn external_drag_payload<T>(
1596 mut self,
1597 resolver: impl Fn(&T, &mut Window, &mut App) -> Option<ExternalDragPayload> + 'static,
1598 ) -> Self
1599 where
1600 Self: Sized,
1601 T: 'static,
1602 {
1603 self.interactivity().external_drag_payload(resolver);
1604 self
1605 }
1606
1607 fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self
1618 where
1619 Self: Sized,
1620 {
1621 self.interactivity().on_hover(listener);
1622 self
1623 }
1624
1625 fn hover_listener_mode(mut self, mode: HoverListenerMode) -> Self
1629 where
1630 Self: Sized,
1631 {
1632 self.interactivity().hover_listener_mode(mode);
1633 self
1634 }
1635
1636 fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self
1639 where
1640 Self: Sized,
1641 {
1642 self.interactivity().tooltip(build_tooltip);
1643 self
1644 }
1645
1646 fn hoverable_tooltip(
1650 mut self,
1651 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
1652 ) -> Self
1653 where
1654 Self: Sized,
1655 {
1656 self.interactivity().hoverable_tooltip(build_tooltip);
1657 self
1658 }
1659
1660 fn tooltip_show_delay(mut self, delay: Duration) -> Self
1663 where
1664 Self: Sized,
1665 {
1666 self.interactivity().tooltip_show_delay(delay);
1667 self
1668 }
1669}
1670
1671pub(crate) type MouseDownListener =
1672 Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1673pub(crate) type MouseUpListener =
1674 Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1675pub(crate) type MousePressureListener =
1676 Box<dyn Fn(&MousePressureEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1677pub(crate) type MouseMoveListener =
1678 Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1679pub(crate) type MouseExitListener =
1680 Box<dyn Fn(&MouseExitEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1681
1682pub(crate) type ScrollWheelListener =
1683 Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1684
1685pub(crate) type PinchListener =
1686 Box<dyn Fn(&PinchEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1687
1688pub(crate) type ClickListener = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
1689
1690#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1693pub enum HoverListenerMode {
1694 #[default]
1697 InputModalityAware,
1698 InputModalityIndependent,
1702}
1703
1704impl HoverListenerMode {
1705 fn is_hovered(self, hitbox: &Hitbox, window: &Window) -> bool {
1706 match self {
1707 Self::InputModalityAware => hitbox.is_hovered(window),
1708 Self::InputModalityIndependent => hitbox.id.is_hovered_ignoring_last_input(window),
1709 }
1710 }
1711}
1712
1713pub(crate) struct DragListener {
1714 value: Arc<dyn Any>,
1715 render: Box<dyn Fn(&dyn Any, Point<Pixels>, &mut Window, &mut App) -> AnyView + 'static>,
1716 external_payload: Option<ExternalDragPayloadResolver>,
1717}
1718
1719type ExternalDragPayloadResolver =
1720 Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> Option<ExternalDragPayload> + 'static>;
1721
1722type DropListener = Box<dyn Fn(&dyn Any, &mut Window, &mut App) + 'static>;
1723
1724type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>;
1725
1726pub(crate) struct TooltipBuilder {
1727 build: Rc<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>,
1728 hoverable: bool,
1729}
1730
1731pub(crate) type KeyDownListener =
1732 Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1733
1734pub(crate) type KeyUpListener =
1735 Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1736
1737pub(crate) type ModifiersChangedListener =
1738 Box<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static>;
1739
1740pub(crate) type ActionListener =
1741 Box<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
1742
1743#[track_caller]
1745pub fn div() -> Div {
1746 Div {
1747 interactivity: Interactivity::new(),
1748 children: SmallVec::default(),
1749 prepaint_listener: None,
1750 image_cache: None,
1751 prepaint_order_fn: None,
1752 }
1753}
1754
1755pub struct Div {
1757 interactivity: Interactivity,
1758 children: SmallVec<[StackSafe<AnyElement>; 2]>,
1759 prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static>>,
1760 image_cache: Option<Box<dyn ImageCacheProvider>>,
1761 prepaint_order_fn: Option<Box<dyn Fn(&mut Window, &mut App) -> SmallVec<[usize; 8]>>>,
1762}
1763
1764impl Div {
1765 pub fn on_children_prepainted(
1768 mut self,
1769 listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
1770 ) -> Self {
1771 self.prepaint_listener = Some(Box::new(listener));
1772 self
1773 }
1774
1775 pub fn image_cache(mut self, cache: impl ImageCacheProvider) -> Self {
1777 self.image_cache = Some(Box::new(cache));
1778 self
1779 }
1780
1781 pub fn with_dynamic_prepaint_order(
1790 mut self,
1791 order_fn: impl Fn(&mut Window, &mut App) -> SmallVec<[usize; 8]> + 'static,
1792 ) -> Self {
1793 self.prepaint_order_fn = Some(Box::new(order_fn));
1794 self
1795 }
1796}
1797
1798pub struct DivFrameState {
1805 child_layout_ids: SmallVec<[LayoutId; 2]>,
1806}
1807
1808#[derive(Clone)]
1810pub struct DivInspectorState {
1811 #[cfg(any(feature = "inspector", debug_assertions))]
1815 pub base_style: Box<StyleRefinement>,
1816 pub bounds: Bounds<Pixels>,
1818 pub content_size: Size<Pixels>,
1820}
1821
1822impl Styled for Div {
1823 fn style(&mut self) -> &mut StyleRefinement {
1824 &mut self.interactivity.base_style
1825 }
1826}
1827
1828impl InteractiveElement for Div {
1829 fn interactivity(&mut self) -> &mut Interactivity {
1830 &mut self.interactivity
1831 }
1832}
1833
1834impl ParentElement for Div {
1835 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1836 #[cfg(feature = "stacker")]
1837 self.children
1838 .extend(elements.into_iter().map(StackSafe::new));
1839 #[cfg(not(feature = "stacker"))]
1840 self.children.extend(elements);
1841 }
1842}
1843
1844impl Element for Div {
1845 type RequestLayoutState = DivFrameState;
1846 type PrepaintState = Option<Hitbox>;
1847
1848 fn id(&self) -> Option<ElementId> {
1849 self.interactivity.element_id.clone()
1850 }
1851
1852 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
1853 self.interactivity.source_location()
1854 }
1855
1856 fn a11y_role(&self) -> Option<accesskit::Role> {
1857 self.interactivity
1860 .override_role
1861 .filter(|role| *role != accesskit::Role::GenericContainer)
1862 }
1863
1864 fn write_a11y_info(&self, node: &mut accesskit::Node) {
1865 self.interactivity.write_a11y_info(node);
1866 }
1867
1868 fn a11y_synthetic_children(
1869 &mut self,
1870 _prepaint: &mut Self::PrepaintState,
1871 builder: &mut crate::A11ySubtreeBuilder,
1872 ) {
1873 if let Some(f) = self.interactivity.a11y_synthetic_children.take() {
1874 f(builder);
1875 }
1876 }
1877
1878 #[cfg_attr(feature = "stacker", stacksafe::stacksafe)]
1879 fn request_layout(
1880 &mut self,
1881 global_id: Option<&GlobalElementId>,
1882 inspector_id: Option<&InspectorElementId>,
1883 window: &mut Window,
1884 cx: &mut App,
1885 ) -> (LayoutId, Self::RequestLayoutState) {
1886 let mut child_layout_ids = SmallVec::new();
1887 let image_cache = self
1888 .image_cache
1889 .as_mut()
1890 .map(|provider| provider.provide(window, cx));
1891
1892 let layout_id = window.with_image_cache(image_cache, |window| {
1893 self.interactivity.request_layout(
1894 global_id,
1895 inspector_id,
1896 window,
1897 cx,
1898 |style, window, cx| {
1899 window.with_text_style(style.text_style().cloned(), |window| {
1900 child_layout_ids = self
1901 .children
1902 .iter_mut()
1903 .map(|child| child.request_layout(window, cx))
1904 .collect::<SmallVec<_>>();
1905 window.request_layout(style, child_layout_ids.iter().copied(), cx)
1906 })
1907 },
1908 )
1909 });
1910
1911 (layout_id, DivFrameState { child_layout_ids })
1912 }
1913
1914 #[cfg_attr(feature = "stacker", stacksafe::stacksafe)]
1915 fn prepaint(
1916 &mut self,
1917 global_id: Option<&GlobalElementId>,
1918 inspector_id: Option<&InspectorElementId>,
1919 bounds: Bounds<Pixels>,
1920 request_layout: &mut Self::RequestLayoutState,
1921 window: &mut Window,
1922 cx: &mut App,
1923 ) -> Option<Hitbox> {
1924 let image_cache = self
1925 .image_cache
1926 .as_mut()
1927 .map(|provider| provider.provide(window, cx));
1928
1929 let has_prepaint_listener = self.prepaint_listener.is_some();
1930 let mut children_bounds = Vec::with_capacity(if has_prepaint_listener {
1931 request_layout.child_layout_ids.len()
1932 } else {
1933 0
1934 });
1935
1936 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1937 let mut child_max = Point::default();
1938 if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1939 *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
1940 }
1941 let content_size = if request_layout.child_layout_ids.is_empty() {
1942 bounds.size
1943 } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1944 let mut state = scroll_handle.0.borrow_mut();
1945 state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1946 for child_layout_id in &request_layout.child_layout_ids {
1947 let child_bounds = window.layout_bounds(*child_layout_id);
1948 child_min = child_min.min(&child_bounds.origin);
1949 child_max = child_max.max(&child_bounds.bottom_right());
1950 state.child_bounds.push(child_bounds);
1951 }
1952 (child_max - child_min).into()
1953 } else {
1954 for child_layout_id in &request_layout.child_layout_ids {
1955 let child_bounds = window.layout_bounds(*child_layout_id);
1956 child_min = child_min.min(&child_bounds.origin);
1957 child_max = child_max.max(&child_bounds.bottom_right());
1958
1959 if has_prepaint_listener {
1960 children_bounds.push(child_bounds);
1961 }
1962 }
1963 (child_max - child_min).into()
1964 };
1965
1966 if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1967 scroll_handle.scroll_to_active_item();
1968 }
1969
1970 self.interactivity.prepaint(
1971 global_id,
1972 inspector_id,
1973 bounds,
1974 content_size,
1975 window,
1976 cx,
1977 |style, scroll_offset, hitbox, window, cx| {
1978 if style.display == Display::None {
1980 return hitbox;
1981 }
1982
1983 window.with_image_cache(image_cache, |window| {
1984 window.with_element_offset(scroll_offset, |window| {
1985 if let Some(order_fn) = &self.prepaint_order_fn {
1986 let order = order_fn(window, cx);
1987 for idx in order {
1988 if let Some(child) = self.children.get_mut(idx) {
1989 child.prepaint(window, cx);
1990 }
1991 }
1992 } else {
1993 for child in &mut self.children {
1994 child.prepaint(window, cx);
1995 }
1996 }
1997 });
1998
1999 if let Some(listener) = self.prepaint_listener.as_ref() {
2000 listener(children_bounds, window, cx);
2001 }
2002 });
2003
2004 hitbox
2005 },
2006 )
2007 }
2008
2009 #[cfg_attr(feature = "stacker", stacksafe::stacksafe)]
2010 fn paint(
2011 &mut self,
2012 global_id: Option<&GlobalElementId>,
2013 inspector_id: Option<&InspectorElementId>,
2014 bounds: Bounds<Pixels>,
2015 _request_layout: &mut Self::RequestLayoutState,
2016 hitbox: &mut Option<Hitbox>,
2017 window: &mut Window,
2018 cx: &mut App,
2019 ) {
2020 let image_cache = self
2021 .image_cache
2022 .as_mut()
2023 .map(|provider| provider.provide(window, cx));
2024
2025 window.with_image_cache(image_cache, |window| {
2026 self.interactivity.paint(
2027 global_id,
2028 inspector_id,
2029 bounds,
2030 hitbox.as_ref(),
2031 window,
2032 cx,
2033 |style, window, cx| {
2034 if style.display == Display::None {
2036 return;
2037 }
2038
2039 for child in &mut self.children {
2040 child.paint(window, cx);
2041 }
2042 },
2043 )
2044 });
2045 }
2046}
2047
2048impl IntoElement for Div {
2049 type Element = Self;
2050
2051 fn into_element(self) -> Self::Element {
2052 self
2053 }
2054}
2055
2056#[derive(Default)]
2057pub(crate) struct AriaProperties {
2058 pub(crate) author_id: Option<SharedString>,
2059 pub(crate) label: Option<SharedString>,
2060 pub(crate) description: Option<SharedString>,
2061 pub(crate) keyshortcuts: Option<SharedString>,
2062 pub(crate) selected: Option<bool>,
2063 pub(crate) expanded: Option<bool>,
2064 pub(crate) toggled: Option<accesskit::Toggled>,
2065 pub(crate) numeric_value: Option<f64>,
2066 pub(crate) min_numeric_value: Option<f64>,
2067 pub(crate) max_numeric_value: Option<f64>,
2068 pub(crate) numeric_value_step: Option<f64>,
2069 pub(crate) value: Option<SharedString>,
2070 pub(crate) placeholder: Option<SharedString>,
2071 pub(crate) orientation: Option<accesskit::Orientation>,
2072 pub(crate) level: Option<usize>,
2073 pub(crate) position_in_set: Option<usize>,
2074 pub(crate) size_of_set: Option<usize>,
2075 pub(crate) row_index: Option<usize>,
2076 pub(crate) column_index: Option<usize>,
2077 pub(crate) row_count: Option<usize>,
2078 pub(crate) column_count: Option<usize>,
2079}
2080
2081#[derive(Default)]
2084pub struct Interactivity {
2085 pub element_id: Option<ElementId>,
2087 pub active: Option<bool>,
2089 pub hovered: Option<bool>,
2092 pub(crate) tooltip_id: Option<TooltipId>,
2093 pub(crate) content_size: Size<Pixels>,
2094 pub(crate) key_context: Option<KeyContext>,
2095 pub(crate) focusable: bool,
2096 pub(crate) tracked_focus_handle: Option<FocusHandle>,
2097 pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
2098 pub(crate) scroll_anchor: Option<ScrollAnchor>,
2099 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2100 pub(crate) ongoing_scroll: Option<Rc<RefCell<OngoingScroll>>>,
2101 pub(crate) group: Option<SharedString>,
2102 pub base_style: Box<StyleRefinement>,
2105 pub(crate) focus_style: Option<Box<StyleRefinement>>,
2106 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
2107 pub(crate) focus_visible_style: Option<Box<StyleRefinement>>,
2108 pub(crate) hover_style: Option<Box<StyleRefinement>>,
2109 pub(crate) group_hover_style: Option<GroupStyle>,
2110 pub(crate) active_style: Option<Box<StyleRefinement>>,
2111 pub(crate) group_active_style: Option<GroupStyle>,
2112 pub(crate) drag_over_styles: Vec<(
2113 TypeId,
2114 Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> StyleRefinement>,
2115 )>,
2116 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
2117 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
2118 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
2119 pub(crate) mouse_pressure_listeners: Vec<MousePressureListener>,
2120 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
2121 pub(crate) mouse_exit_listeners: Vec<MouseExitListener>,
2122 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
2123 pub(crate) pinch_listeners: Vec<PinchListener>,
2124 pub(crate) key_down_listeners: Vec<KeyDownListener>,
2125 pub(crate) key_up_listeners: Vec<KeyUpListener>,
2126 pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
2127 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
2128 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
2129 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
2130 pub(crate) click_listeners: Vec<ClickListener>,
2131 pub(crate) aux_click_listeners: Vec<ClickListener>,
2132 pub(crate) drag_listener: Option<DragListener>,
2133 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
2134 pub(crate) hover_listener_mode: HoverListenerMode,
2135 pub(crate) tooltip_builder: Option<TooltipBuilder>,
2136 pub(crate) tooltip_show_delay: Option<Duration>,
2137 pub(crate) window_control: Option<WindowControlArea>,
2138 pub(crate) hitbox_behavior: HitboxBehavior,
2139 pub(crate) tab_index: Option<isize>,
2140 pub(crate) tab_group: bool,
2141 pub(crate) tab_stop: bool,
2142
2143 pub(crate) a11y_action_listeners:
2144 Vec<(accesskit::Action, crate::window::a11y::A11yActionListener)>,
2145 pub(crate) a11y_synthetic_children: Option<Box<dyn FnOnce(&mut crate::A11ySubtreeBuilder)>>,
2146 pub(crate) report_active_descendant_focus: bool,
2147 pub(crate) override_role: Option<accesskit::Role>,
2148 pub(crate) aria: AriaProperties,
2149
2150 #[cfg(any(feature = "inspector", debug_assertions))]
2151 pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
2152
2153 #[cfg(any(test, feature = "test-support"))]
2154 pub(crate) debug_selector: Option<String>,
2155}
2156
2157impl Interactivity {
2158 pub fn request_layout(
2160 &mut self,
2161 global_id: Option<&GlobalElementId>,
2162 _inspector_id: Option<&InspectorElementId>,
2163 window: &mut Window,
2164 cx: &mut App,
2165 f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
2166 ) -> LayoutId {
2167 #[cfg(any(feature = "inspector", debug_assertions))]
2168 window.with_inspector_state(
2169 _inspector_id,
2170 cx,
2171 |inspector_state: &mut Option<DivInspectorState>, _window| {
2172 if let Some(inspector_state) = inspector_state {
2173 self.base_style = inspector_state.base_style.clone();
2174 } else {
2175 *inspector_state = Some(DivInspectorState {
2176 base_style: self.base_style.clone(),
2177 bounds: Default::default(),
2178 content_size: Default::default(),
2179 })
2180 }
2181 },
2182 );
2183
2184 window.with_optional_element_state::<InteractiveElementState, _>(
2185 global_id,
2186 |element_state, window| {
2187 let mut element_state =
2188 element_state.map(|element_state| element_state.unwrap_or_default());
2189
2190 if let Some(element_state) = element_state.as_ref()
2191 && cx.has_active_drag()
2192 {
2193 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
2194 *pending_mouse_down.borrow_mut() = None;
2195 }
2196 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
2197 *clicked_state.borrow_mut() = ElementClickedState::default();
2198 }
2199 }
2200
2201 if self.focusable
2206 && self.tracked_focus_handle.is_none()
2207 && let Some(element_state) = element_state.as_mut()
2208 {
2209 let mut handle = element_state
2210 .focus_handle
2211 .get_or_insert_with(|| cx.focus_handle())
2212 .clone()
2213 .tab_stop(self.tab_stop);
2214
2215 if let Some(index) = self.tab_index {
2216 handle = handle.tab_index(index);
2217 }
2218
2219 self.tracked_focus_handle = Some(handle);
2220 }
2221
2222 if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
2223 let scroll_handle_state = scroll_handle.0.borrow();
2224 self.scroll_offset = Some(scroll_handle_state.offset.clone());
2225 self.ongoing_scroll = Some(scroll_handle_state.ongoing_scroll.clone());
2226 } else if (self.base_style.overflow.x == Some(Overflow::Scroll)
2227 || self.base_style.overflow.y == Some(Overflow::Scroll))
2228 && let Some(element_state) = element_state.as_mut()
2229 {
2230 self.scroll_offset = Some(
2231 element_state
2232 .scroll_offset
2233 .get_or_insert_with(Rc::default)
2234 .clone(),
2235 );
2236 self.ongoing_scroll = Some(
2237 element_state
2238 .ongoing_scroll
2239 .get_or_insert_with(|| Rc::new(RefCell::new(OngoingScroll::default())))
2240 .clone(),
2241 );
2242 }
2243
2244 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
2245 let layout_id = f(style, window, cx);
2246 (layout_id, element_state)
2247 },
2248 )
2249 }
2250
2251 pub fn prepaint<R>(
2253 &mut self,
2254 global_id: Option<&GlobalElementId>,
2255 _inspector_id: Option<&InspectorElementId>,
2256 bounds: Bounds<Pixels>,
2257 content_size: Size<Pixels>,
2258 window: &mut Window,
2259 cx: &mut App,
2260 f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
2261 ) -> R {
2262 self.content_size = content_size;
2263
2264 #[cfg(any(feature = "inspector", debug_assertions))]
2265 window.with_inspector_state(
2266 _inspector_id,
2267 cx,
2268 |inspector_state: &mut Option<DivInspectorState>, _window| {
2269 if let Some(inspector_state) = inspector_state {
2270 inspector_state.bounds = bounds;
2271 inspector_state.content_size = content_size;
2272 }
2273 },
2274 );
2275
2276 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2277 window.set_focus_handle(focus_handle, cx);
2278
2279 if window.a11y.is_active() {
2280 if let Some(global_id) = global_id {
2281 let node_id = global_id.accesskit_node_id();
2282 window.a11y.set_focusable(node_id, focus_handle.id);
2283 if focus_handle.is_focused(window) {
2284 window.a11y.set_focus(node_id);
2285 }
2286 } else if focus_handle.is_focused(window) {
2287 window
2291 .a11y
2292 .note_focus_without_node(focus_handle.id, "it has no element id");
2293 }
2294 }
2295 }
2296
2297 if self.report_active_descendant_focus && window.a11y.is_active() {
2298 if let Some(global_id) = global_id {
2299 window
2300 .a11y
2301 .set_active_descendant(global_id.accesskit_node_id());
2302 }
2303 }
2304 window.with_optional_element_state::<InteractiveElementState, _>(
2305 global_id,
2306 |element_state, window| {
2307 let mut element_state =
2308 element_state.map(|element_state| element_state.unwrap_or_default());
2309 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
2310
2311 if let Some(element_state) = element_state.as_mut() {
2312 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
2313 let clicked_state = clicked_state.borrow();
2314 self.active = Some(clicked_state.element);
2315 }
2316 if self.hover_style.is_some() || self.group_hover_style.is_some() {
2317 element_state
2318 .hover_state
2319 .get_or_insert_with(Default::default);
2320 }
2321 if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
2322 if self.tooltip_builder.is_some() {
2323 self.tooltip_id = set_tooltip_on_window(active_tooltip, window);
2324 } else {
2325 element_state.active_tooltip.take();
2327 }
2328 }
2329 }
2330
2331 window.with_text_style(style.text_style().cloned(), |window| {
2332 window.with_content_mask(
2333 style.overflow_mask(bounds, window.rem_size()),
2334 |window| {
2335 let hitbox = if self.should_insert_hitbox(&style, window, cx) {
2336 Some(window.insert_hitbox(bounds, self.hitbox_behavior))
2337 } else {
2338 None
2339 };
2340
2341 let scroll_offset =
2342 self.clamp_scroll_position(bounds, &style, window, cx);
2343 let result = f(&style, scroll_offset, hitbox, window, cx);
2344 (result, element_state)
2345 },
2346 )
2347 })
2348 },
2349 )
2350 }
2351
2352 fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
2353 self.hitbox_behavior != HitboxBehavior::Normal
2354 || self.window_control.is_some()
2355 || style.mouse_cursor.is_some()
2356 || self.group.is_some()
2357 || self.scroll_offset.is_some()
2358 || self.tracked_focus_handle.is_some()
2359 || self.hover_style.is_some()
2360 || self.group_hover_style.is_some()
2361 || self.hover_listener.is_some()
2362 || !self.mouse_up_listeners.is_empty()
2363 || !self.mouse_pressure_listeners.is_empty()
2364 || !self.mouse_down_listeners.is_empty()
2365 || !self.mouse_move_listeners.is_empty()
2366 || !self.mouse_exit_listeners.is_empty()
2367 || !self.click_listeners.is_empty()
2368 || !self.aux_click_listeners.is_empty()
2369 || !self.scroll_wheel_listeners.is_empty()
2370 || self.has_pinch_listeners()
2371 || self.drag_listener.is_some()
2372 || !self.drop_listeners.is_empty()
2373 || !self.drag_over_styles.is_empty()
2374 || self.tooltip_builder.is_some()
2375 || window.is_inspector_picking(cx)
2376 }
2377
2378 fn clamp_scroll_position(
2379 &self,
2380 bounds: Bounds<Pixels>,
2381 style: &Style,
2382 window: &mut Window,
2383 _cx: &mut App,
2384 ) -> Point<Pixels> {
2385 fn round_to_two_decimals(pixels: Pixels) -> Pixels {
2386 const ROUNDING_FACTOR: f32 = 100.0;
2387 (pixels * ROUNDING_FACTOR).round() / ROUNDING_FACTOR
2388 }
2389
2390 if let Some(scroll_offset) = self.scroll_offset.as_ref() {
2391 let mut scroll_to_bottom = false;
2392 let mut tracked_scroll_handle = self
2393 .tracked_scroll_handle
2394 .as_ref()
2395 .map(|handle| handle.0.borrow_mut());
2396 if let Some(mut scroll_handle_state) = tracked_scroll_handle.as_deref_mut() {
2397 scroll_handle_state.overflow = style.overflow;
2398 scroll_to_bottom = mem::take(&mut scroll_handle_state.scroll_to_bottom);
2399 }
2400
2401 let rem_size = window.rem_size();
2402 let padding = style
2407 .padding
2408 .to_pixels(bounds.size.into(), rem_size)
2409 .map(|edge| window.pixel_snap(*edge));
2410 let padding_size = size(padding.left + padding.right, padding.top + padding.bottom);
2411 let padded_content_size = self.content_size + padding_size;
2418 let scroll_max = Point::from(padded_content_size - bounds.size)
2419 .map(round_to_two_decimals)
2420 .max(&Default::default());
2421 let mut scroll_offset = scroll_offset.borrow_mut();
2424
2425 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.x, px(0.));
2426 if scroll_to_bottom {
2427 scroll_offset.y = -scroll_max.y;
2428 } else {
2429 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.y, px(0.));
2430 }
2431
2432 if let Some(mut scroll_handle_state) = tracked_scroll_handle {
2433 scroll_handle_state.max_offset = scroll_max;
2434 scroll_handle_state.bounds = bounds;
2435 }
2436
2437 *scroll_offset
2438 } else {
2439 Point::default()
2440 }
2441 }
2442
2443 pub fn paint(
2452 &mut self,
2453 global_id: Option<&GlobalElementId>,
2454 _inspector_id: Option<&InspectorElementId>,
2455 bounds: Bounds<Pixels>,
2456 hitbox: Option<&Hitbox>,
2457 window: &mut Window,
2458 cx: &mut App,
2459 f: impl FnOnce(&Style, &mut Window, &mut App),
2460 ) {
2461 self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window));
2462 window.with_optional_element_state::<InteractiveElementState, _>(
2463 global_id,
2464 |element_state, window| {
2465 let mut element_state =
2466 element_state.map(|element_state| element_state.unwrap_or_default());
2467
2468 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2469
2470 #[cfg(any(feature = "test-support", test))]
2471 if let Some(debug_selector) = &self.debug_selector {
2472 window
2473 .next_frame
2474 .debug_bounds
2475 .insert(debug_selector.clone(), bounds);
2476 }
2477
2478 self.paint_hover_group_handler(window, cx);
2479
2480 if style.visibility == Visibility::Hidden {
2481 return ((), element_state);
2482 }
2483
2484 let mut tab_group = None;
2485 if self.tab_group {
2486 tab_group = self.tab_index;
2487 }
2488
2489 window.with_element_opacity(style.opacity, |window| {
2490 style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
2491 window.with_text_style(style.text_style().cloned(), |window| {
2492 window.with_content_mask(
2493 style.overflow_mask(bounds, window.rem_size()),
2494 |window| {
2495 window.with_tab_group(tab_group, |window| {
2496 if let Some(focus_handle) = &self.tracked_focus_handle {
2505 window.next_frame.tab_stops.insert(focus_handle);
2506 }
2507 if let Some(hitbox) = hitbox {
2508 #[cfg(debug_assertions)]
2509 self.paint_debug_info(
2510 global_id, hitbox, &style, window, cx,
2511 );
2512
2513 if let Some(drag) = cx.active_drag.as_ref() {
2514 if let Some(mouse_cursor) = drag.cursor_style {
2515 window.set_window_cursor_style(mouse_cursor);
2516 }
2517 } else {
2518 if let Some(mouse_cursor) = style.mouse_cursor {
2519 window.set_cursor_style(mouse_cursor, hitbox);
2520 }
2521 }
2522
2523 if let Some(group) = self.group.clone() {
2524 GroupHitboxes::push(group, hitbox.id, cx);
2525 }
2526
2527 if let Some(area) = self.window_control {
2528 window.insert_window_control_hitbox(
2529 area,
2530 hitbox.clone(),
2531 );
2532 }
2533
2534 self.paint_mouse_listeners(
2535 hitbox,
2536 element_state.as_mut(),
2537 window,
2538 cx,
2539 );
2540 self.paint_scroll_listener(hitbox, &style, window, cx);
2541 }
2542
2543 self.paint_keyboard_listeners(window, cx);
2544
2545 if window.a11y.is_active() {
2546 if let Some(global_id) = global_id {
2547 if !self.a11y_action_listeners.is_empty() {
2548 let node_id = global_id.accesskit_node_id();
2549 for (action, listener) in
2550 self.a11y_action_listeners.drain(..)
2551 {
2552 window.on_a11y_action(
2553 node_id, action, listener,
2554 );
2555 }
2556 }
2557 }
2558 }
2559
2560 f(&style, window, cx);
2561
2562 if let Some(_hitbox) = hitbox {
2563 #[cfg(any(feature = "inspector", debug_assertions))]
2564 window.insert_inspector_hitbox(
2565 _hitbox.id,
2566 _inspector_id,
2567 cx,
2568 );
2569
2570 if let Some(group) = self.group.as_ref() {
2571 GroupHitboxes::pop(group, cx);
2572 }
2573 }
2574 })
2575 },
2576 );
2577 });
2578 });
2579 });
2580
2581 ((), element_state)
2582 },
2583 );
2584 }
2585
2586 #[cfg(debug_assertions)]
2587 fn paint_debug_info(
2588 &self,
2589 global_id: Option<&GlobalElementId>,
2590 hitbox: &Hitbox,
2591 style: &Style,
2592 window: &mut Window,
2593 cx: &mut App,
2594 ) {
2595 use crate::{BorderStyle, TextAlign};
2596
2597 if let Some(global_id) = global_id
2598 && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
2599 && hitbox.is_hovered(window)
2600 {
2601 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
2602 let element_id = format!("{global_id:?}");
2603 let str_len = element_id.len();
2604
2605 let render_debug_text = |window: &mut Window| {
2606 if let Some(text) = window
2607 .text_system()
2608 .shape_text(
2609 element_id.into(),
2610 FONT_SIZE,
2611 &[window.text_style().to_run(str_len)],
2612 None,
2613 None,
2614 )
2615 .ok()
2616 .and_then(|mut text| text.pop())
2617 {
2618 text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
2619 .ok();
2620
2621 let text_bounds = crate::Bounds {
2622 origin: hitbox.origin,
2623 size: text.size(FONT_SIZE),
2624 };
2625 if let Some(source_location) = self.source_location
2626 && text_bounds.contains(&window.mouse_position())
2627 && window.modifiers().secondary()
2628 {
2629 let secondary_held = window.modifiers().secondary();
2630 window.on_key_event({
2631 move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| {
2632 if e.modifiers.secondary() != secondary_held
2633 && text_bounds.contains(&window.mouse_position())
2634 {
2635 window.refresh();
2636 }
2637 }
2638 });
2639
2640 let was_hovered = hitbox.is_hovered(window);
2641 let current_view = window.current_view();
2642 window.on_mouse_event({
2643 let hitbox = hitbox.clone();
2644 move |_: &MouseMoveEvent, phase, window, cx| {
2645 if phase == DispatchPhase::Capture {
2646 let hovered = hitbox.is_hovered(window);
2647 if hovered != was_hovered {
2648 cx.notify(current_view)
2649 }
2650 }
2651 }
2652 });
2653
2654 window.on_mouse_event({
2655 let hitbox = hitbox.clone();
2656 move |e: &crate::MouseDownEvent, phase, window, cx| {
2657 if text_bounds.contains(&e.position)
2658 && phase.capture()
2659 && hitbox.is_hovered(window)
2660 {
2661 cx.stop_propagation();
2662 let Ok(dir) = std::env::current_dir() else {
2663 return;
2664 };
2665
2666 eprintln!(
2667 "This element was created at:\n{}:{}:{}",
2668 dir.join(source_location.file()).to_string_lossy(),
2669 source_location.line(),
2670 source_location.column()
2671 );
2672 }
2673 }
2674 });
2675 window.paint_quad(crate::outline(
2676 crate::Bounds {
2677 origin: hitbox.origin
2678 + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
2679 size: crate::Size {
2680 width: text_bounds.size.width,
2681 height: crate::px(1.),
2682 },
2683 },
2684 crate::red(),
2685 BorderStyle::default(),
2686 ))
2687 }
2688 }
2689 };
2690
2691 window.with_text_style(
2692 Some(crate::TextStyleRefinement {
2693 color: Some(crate::red()),
2694 line_height: Some(FONT_SIZE.into()),
2695 background_color: Some(crate::white()),
2696 ..Default::default()
2697 }),
2698 render_debug_text,
2699 )
2700 }
2701 }
2702
2703 fn paint_mouse_listeners(
2704 &mut self,
2705 hitbox: &Hitbox,
2706 element_state: Option<&mut InteractiveElementState>,
2707 window: &mut Window,
2708 cx: &mut App,
2709 ) {
2710 let is_focused = self
2711 .tracked_focus_handle
2712 .as_ref()
2713 .map(|handle| handle.is_focused(window))
2714 .unwrap_or(false);
2715
2716 if let Some(focus_handle) = self.tracked_focus_handle.clone() {
2720 let hitbox = hitbox.clone();
2721 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, cx| {
2722 if phase == DispatchPhase::Bubble
2723 && hitbox.is_hovered(window)
2724 && !window.default_prevented()
2725 {
2726 window.focus(&focus_handle, cx);
2727 window.prevent_default();
2730 }
2731 });
2732 }
2733
2734 for listener in self.mouse_down_listeners.drain(..) {
2735 let hitbox = hitbox.clone();
2736 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
2737 listener(event, phase, &hitbox, window, cx);
2738 })
2739 }
2740
2741 for listener in self.mouse_up_listeners.drain(..) {
2742 let hitbox = hitbox.clone();
2743 window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
2744 listener(event, phase, &hitbox, window, cx);
2745 })
2746 }
2747
2748 for listener in self.mouse_pressure_listeners.drain(..) {
2749 let hitbox = hitbox.clone();
2750 window.on_mouse_event(move |event: &MousePressureEvent, phase, window, cx| {
2751 listener(event, phase, &hitbox, window, cx);
2752 })
2753 }
2754
2755 for listener in self.mouse_move_listeners.drain(..) {
2756 let hitbox = hitbox.clone();
2757 window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
2758 listener(event, phase, &hitbox, window, cx);
2759 })
2760 }
2761
2762 for listener in self.mouse_exit_listeners.drain(..) {
2763 let hitbox = hitbox.clone();
2764 window.on_mouse_event(move |event: &MouseExitEvent, phase, window, cx| {
2765 listener(event, phase, &hitbox, window, cx);
2766 })
2767 }
2768
2769 for listener in self.scroll_wheel_listeners.drain(..) {
2770 let hitbox = hitbox.clone();
2771 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2772 listener(event, phase, &hitbox, window, cx);
2773 })
2774 }
2775
2776 for listener in self.pinch_listeners.drain(..) {
2777 let hitbox = hitbox.clone();
2778 window.on_mouse_event(move |event: &PinchEvent, phase, window, cx| {
2779 listener(event, phase, &hitbox, window, cx);
2780 })
2781 }
2782
2783 if self.hover_style.is_some()
2784 || self.base_style.mouse_cursor.is_some()
2785 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
2786 {
2787 let hitbox = hitbox.clone();
2788 let hover_state = self.hover_style.as_ref().and_then(|_| {
2789 element_state
2790 .as_ref()
2791 .and_then(|state| state.hover_state.as_ref())
2792 .cloned()
2793 });
2794 let current_view = window.current_view();
2795
2796 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2797 let hovered = hitbox.is_hovered(window);
2798 let was_hovered = hover_state
2799 .as_ref()
2800 .is_some_and(|state| state.borrow().element);
2801 if phase == DispatchPhase::Capture && hovered != was_hovered {
2802 if let Some(hover_state) = &hover_state {
2803 hover_state.borrow_mut().element = hovered;
2804 cx.notify(current_view);
2805 }
2806 }
2807 });
2808 }
2809
2810 if let Some(group_hover) = self.group_hover_style.as_ref() {
2811 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
2812 let hover_state = element_state
2813 .as_ref()
2814 .and_then(|element| element.hover_state.as_ref())
2815 .cloned();
2816 let current_view = window.current_view();
2817
2818 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2819 let group_hovered = group_hitbox_id.is_hovered(window);
2820 let was_group_hovered = hover_state
2821 .as_ref()
2822 .is_some_and(|state| state.borrow().group);
2823 if phase == DispatchPhase::Capture && group_hovered != was_group_hovered {
2824 if let Some(hover_state) = &hover_state {
2825 hover_state.borrow_mut().group = group_hovered;
2826 cx.notify(current_view);
2827 }
2828 }
2829 });
2830 }
2831 }
2832
2833 let drag_cursor_style = self.base_style.as_ref().mouse_cursor;
2834
2835 let mut drag_listener = mem::take(&mut self.drag_listener);
2836 let drop_listeners = mem::take(&mut self.drop_listeners);
2837 let click_listeners = mem::take(&mut self.click_listeners);
2838 let aux_click_listeners = mem::take(&mut self.aux_click_listeners);
2839 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
2840
2841 if !drop_listeners.is_empty() {
2842 let hitbox = hitbox.clone();
2843 window.on_mouse_event({
2844 move |_: &MouseUpEvent, phase, window, cx| {
2845 if let Some(drag) = &cx.active_drag
2846 && phase == DispatchPhase::Bubble
2847 && hitbox.is_hovered(window)
2848 {
2849 let drag_state_type = drag.value.as_ref().type_id();
2850 for (drop_state_type, listener) in &drop_listeners {
2851 if *drop_state_type == drag_state_type {
2852 let drag = cx
2853 .active_drag
2854 .take()
2855 .expect("checked for type drag state type above");
2856
2857 let mut can_drop = true;
2858 if let Some(predicate) = &can_drop_predicate {
2859 can_drop = predicate(drag.value.as_ref(), window, cx);
2860 }
2861
2862 if can_drop {
2863 listener(drag.value.as_ref(), window, cx);
2864 window.refresh();
2865 cx.stop_propagation();
2866 }
2867 }
2868 }
2869 }
2870 }
2871 });
2872 }
2873
2874 if let Some(element_state) = element_state {
2875 if !click_listeners.is_empty()
2876 || !aux_click_listeners.is_empty()
2877 || drag_listener.is_some()
2878 {
2879 let pending_mouse_down = element_state
2880 .pending_mouse_down
2881 .get_or_insert_with(Default::default)
2882 .clone();
2883
2884 let pending_keyboard_down = element_state
2885 .pending_keyboard_down
2886 .get_or_insert_with(Default::default)
2887 .clone();
2888
2889 let clicked_state = element_state
2890 .clicked_state
2891 .get_or_insert_with(Default::default)
2892 .clone();
2893
2894 window.on_mouse_event({
2895 let pending_mouse_down = pending_mouse_down.clone();
2896 let hitbox = hitbox.clone();
2897 let has_aux_click_listeners = !aux_click_listeners.is_empty();
2898 move |event: &MouseDownEvent, phase, window, _cx| {
2899 if phase == DispatchPhase::Bubble
2900 && (event.button == MouseButton::Left || has_aux_click_listeners)
2901 && hitbox.is_hovered(window)
2902 {
2903 *pending_mouse_down.borrow_mut() = Some(event.clone());
2904 window.refresh();
2905 }
2906 }
2907 });
2908
2909 window.on_mouse_event({
2910 let pending_mouse_down = pending_mouse_down.clone();
2911 let hitbox = hitbox.clone();
2912 move |event: &MouseMoveEvent, phase, window, cx| {
2913 if phase == DispatchPhase::Capture {
2914 return;
2915 }
2916
2917 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2918 if let Some(mouse_down) = pending_mouse_down.clone()
2919 && !cx.has_active_drag()
2920 && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
2921 && let Some(listener) = drag_listener.take()
2922 && mouse_down.button == MouseButton::Left
2923 {
2924 *clicked_state.borrow_mut() = ElementClickedState::default();
2925 let cursor_offset = event.position - hitbox.origin;
2926 let drag = (listener.render)(
2927 listener.value.as_ref(),
2928 cursor_offset,
2929 window,
2930 cx,
2931 );
2932 let external_payload_source =
2933 listener.external_payload.map(|external_payload| {
2934 let value = listener.value.clone();
2935 Box::new(move |window: &mut Window, cx: &mut App| {
2936 external_payload(value.as_ref(), window, cx)
2937 })
2938 as ExternalDragPayloadSource
2939 });
2940 cx.active_drag = Some(AnyDrag {
2941 view: drag,
2942 value: listener.value,
2943 cursor_offset,
2944 cursor_style: drag_cursor_style,
2945 external_payload_source,
2946 });
2947 pending_mouse_down.take();
2948 window.refresh();
2949 cx.stop_propagation();
2950 }
2951 }
2952 });
2953
2954 if is_focused {
2955 window.on_key_event({
2965 let pending_keyboard_down = pending_keyboard_down.clone();
2966 move |event: &KeyDownEvent, phase, window, _cx| {
2967 if phase.bubble() && !window.default_prevented() {
2968 let stroke = &event.keystroke;
2969 let is_activation_key = (stroke.key.eq("enter")
2970 || stroke.key.eq("space"))
2971 && !stroke.modifiers.modified();
2972 *pending_keyboard_down.borrow_mut() =
2973 is_activation_key.then_some(window.focus_generation);
2974 }
2975 }
2976 });
2977
2978 window.on_key_event({
2980 let click_listeners = click_listeners.clone();
2981 let hitbox = hitbox.clone();
2982 move |event: &KeyUpEvent, phase, window, cx| {
2983 if phase.bubble() && !window.default_prevented() {
2984 let stroke = &event.keystroke;
2985 let keyboard_button = if stroke.key.eq("enter") {
2986 Some(KeyboardButton::Enter)
2987 } else if stroke.key.eq("space") {
2988 Some(KeyboardButton::Space)
2989 } else {
2990 None
2991 };
2992
2993 if let Some(button) = keyboard_button
2994 && !stroke.modifiers.modified()
2995 {
2996 let pending =
2997 std::mem::take(&mut *pending_keyboard_down.borrow_mut());
2998 if pending != Some(window.focus_generation) {
2999 return;
3000 }
3001
3002 let click_event = ClickEvent::Keyboard(KeyboardClickEvent {
3003 button,
3004 bounds: hitbox.bounds,
3005 });
3006
3007 for listener in &click_listeners {
3008 listener(&click_event, window, cx);
3009 }
3010 } else {
3011 *pending_keyboard_down.borrow_mut() = None;
3015 }
3016 }
3017 }
3018 });
3019 }
3020
3021 window.on_mouse_event({
3022 let mut captured_mouse_down = None;
3023 let hitbox = hitbox.clone();
3024 move |event: &MouseUpEvent, phase, window, cx| match phase {
3025 DispatchPhase::Capture => {
3029 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
3030 if pending_mouse_down.is_some() && hitbox.is_hovered(window) {
3031 captured_mouse_down = pending_mouse_down.take();
3032 window.refresh();
3033 } else if pending_mouse_down.is_some() {
3034 pending_mouse_down.take();
3040 window.refresh();
3041 }
3042 }
3043 DispatchPhase::Bubble => {
3045 if let Some(mouse_down) = captured_mouse_down.take() {
3046 let btn = mouse_down.button;
3047
3048 let mouse_click = ClickEvent::Mouse(MouseClickEvent {
3049 down: mouse_down,
3050 up: event.clone(),
3051 });
3052
3053 match btn {
3054 MouseButton::Left => {
3055 for listener in &click_listeners {
3056 listener(&mouse_click, window, cx);
3057 }
3058 }
3059 _ => {
3060 for listener in &aux_click_listeners {
3061 listener(&mouse_click, window, cx);
3062 }
3063 }
3064 }
3065 }
3066 }
3067 }
3068 });
3069 }
3070
3071 if let Some(hover_listener) = self.hover_listener.take() {
3072 let was_hovered = element_state
3073 .hover_listener_state
3074 .get_or_insert_with(Default::default)
3075 .clone();
3076 let has_mouse_down = element_state
3077 .pending_mouse_down
3078 .get_or_insert_with(Default::default)
3079 .clone();
3080 let hover_listener = Rc::new(hover_listener);
3081 let hover_listener_state = was_hovered.clone();
3082 let update_hover = move |is_hovered: bool, window: &mut Window, cx: &mut App| {
3083 let mut was_hovered = hover_listener_state.borrow_mut();
3084 if is_hovered != *was_hovered {
3085 *was_hovered = is_hovered;
3086 drop(was_hovered);
3087 hover_listener(&is_hovered, window, cx);
3088 }
3089 };
3090 let hover_listener_mode = self.hover_listener_mode;
3091
3092 if has_mouse_down.borrow().is_none() {
3093 let is_hovered =
3094 !cx.has_active_drag() && hover_listener_mode.is_hovered(hitbox, window);
3095 if is_hovered != *was_hovered.borrow() {
3096 let update_hover = update_hover.clone();
3097 window.defer(cx, move |window, cx| {
3098 update_hover(is_hovered, window, cx);
3099 });
3100 }
3101 }
3102
3103 window.on_mouse_event({
3104 let update_hover = update_hover.clone();
3105 let hitbox = hitbox.clone();
3106 move |_: &MouseMoveEvent, phase, window, cx| {
3107 if phase == DispatchPhase::Bubble {
3108 let is_hovered = has_mouse_down.borrow().is_none()
3109 && !cx.has_active_drag()
3110 && hover_listener_mode.is_hovered(&hitbox, window);
3111 update_hover(is_hovered, window, cx);
3112 }
3113 }
3114 });
3115
3116 window.on_mouse_event(move |_: &MouseExitEvent, phase, window, cx| {
3119 if phase == DispatchPhase::Bubble {
3120 update_hover(false, window, cx);
3121 }
3122 });
3123 }
3124
3125 if let Some(tooltip_builder) = self.tooltip_builder.take() {
3126 let active_tooltip = element_state
3127 .active_tooltip
3128 .get_or_insert_with(Default::default)
3129 .clone();
3130 let pending_mouse_down = element_state
3131 .pending_mouse_down
3132 .get_or_insert_with(Default::default)
3133 .clone();
3134
3135 let tooltip_is_hoverable = tooltip_builder.hoverable;
3136 let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| {
3137 Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable))
3138 });
3139 let check_is_hovered_during_prepaint = Rc::new({
3141 let pending_mouse_down = pending_mouse_down.clone();
3142 let source_bounds = hitbox.bounds;
3143 move |window: &Window| {
3144 !window.last_input_was_keyboard()
3145 && pending_mouse_down.borrow().is_none()
3146 && source_bounds.contains(&window.mouse_position())
3147 }
3148 });
3149 let check_is_hovered = Rc::new({
3150 let hitbox = hitbox.clone();
3151 move |window: &Window| {
3152 pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window)
3153 }
3154 });
3155 register_tooltip_mouse_handlers(
3156 &active_tooltip,
3157 self.tooltip_id,
3158 build_tooltip,
3159 check_is_hovered,
3160 check_is_hovered_during_prepaint,
3161 self.tooltip_show_delay,
3162 window,
3163 );
3164 }
3165
3166 let active_state = element_state
3169 .clicked_state
3170 .get_or_insert_with(Default::default)
3171 .clone();
3172
3173 {
3174 let active_state = active_state.clone();
3175 window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| {
3176 if phase == DispatchPhase::Capture && active_state.borrow().is_clicked() {
3177 *active_state.borrow_mut() = ElementClickedState::default();
3178 window.refresh();
3179 }
3180 });
3181 }
3182
3183 {
3184 let active_group_hitbox = self
3185 .group_active_style
3186 .as_ref()
3187 .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
3188 let hitbox = hitbox.clone();
3189 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| {
3190 if phase == DispatchPhase::Bubble && !window.default_prevented() {
3191 let group_hovered = active_group_hitbox
3192 .is_some_and(|group_hitbox_id| group_hitbox_id.is_hovered(window));
3193 let element_hovered = hitbox.is_hovered(window);
3194 if group_hovered || element_hovered {
3195 *active_state.borrow_mut() = ElementClickedState {
3196 group: group_hovered,
3197 element: element_hovered,
3198 };
3199 window.refresh();
3200 }
3201 }
3202 });
3203 }
3204 }
3205 }
3206
3207 fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) {
3208 let key_down_listeners = mem::take(&mut self.key_down_listeners);
3209 let key_up_listeners = mem::take(&mut self.key_up_listeners);
3210 let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
3211 let action_listeners = mem::take(&mut self.action_listeners);
3212 if let Some(context) = self.key_context.clone() {
3213 window.set_key_context(context);
3214 }
3215
3216 for listener in key_down_listeners {
3217 window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| {
3218 listener(event, phase, window, cx);
3219 })
3220 }
3221
3222 for listener in key_up_listeners {
3223 window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| {
3224 listener(event, phase, window, cx);
3225 })
3226 }
3227
3228 for listener in modifiers_changed_listeners {
3229 window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| {
3230 listener(event, window, cx);
3231 })
3232 }
3233
3234 for (action_type, listener) in action_listeners {
3235 window.on_action(action_type, listener)
3236 }
3237 }
3238
3239 fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) {
3240 let group_hitbox = self
3241 .group_hover_style
3242 .as_ref()
3243 .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
3244
3245 if let Some(group_hitbox) = group_hitbox {
3246 let was_hovered = group_hitbox.is_hovered(window);
3247 let current_view = window.current_view();
3248 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
3249 let hovered = group_hitbox.is_hovered(window);
3250 if phase == DispatchPhase::Capture && hovered != was_hovered {
3251 cx.notify(current_view);
3252 }
3253 });
3254 }
3255 }
3256
3257 fn paint_scroll_listener(
3258 &self,
3259 hitbox: &Hitbox,
3260 style: &Style,
3261 window: &mut Window,
3262 _cx: &mut App,
3263 ) {
3264 if let Some(scroll_offset) = self.scroll_offset.clone() {
3265 let ongoing_scroll = self.ongoing_scroll.clone();
3266 let overflow = style.overflow;
3267 let allow_concurrent_scroll = style.allow_concurrent_scroll;
3268 let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
3269 let line_height = window.line_height();
3270 let hitbox = hitbox.clone();
3271 let current_view = window.current_view();
3272 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
3273 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
3274 let mut scroll_offset = scroll_offset.borrow_mut();
3275 let old_scroll_offset = *scroll_offset;
3276 let mut delta = event.delta.pixel_delta(line_height);
3277
3278 if restrict_scroll_to_axis
3279 && event.delta.precise()
3280 && let Some(ongoing_scroll) = &ongoing_scroll
3281 {
3282 ongoing_scroll
3283 .borrow_mut()
3284 .filter(&mut delta, event.touch_phase);
3285 }
3286
3287 let mut delta_x = match overflow.x {
3288 Overflow::Scroll if !delta.x.is_zero() => delta.x,
3289 Overflow::Scroll
3290 if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll =>
3291 {
3292 delta.y
3293 }
3294 _ => Pixels::ZERO,
3295 };
3296 let mut delta_y = match overflow.y {
3297 Overflow::Scroll if !delta.y.is_zero() => delta.y,
3298 Overflow::Scroll
3299 if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll =>
3300 {
3301 delta.x
3302 }
3303 _ => Pixels::ZERO,
3304 };
3305 if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
3306 if delta_x.abs() > delta_y.abs() {
3307 delta_y = Pixels::ZERO;
3308 } else {
3309 delta_x = Pixels::ZERO;
3310 }
3311 }
3312 scroll_offset.y += delta_y;
3313 scroll_offset.x += delta_x;
3314 if *scroll_offset != old_scroll_offset {
3315 cx.notify(current_view);
3316 }
3317 }
3318 });
3319 }
3320 }
3321
3322 pub fn compute_style(
3324 &self,
3325 global_id: Option<&GlobalElementId>,
3326 hitbox: Option<&Hitbox>,
3327 window: &mut Window,
3328 cx: &mut App,
3329 ) -> Style {
3330 window.with_optional_element_state(global_id, |element_state, window| {
3331 let mut element_state =
3332 element_state.map(|element_state| element_state.unwrap_or_default());
3333 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
3334 (style, element_state)
3335 })
3336 }
3337
3338 fn compute_style_internal(
3340 &self,
3341 hitbox: Option<&Hitbox>,
3342 element_state: Option<&mut InteractiveElementState>,
3343 window: &mut Window,
3344 cx: &mut App,
3345 ) -> Style {
3346 let mut style = Style::default();
3347 style.refine(&self.base_style);
3348
3349 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
3350 if let Some(in_focus_style) = self.in_focus_style.as_ref()
3351 && focus_handle.within_focused(window, cx)
3352 {
3353 style.refine(in_focus_style);
3354 }
3355
3356 if let Some(focus_style) = self.focus_style.as_ref()
3357 && focus_handle.is_focused(window)
3358 {
3359 style.refine(focus_style);
3360 }
3361
3362 if let Some(focus_visible_style) = self.focus_visible_style.as_ref()
3363 && focus_handle.is_focused(window)
3364 && window.last_input_was_keyboard()
3365 {
3366 style.refine(focus_visible_style);
3367 }
3368 }
3369
3370 if !cx.has_active_drag() {
3371 if let Some(group_hover) = self.group_hover_style.as_ref() {
3372 let is_group_hovered =
3373 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
3374 !window.last_input_was_touch() && group_hitbox_id.is_hovered(window)
3375 } else if let Some(element_state) = element_state.as_ref() {
3376 !window.last_input_was_touch()
3377 && element_state
3378 .hover_state
3379 .as_ref()
3380 .map(|state| state.borrow().group)
3381 .unwrap_or(false)
3382 } else {
3383 false
3384 };
3385
3386 if is_group_hovered {
3387 style.refine(&group_hover.style);
3388 }
3389 }
3390
3391 if let Some(hover_style) = self.hover_style.as_ref() {
3392 let is_hovered = if let Some(hitbox) = hitbox {
3393 !window.last_input_was_touch() && hitbox.is_hovered(window)
3394 } else if let Some(element_state) = element_state.as_ref() {
3395 !window.last_input_was_touch()
3396 && element_state
3397 .hover_state
3398 .as_ref()
3399 .map(|state| state.borrow().element)
3400 .unwrap_or(false)
3401 } else {
3402 false
3403 };
3404
3405 if is_hovered {
3406 style.refine(hover_style);
3407 }
3408 }
3409 }
3410
3411 if let Some(hitbox) = hitbox {
3412 if let Some(drag) = cx.active_drag.take() {
3413 let mut can_drop = true;
3414 if let Some(can_drop_predicate) = &self.can_drop_predicate {
3415 can_drop = can_drop_predicate(drag.value.as_ref(), window, cx);
3416 }
3417
3418 if can_drop {
3419 for (state_type, group_drag_style) in &self.group_drag_over_styles {
3420 if let Some(group_hitbox_id) =
3421 GroupHitboxes::get(&group_drag_style.group, cx)
3422 && *state_type == drag.value.as_ref().type_id()
3423 && group_hitbox_id.is_hovered(window)
3424 {
3425 style.refine(&group_drag_style.style);
3426 }
3427 }
3428
3429 for (state_type, build_drag_over_style) in &self.drag_over_styles {
3430 if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window)
3431 {
3432 style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx));
3433 }
3434 }
3435 }
3436
3437 style.mouse_cursor = drag.cursor_style;
3438 cx.active_drag = Some(drag);
3439 }
3440 }
3441
3442 if let Some(element_state) = element_state {
3443 let clicked_state = element_state
3444 .clicked_state
3445 .get_or_insert_with(Default::default)
3446 .borrow();
3447 if clicked_state.group
3448 && let Some(group) = self.group_active_style.as_ref()
3449 {
3450 style.refine(&group.style)
3451 }
3452
3453 if let Some(active_style) = self.active_style.as_ref()
3454 && clicked_state.element
3455 {
3456 style.refine(active_style)
3457 }
3458 }
3459
3460 style
3461 }
3462
3463 pub(crate) fn write_a11y_info(&self, node: &mut accesskit::Node) {
3464 if let Some(id) = &self.aria.author_id {
3465 node.set_author_id(id.to_string());
3466 }
3467 if let Some(label) = &self.aria.label {
3468 node.set_label(label.to_string());
3469 }
3470 if let Some(description) = &self.aria.description {
3471 node.set_description(description.to_string());
3472 }
3473 if let Some(keyshortcuts) = &self.aria.keyshortcuts {
3474 node.set_keyboard_shortcut(keyshortcuts.to_string());
3475 }
3476 if let Some(selected) = self.aria.selected {
3477 node.set_selected(selected);
3478 }
3479 if let Some(expanded) = self.aria.expanded {
3480 node.set_expanded(expanded);
3481 }
3482 if let Some(toggled) = self.aria.toggled {
3483 node.set_toggled(toggled);
3484 }
3485 if let Some(value) = self.aria.numeric_value {
3486 node.set_numeric_value(value);
3487 }
3488 if let Some(value) = self.aria.min_numeric_value {
3489 node.set_min_numeric_value(value);
3490 }
3491 if let Some(value) = self.aria.max_numeric_value {
3492 node.set_max_numeric_value(value);
3493 }
3494 if let Some(step) = self.aria.numeric_value_step {
3495 node.set_numeric_value_step(step);
3496 }
3497 if let Some(value) = &self.aria.value {
3498 node.set_value(value.to_string());
3499 }
3500 if let Some(placeholder) = &self.aria.placeholder {
3501 node.set_placeholder(placeholder.to_string());
3502 }
3503 if let Some(orientation) = self.aria.orientation {
3504 node.set_orientation(orientation);
3505 }
3506 if let Some(level) = self.aria.level {
3507 node.set_level(level);
3508 }
3509 if let Some(position) = self.aria.position_in_set {
3510 node.set_position_in_set(position);
3511 }
3512 if let Some(size) = self.aria.size_of_set {
3513 node.set_size_of_set(size);
3514 }
3515 if let Some(index) = self.aria.row_index {
3516 node.set_row_index(index);
3517 }
3518 if let Some(index) = self.aria.column_index {
3519 node.set_column_index(index);
3520 }
3521 if let Some(count) = self.aria.row_count {
3522 node.set_row_count(count);
3523 }
3524 if let Some(count) = self.aria.column_count {
3525 node.set_column_count(count);
3526 }
3527 if !self.click_listeners.is_empty() {
3528 node.add_action(accesskit::Action::Click);
3529 }
3530 if self.tracked_focus_handle.is_some() || self.focusable {
3531 node.add_action(accesskit::Action::Focus);
3532 }
3533 for (action, _) in &self.a11y_action_listeners {
3534 node.add_action(*action);
3535 }
3536 }
3537}
3538
3539#[derive(Default)]
3542pub struct InteractiveElementState {
3543 pub(crate) focus_handle: Option<FocusHandle>,
3544 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
3545 pub(crate) hover_state: Option<Rc<RefCell<ElementHoverState>>>,
3546 pub(crate) hover_listener_state: Option<Rc<RefCell<bool>>>,
3547 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
3548 pub(crate) pending_keyboard_down: Option<Rc<RefCell<Option<u64>>>>,
3556 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
3557 ongoing_scroll: Option<Rc<RefCell<OngoingScroll>>>,
3558 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
3559}
3560
3561#[derive(Copy, Clone, Default, Eq, PartialEq)]
3563pub struct ElementClickedState {
3564 pub group: bool,
3566
3567 pub element: bool,
3569}
3570
3571impl ElementClickedState {
3572 fn is_clicked(&self) -> bool {
3573 self.group || self.element
3574 }
3575}
3576
3577#[derive(Copy, Clone, Default, Eq, PartialEq)]
3579pub struct ElementHoverState {
3580 pub group: bool,
3582
3583 pub element: bool,
3585}
3586
3587pub(crate) enum ActiveTooltip {
3588 WaitingForShow { _task: Task<()> },
3590 Visible {
3592 tooltip: AnyTooltip,
3593 is_hoverable: bool,
3594 },
3595 WaitingForHide {
3598 tooltip: AnyTooltip,
3599 _task: Task<()>,
3600 },
3601}
3602
3603pub(crate) fn clear_active_tooltip(
3604 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3605 window: &mut Window,
3606) {
3607 match active_tooltip.borrow_mut().take() {
3608 None => {}
3609 Some(ActiveTooltip::WaitingForShow { .. }) => {}
3610 Some(ActiveTooltip::Visible { .. }) => window.refresh(),
3611 Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(),
3612 }
3613}
3614
3615pub(crate) fn clear_active_tooltip_if_not_hoverable(
3616 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3617 window: &mut Window,
3618) {
3619 let should_clear = match active_tooltip.borrow().as_ref() {
3620 None => false,
3621 Some(ActiveTooltip::WaitingForShow { .. }) => false,
3622 Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable,
3623 Some(ActiveTooltip::WaitingForHide { .. }) => false,
3624 };
3625 if should_clear {
3626 active_tooltip.borrow_mut().take();
3627 window.refresh();
3628 }
3629}
3630
3631pub(crate) fn set_tooltip_on_window(
3632 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3633 window: &mut Window,
3634) -> Option<TooltipId> {
3635 let tooltip = match active_tooltip.borrow().as_ref() {
3636 None => return None,
3637 Some(ActiveTooltip::WaitingForShow { .. }) => return None,
3638 Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(),
3639 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(),
3640 };
3641 Some(window.set_tooltip(tooltip))
3642}
3643
3644pub(crate) fn register_tooltip_mouse_handlers(
3645 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3646 tooltip_id: Option<TooltipId>,
3647 build_tooltip: Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3648 check_is_hovered: Rc<dyn Fn(&Window) -> bool>,
3649 check_is_hovered_during_prepaint: Rc<dyn Fn(&Window) -> bool>,
3650 show_delay: Option<Duration>,
3651 window: &mut Window,
3652) {
3653 let current_view = window.current_view();
3654 let show_delay = show_delay.unwrap_or(DEFAULT_TOOLTIP_SHOW_DELAY);
3655
3656 window.on_mouse_event({
3657 let active_tooltip = active_tooltip.clone();
3658 let build_tooltip = build_tooltip.clone();
3659 let check_is_hovered = check_is_hovered.clone();
3660 move |_: &MouseMoveEvent, phase, window, cx| {
3661 handle_tooltip_mouse_move(
3662 &active_tooltip,
3663 &build_tooltip,
3664 &check_is_hovered,
3665 &check_is_hovered_during_prepaint,
3666 tooltip_id,
3667 current_view,
3668 phase,
3669 show_delay,
3670 window,
3671 cx,
3672 )
3673 }
3674 });
3675
3676 window.on_mouse_event({
3677 let active_tooltip = active_tooltip.clone();
3678 move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| {
3679 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3680 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3681 }
3682 }
3683 });
3684
3685 window.on_mouse_event({
3686 let active_tooltip = active_tooltip.clone();
3687 move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| {
3688 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3689 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3690 }
3691 }
3692 });
3693}
3694
3695fn handle_tooltip_mouse_move(
3707 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3708 build_tooltip: &Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3709 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3710 check_is_hovered_during_prepaint: &Rc<dyn Fn(&Window) -> bool>,
3711 tooltip_id: Option<TooltipId>,
3712 current_view: EntityId,
3713 phase: DispatchPhase,
3714 show_delay: Duration,
3715 window: &mut Window,
3716 cx: &mut App,
3717) {
3718 enum Action {
3721 None,
3722 CancelShow,
3723 ScheduleShow,
3724 CheckVisible,
3725 }
3726
3727 let action = match active_tooltip.borrow().as_ref() {
3728 None => {
3729 let is_hovered = check_is_hovered(window);
3730 if is_hovered && phase.bubble() {
3731 Action::ScheduleShow
3732 } else {
3733 Action::None
3734 }
3735 }
3736 Some(ActiveTooltip::WaitingForShow { .. }) => {
3737 let is_hovered = check_is_hovered(window);
3738 if is_hovered {
3739 Action::None
3740 } else {
3741 Action::CancelShow
3742 }
3743 }
3744 Some(ActiveTooltip::Visible { is_hoverable, .. }) => {
3745 if phase.capture()
3746 && !check_is_hovered(window)
3747 && (!*is_hoverable
3748 || !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)))
3749 {
3750 Action::CheckVisible
3751 } else {
3752 Action::None
3753 }
3754 }
3755 Some(ActiveTooltip::WaitingForHide { .. }) => {
3756 if phase.capture()
3757 && (check_is_hovered(window)
3758 || tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)))
3759 {
3760 Action::CheckVisible
3761 } else {
3762 Action::None
3763 }
3764 }
3765 };
3766
3767 match action {
3768 Action::None => {}
3769 Action::CancelShow => {
3770 active_tooltip.borrow_mut().take();
3772 }
3773 Action::ScheduleShow => {
3774 let delayed_show_task = window.spawn(cx, {
3775 let weak_active_tooltip = Rc::downgrade(active_tooltip);
3776 let build_tooltip = build_tooltip.clone();
3777 let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone();
3778 async move |cx| {
3779 cx.background_executor().timer(show_delay).await;
3780 let Some(active_tooltip) = weak_active_tooltip.upgrade() else {
3781 return;
3782 };
3783 cx.update(|window, cx| {
3784 let new_tooltip =
3785 build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| {
3786 let weak_active_tooltip = Rc::downgrade(&active_tooltip);
3787 ActiveTooltip::Visible {
3788 tooltip: AnyTooltip {
3789 view,
3790 mouse_position: window.mouse_position(),
3791 check_visible_and_update: Rc::new(
3792 move |tooltip_bounds, window, cx| {
3793 let Some(active_tooltip) =
3794 weak_active_tooltip.upgrade()
3795 else {
3796 return false;
3797 };
3798 handle_tooltip_check_visible_and_update(
3799 &active_tooltip,
3800 tooltip_is_hoverable,
3801 &check_is_hovered_during_prepaint,
3802 tooltip_bounds,
3803 window,
3804 cx,
3805 )
3806 },
3807 ),
3808 },
3809 is_hoverable: tooltip_is_hoverable,
3810 }
3811 });
3812 *active_tooltip.borrow_mut() = new_tooltip;
3813 window.refresh();
3814 })
3815 .ok();
3816 }
3817 });
3818 active_tooltip
3819 .borrow_mut()
3820 .replace(ActiveTooltip::WaitingForShow {
3821 _task: delayed_show_task,
3822 });
3823 }
3824 Action::CheckVisible => cx.notify(current_view),
3825 }
3826}
3827
3828fn handle_tooltip_check_visible_and_update(
3832 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3833 tooltip_is_hoverable: bool,
3834 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3835 tooltip_bounds: Bounds<Pixels>,
3836 window: &mut Window,
3837 cx: &mut App,
3838) -> bool {
3839 enum Action {
3842 None,
3843 Hide,
3844 ScheduleHide(AnyTooltip),
3845 CancelHide(AnyTooltip),
3846 }
3847
3848 let is_hovered = check_is_hovered(window)
3849 || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position()));
3850 let action = match active_tooltip.borrow().as_ref() {
3851 Some(ActiveTooltip::Visible { tooltip, .. }) => {
3852 if is_hovered {
3853 Action::None
3854 } else {
3855 if tooltip_is_hoverable {
3856 Action::ScheduleHide(tooltip.clone())
3857 } else {
3858 Action::Hide
3859 }
3860 }
3861 }
3862 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => {
3863 if is_hovered {
3864 Action::CancelHide(tooltip.clone())
3865 } else {
3866 Action::None
3867 }
3868 }
3869 None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None,
3870 };
3871
3872 match action {
3873 Action::None => {}
3874 Action::Hide => clear_active_tooltip(active_tooltip, window),
3875 Action::ScheduleHide(tooltip) => {
3876 let delayed_hide_task = window.spawn(cx, {
3877 let weak_active_tooltip = Rc::downgrade(active_tooltip);
3878 async move |cx| {
3879 cx.background_executor()
3880 .timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
3881 .await;
3882 let Some(active_tooltip) = weak_active_tooltip.upgrade() else {
3883 return;
3884 };
3885 if active_tooltip.borrow_mut().take().is_some() {
3886 cx.update(|window, _cx| window.refresh()).ok();
3887 }
3888 }
3889 });
3890 active_tooltip
3891 .borrow_mut()
3892 .replace(ActiveTooltip::WaitingForHide {
3893 tooltip,
3894 _task: delayed_hide_task,
3895 });
3896 }
3897 Action::CancelHide(tooltip) => {
3898 active_tooltip.borrow_mut().replace(ActiveTooltip::Visible {
3900 tooltip,
3901 is_hoverable: true,
3902 });
3903 }
3904 }
3905
3906 active_tooltip.borrow().is_some()
3907}
3908
3909#[derive(Default)]
3910pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
3911
3912impl Global for GroupHitboxes {}
3913
3914impl GroupHitboxes {
3915 pub fn get(name: &SharedString, cx: &mut App) -> Option<HitboxId> {
3916 cx.default_global::<Self>()
3917 .0
3918 .get(name)
3919 .and_then(|bounds_stack| bounds_stack.last())
3920 .cloned()
3921 }
3922
3923 pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) {
3924 cx.default_global::<Self>()
3925 .0
3926 .entry(name)
3927 .or_default()
3928 .push(hitbox_id);
3929 }
3930
3931 pub fn pop(name: &SharedString, cx: &mut App) {
3932 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
3933 }
3934}
3935
3936pub struct Stateful<E> {
3938 pub(crate) element: E,
3939}
3940
3941impl<E> Styled for Stateful<E>
3942where
3943 E: Styled,
3944{
3945 fn style(&mut self) -> &mut StyleRefinement {
3946 self.element.style()
3947 }
3948}
3949
3950impl<E> StatefulInteractiveElement for Stateful<E>
3951where
3952 E: Element,
3953 Self: InteractiveElement,
3954{
3955}
3956
3957impl<E> InteractiveElement for Stateful<E>
3958where
3959 E: InteractiveElement,
3960{
3961 fn interactivity(&mut self) -> &mut Interactivity {
3962 self.element.interactivity()
3963 }
3964}
3965
3966impl<E> Element for Stateful<E>
3967where
3968 E: Element,
3969{
3970 type RequestLayoutState = E::RequestLayoutState;
3971 type PrepaintState = E::PrepaintState;
3972
3973 fn id(&self) -> Option<ElementId> {
3974 self.element.id()
3975 }
3976
3977 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
3978 self.element.source_location()
3979 }
3980
3981 fn a11y_role(&self) -> Option<accesskit::Role> {
3982 self.element.a11y_role()
3983 }
3984
3985 fn write_a11y_info(&self, node: &mut accesskit::Node) {
3986 self.element.write_a11y_info(node);
3987 }
3988
3989 fn a11y_synthetic_children(
3990 &mut self,
3991 prepaint: &mut Self::PrepaintState,
3992 builder: &mut crate::A11ySubtreeBuilder,
3993 ) {
3994 self.element.a11y_synthetic_children(prepaint, builder);
3995 }
3996
3997 fn request_layout(
3998 &mut self,
3999 id: Option<&GlobalElementId>,
4000 inspector_id: Option<&InspectorElementId>,
4001 window: &mut Window,
4002 cx: &mut App,
4003 ) -> (LayoutId, Self::RequestLayoutState) {
4004 self.element.request_layout(id, inspector_id, window, cx)
4005 }
4006
4007 fn prepaint(
4008 &mut self,
4009 id: Option<&GlobalElementId>,
4010 inspector_id: Option<&InspectorElementId>,
4011 bounds: Bounds<Pixels>,
4012 state: &mut Self::RequestLayoutState,
4013 window: &mut Window,
4014 cx: &mut App,
4015 ) -> E::PrepaintState {
4016 self.element
4017 .prepaint(id, inspector_id, bounds, state, window, cx)
4018 }
4019
4020 fn paint(
4021 &mut self,
4022 id: Option<&GlobalElementId>,
4023 inspector_id: Option<&InspectorElementId>,
4024 bounds: Bounds<Pixels>,
4025 request_layout: &mut Self::RequestLayoutState,
4026 prepaint: &mut Self::PrepaintState,
4027 window: &mut Window,
4028 cx: &mut App,
4029 ) {
4030 self.element.paint(
4031 id,
4032 inspector_id,
4033 bounds,
4034 request_layout,
4035 prepaint,
4036 window,
4037 cx,
4038 );
4039 }
4040}
4041
4042impl<E> IntoElement for Stateful<E>
4043where
4044 E: Element,
4045{
4046 type Element = Self;
4047
4048 fn into_element(self) -> Self::Element {
4049 self
4050 }
4051}
4052
4053impl<E> ParentElement for Stateful<E>
4054where
4055 E: ParentElement,
4056{
4057 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
4058 self.element.extend(elements)
4059 }
4060}
4061
4062#[derive(Clone)]
4065pub struct ScrollAnchor {
4066 handle: ScrollHandle,
4067 last_origin: Rc<RefCell<Point<Pixels>>>,
4068}
4069
4070impl ScrollAnchor {
4071 pub fn for_handle(handle: ScrollHandle) -> Self {
4073 Self {
4074 handle,
4075 last_origin: Default::default(),
4076 }
4077 }
4078 pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) {
4080 let this = self.clone();
4081
4082 window.on_next_frame(move |_, _| {
4083 let viewport_bounds = this.handle.bounds();
4084 let self_bounds = *this.last_origin.borrow();
4085 this.handle.set_offset(viewport_bounds.origin - self_bounds);
4086 });
4087 }
4088}
4089
4090#[derive(Default, Debug)]
4091struct ScrollHandleState {
4092 offset: Rc<RefCell<Point<Pixels>>>,
4093 ongoing_scroll: Rc<RefCell<OngoingScroll>>,
4094 bounds: Bounds<Pixels>,
4095 max_offset: Point<Pixels>,
4096 child_bounds: Vec<Bounds<Pixels>>,
4097 scroll_to_bottom: bool,
4098 overflow: Point<Overflow>,
4099 active_item: Option<ScrollActiveItem>,
4100}
4101
4102#[derive(Default, Debug, Clone, Copy)]
4103struct ScrollActiveItem {
4104 index: usize,
4105 strategy: ScrollStrategy,
4106}
4107
4108#[derive(Default, Debug, Clone, Copy)]
4109enum ScrollStrategy {
4110 #[default]
4111 FirstVisible,
4112 Top,
4113}
4114
4115#[derive(Clone, Debug)]
4119pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
4120
4121impl Default for ScrollHandle {
4122 fn default() -> Self {
4123 Self::new()
4124 }
4125}
4126
4127impl ScrollHandle {
4128 pub fn new() -> Self {
4130 Self(Rc::default())
4131 }
4132
4133 pub fn offset(&self) -> Point<Pixels> {
4135 *self.0.borrow().offset.borrow()
4136 }
4137
4138 pub fn max_offset(&self) -> Point<Pixels> {
4140 self.0.borrow().max_offset
4141 }
4142
4143 pub fn top_item(&self) -> usize {
4145 let state = self.0.borrow();
4146 let top = state.bounds.top() - state.offset.borrow().y;
4147
4148 match state.child_bounds.binary_search_by(|bounds| {
4149 if top < bounds.top() {
4150 Ordering::Greater
4151 } else if top > bounds.bottom() {
4152 Ordering::Less
4153 } else {
4154 Ordering::Equal
4155 }
4156 }) {
4157 Ok(ix) => ix,
4158 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
4159 }
4160 }
4161
4162 pub fn bottom_item(&self) -> usize {
4164 let state = self.0.borrow();
4165 let bottom = state.bounds.bottom() - state.offset.borrow().y;
4166
4167 match state.child_bounds.binary_search_by(|bounds| {
4168 if bottom < bounds.top() {
4169 Ordering::Greater
4170 } else if bottom > bounds.bottom() {
4171 Ordering::Less
4172 } else {
4173 Ordering::Equal
4174 }
4175 }) {
4176 Ok(ix) => ix,
4177 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
4178 }
4179 }
4180
4181 pub fn bounds(&self) -> Bounds<Pixels> {
4183 self.0.borrow().bounds
4184 }
4185
4186 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
4188 self.0.borrow().child_bounds.get(ix).cloned()
4189 }
4190
4191 pub fn scroll_to_item(&self, ix: usize) {
4193 let mut state = self.0.borrow_mut();
4194 state.active_item = Some(ScrollActiveItem {
4195 index: ix,
4196 strategy: ScrollStrategy::default(),
4197 });
4198 }
4199
4200 pub fn scroll_to_top_of_item(&self, ix: usize) {
4203 let mut state = self.0.borrow_mut();
4204 state.active_item = Some(ScrollActiveItem {
4205 index: ix,
4206 strategy: ScrollStrategy::Top,
4207 });
4208 }
4209
4210 fn scroll_to_active_item(&self) {
4214 let mut state = self.0.borrow_mut();
4215
4216 let Some(active_item) = state.active_item else {
4217 return;
4218 };
4219
4220 let active_item = match state.child_bounds.get(active_item.index) {
4221 Some(bounds) => {
4222 let mut scroll_offset = state.offset.borrow_mut();
4223
4224 match active_item.strategy {
4225 ScrollStrategy::FirstVisible => {
4226 if state.overflow.y == Overflow::Scroll {
4227 let child_height = bounds.size.height;
4228 let viewport_height = state.bounds.size.height;
4229 if child_height > viewport_height {
4230 scroll_offset.y = state.bounds.top() - bounds.top();
4231 } else if bounds.top() + scroll_offset.y < state.bounds.top() {
4232 scroll_offset.y = state.bounds.top() - bounds.top();
4233 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
4234 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
4235 }
4236 }
4237 }
4238 ScrollStrategy::Top => {
4239 scroll_offset.y = state.bounds.top() - bounds.top();
4240 }
4241 }
4242
4243 if state.overflow.x == Overflow::Scroll {
4244 let child_width = bounds.size.width;
4245 let viewport_width = state.bounds.size.width;
4246 if child_width > viewport_width {
4247 scroll_offset.x = state.bounds.left() - bounds.left();
4248 } else if bounds.left() + scroll_offset.x < state.bounds.left() {
4249 scroll_offset.x = state.bounds.left() - bounds.left();
4250 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
4251 scroll_offset.x = state.bounds.right() - bounds.right();
4252 }
4253 }
4254 None
4255 }
4256 None => Some(active_item),
4257 };
4258 state.active_item = active_item;
4259 }
4260
4261 pub fn scroll_to_bottom(&self) {
4263 let mut state = self.0.borrow_mut();
4264 state.scroll_to_bottom = true;
4265 }
4266
4267 pub fn set_offset(&self, mut position: Point<Pixels>) {
4271 let state = self.0.borrow();
4272 *state.offset.borrow_mut() = position;
4273 }
4274
4275 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
4277 let ix = self.top_item();
4278 let state = self.0.borrow();
4279
4280 if let Some(child_bounds) = state.child_bounds.get(ix) {
4281 (
4282 ix,
4283 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
4284 )
4285 } else {
4286 (ix, px(0.))
4287 }
4288 }
4289
4290 pub fn logical_scroll_bottom(&self) -> (usize, Pixels) {
4292 let ix = self.bottom_item();
4293 let state = self.0.borrow();
4294
4295 if let Some(child_bounds) = state.child_bounds.get(ix) {
4296 (
4297 ix,
4298 child_bounds.bottom() + state.offset.borrow().y - state.bounds.bottom(),
4299 )
4300 } else {
4301 (ix, px(0.))
4302 }
4303 }
4304
4305 pub fn children_count(&self) -> usize {
4307 self.0.borrow().child_bounds.len()
4308 }
4309}
4310
4311#[cfg(test)]
4312mod tests {
4313 use super::*;
4314 use crate::{
4315 AnyWindowHandle, AppContext as _, Context, InputEvent, Keystroke, MouseMoveEvent,
4316 TestAppContext, canvas, util::FluentBuilder as _,
4317 };
4318 use std::{cell::Cell, rc::Weak};
4319
4320 struct GroupHoverTestView {
4321 render_count: Rc<Cell<usize>>,
4322 anonymous_paint_count: Rc<Cell<usize>>,
4323 stateful_width: Rc<Cell<Pixels>>,
4324 }
4325
4326 impl Render for GroupHoverTestView {
4327 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4328 self.render_count.set(self.render_count.get() + 1);
4329 let anonymous_paint_count = self.anonymous_paint_count.clone();
4330 let stateful_width = self.stateful_width.clone();
4331 div().size_full().child(
4332 div()
4333 .ml(px(20.))
4334 .mt(px(20.))
4335 .size(px(50.))
4336 .relative()
4337 .group("hover-group")
4338 .child(
4339 div()
4340 .absolute()
4341 .size_full()
4342 .invisible()
4343 .group_hover("hover-group", |style| style.visible())
4344 .child(canvas(
4345 |_, _, _| {},
4346 move |_, _, _, _| {
4347 anonymous_paint_count.set(anonymous_paint_count.get() + 1)
4348 },
4349 )),
4350 )
4351 .child(
4352 div()
4353 .id("stateful-group-hover-target")
4354 .absolute()
4355 .top_0()
4356 .left_0()
4357 .size(px(10.))
4358 .group_hover("hover-group", |style| style.size(px(20.)))
4359 .child(canvas(
4360 move |bounds, _, _| stateful_width.set(bounds.size.width),
4361 |_, _, _, _| {},
4362 )),
4363 ),
4364 )
4365 }
4366 }
4367
4368 #[gpui::test]
4369 fn group_hover_styles_update_only_on_transitions(cx: &mut TestAppContext) {
4370 let render_count = Rc::new(Cell::new(0));
4371 let anonymous_paint_count = Rc::new(Cell::new(0));
4372 let stateful_width = Rc::new(Cell::new(px(0.)));
4373 let window = cx.add_window({
4374 let render_count = render_count.clone();
4375 let anonymous_paint_count = anonymous_paint_count.clone();
4376 let stateful_width = stateful_width.clone();
4377 move |_, _| GroupHoverTestView {
4378 render_count,
4379 anonymous_paint_count,
4380 stateful_width,
4381 }
4382 });
4383 let window = AnyWindowHandle::from(window);
4384
4385 cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx))
4386 .unwrap();
4387 assert_eq!(anonymous_paint_count.get(), 0);
4388 assert_eq!(stateful_width.get(), px(10.));
4389
4390 let move_mouse = |cx: &mut TestAppContext, position| {
4391 cx.update_window(window, |_, window, cx| {
4392 window.simulate_mouse_move(position, cx)
4393 })
4394 .unwrap();
4395 };
4396
4397 let initial_render_count = render_count.get();
4398 move_mouse(cx, point(px(25.), px(25.)));
4399 assert_eq!(render_count.get(), initial_render_count + 1);
4400 assert_eq!(anonymous_paint_count.get(), 1);
4401 assert_eq!(stateful_width.get(), px(20.));
4402
4403 move_mouse(cx, point(px(30.), px(30.)));
4404 assert_eq!(render_count.get(), initial_render_count + 1);
4405 assert_eq!(anonymous_paint_count.get(), 1);
4406 assert_eq!(stateful_width.get(), px(20.));
4407
4408 move_mouse(cx, point(px(5.), px(5.)));
4409 assert_eq!(render_count.get(), initial_render_count + 2);
4410 assert_eq!(anonymous_paint_count.get(), 1);
4411 assert_eq!(stateful_width.get(), px(10.));
4412
4413 move_mouse(cx, point(px(10.), px(10.)));
4414 assert_eq!(render_count.get(), initial_render_count + 2);
4415 assert_eq!(anonymous_paint_count.get(), 1);
4416 assert_eq!(stateful_width.get(), px(10.));
4417 }
4418
4419 struct HoverListenerLayoutTestView {
4420 target_left: Pixels,
4421 hover_transitions: Rc<RefCell<Vec<bool>>>,
4422 }
4423
4424 impl Render for HoverListenerLayoutTestView {
4425 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4426 let hover_transitions = self.hover_transitions.clone();
4427 div().relative().size_full().child(
4428 div()
4429 .id("hover-target")
4430 .absolute()
4431 .left(self.target_left)
4432 .top_0()
4433 .size(px(20.))
4434 .on_click(|_, _, _| {})
4435 .on_hover(move |is_hovered, _, _| {
4436 hover_transitions.borrow_mut().push(*is_hovered);
4437 }),
4438 )
4439 }
4440 }
4441
4442 #[gpui::test]
4443 fn default_hover_listener_updates_when_layout_changes_under_stationary_mouse(
4444 cx: &mut TestAppContext,
4445 ) {
4446 let hover_transitions = Rc::new(RefCell::new(Vec::new()));
4447 let window = cx.add_window({
4448 let hover_transitions = hover_transitions.clone();
4449 move |_, _| HoverListenerLayoutTestView {
4450 target_left: px(40.),
4451 hover_transitions,
4452 }
4453 });
4454 let any_window = AnyWindowHandle::from(window);
4455
4456 cx.update_window(any_window, |_, window, cx| {
4457 window.draw(cx).clear(cx);
4458 window.simulate_mouse_move(point(px(10.), px(10.)), cx);
4459 })
4460 .unwrap();
4461 assert!(hover_transitions.borrow().is_empty());
4462
4463 window
4464 .update(cx, |view, _, cx| {
4465 view.target_left = px(0.);
4466 cx.notify();
4467 })
4468 .unwrap();
4469 cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx))
4470 .unwrap();
4471 assert_eq!(*hover_transitions.borrow(), [true]);
4472
4473 window
4474 .update(cx, |view, _, cx| {
4475 view.target_left = px(40.);
4476 cx.notify();
4477 })
4478 .unwrap();
4479 cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx))
4480 .unwrap();
4481 assert_eq!(*hover_transitions.borrow(), [true, false]);
4482 }
4483
4484 #[gpui::test]
4485 fn default_hover_listener_ends_after_key_press(cx: &mut TestAppContext) {
4486 let hover_transitions = Rc::new(RefCell::new(Vec::new()));
4487 let window = cx.add_window({
4488 let hover_transitions = hover_transitions.clone();
4489 move |_, _| HoverListenerLayoutTestView {
4490 target_left: px(0.),
4491 hover_transitions,
4492 }
4493 });
4494 let any_window = AnyWindowHandle::from(window);
4495
4496 cx.update_window(any_window, |_, window, cx| {
4497 window.draw(cx).clear(cx);
4498 window.simulate_mouse_move(point(px(10.), px(10.)), cx);
4499 })
4500 .unwrap();
4501 assert_eq!(*hover_transitions.borrow(), [true]);
4502
4503 key_down(cx, any_window, "a");
4504 cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx))
4505 .unwrap();
4506 assert_eq!(*hover_transitions.borrow(), [true, false]);
4507 }
4508
4509 struct HoverListenerModeLayoutTestView {
4510 target_left: Pixels,
4511 hover_transitions: Rc<RefCell<Vec<bool>>>,
4512 }
4513
4514 impl Render for HoverListenerModeLayoutTestView {
4515 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4516 let hover_transitions = self.hover_transitions.clone();
4517 div().relative().size_full().child(
4518 div()
4519 .id("hover-target")
4520 .absolute()
4521 .left(self.target_left)
4522 .top_0()
4523 .size(px(20.))
4524 .hover_listener_mode(HoverListenerMode::InputModalityIndependent)
4525 .on_hover(move |is_hovered, _, _| {
4526 hover_transitions.borrow_mut().push(*is_hovered);
4527 }),
4528 )
4529 }
4530 }
4531
4532 #[gpui::test]
4533 fn input_modality_independent_hover_listener_updates_after_key_press(cx: &mut TestAppContext) {
4534 let hover_transitions = Rc::new(RefCell::new(Vec::new()));
4535 let window = cx.add_window({
4536 let hover_transitions = hover_transitions.clone();
4537 move |_, _| HoverListenerModeLayoutTestView {
4538 target_left: px(40.),
4539 hover_transitions,
4540 }
4541 });
4542 let any_window = AnyWindowHandle::from(window);
4543 let pointer_position = point(px(10.), px(10.));
4544
4545 cx.update_window(any_window, |_, window, cx| {
4546 window.draw(cx).clear(cx);
4547 window.simulate_mouse_move(pointer_position, cx);
4548 })
4549 .unwrap();
4550 assert!(hover_transitions.borrow().is_empty());
4551
4552 key_down(cx, any_window, "a");
4553 window
4554 .update(cx, |view, _, cx| {
4555 view.target_left = px(0.);
4556 cx.notify();
4557 })
4558 .unwrap();
4559 cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx))
4560 .unwrap();
4561 assert_eq!(*hover_transitions.borrow(), [true]);
4562
4563 key_down(cx, any_window, "b");
4564 cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx))
4565 .unwrap();
4566 assert_eq!(*hover_transitions.borrow(), [true]);
4567
4568 cx.update_window(any_window, |_, window, cx| {
4569 window.simulate_mouse_move(point(px(30.), px(10.)), cx);
4570 })
4571 .unwrap();
4572 assert_eq!(*hover_transitions.borrow(), [true, false]);
4573
4574 cx.update_window(any_window, |_, window, cx| {
4575 window.simulate_mouse_move(pointer_position, cx);
4576 window.dispatch_event(
4577 MouseExitEvent {
4578 position: pointer_position,
4579 ..Default::default()
4580 }
4581 .to_platform_input(),
4582 cx,
4583 );
4584 })
4585 .unwrap();
4586 assert_eq!(*hover_transitions.borrow(), [true, false, true, false]);
4587 }
4588
4589 #[gpui::test]
4590 fn default_hover_listener_remains_hovered_during_stationary_mouse_press(
4591 cx: &mut TestAppContext,
4592 ) {
4593 let hover_transitions = Rc::new(RefCell::new(Vec::new()));
4594 let window = cx.add_window({
4595 let hover_transitions = hover_transitions.clone();
4596 move |_, _| HoverListenerLayoutTestView {
4597 target_left: px(0.),
4598 hover_transitions,
4599 }
4600 });
4601 let any_window = AnyWindowHandle::from(window);
4602 let mouse_position = point(px(10.), px(10.));
4603
4604 cx.update_window(any_window, |_, window, cx| {
4605 window.draw(cx).clear(cx);
4606 window.simulate_mouse_move(mouse_position, cx);
4607 })
4608 .unwrap();
4609 assert_eq!(*hover_transitions.borrow(), [true]);
4610
4611 cx.update_window(any_window, |_, window, cx| {
4612 window.dispatch_event(
4613 MouseDownEvent {
4614 position: mouse_position,
4615 button: MouseButton::Left,
4616 modifiers: Default::default(),
4617 click_count: 1,
4618 first_mouse: false,
4619 }
4620 .to_platform_input(),
4621 cx,
4622 );
4623 window.draw(cx).clear(cx);
4624 })
4625 .unwrap();
4626 assert_eq!(*hover_transitions.borrow(), [true]);
4627
4628 cx.update_window(any_window, |_, window, cx| {
4629 window.dispatch_event(
4630 MouseUpEvent {
4631 position: mouse_position,
4632 button: MouseButton::Left,
4633 modifiers: Default::default(),
4634 click_count: 1,
4635 }
4636 .to_platform_input(),
4637 cx,
4638 );
4639 window.draw(cx).clear(cx);
4640 })
4641 .unwrap();
4642 assert_eq!(*hover_transitions.borrow(), [true]);
4643 }
4644
4645 struct TestTooltipView;
4646
4647 impl Render for TestTooltipView {
4648 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4649 div().w(px(20.)).h(px(20.)).child("tooltip")
4650 }
4651 }
4652
4653 type CapturedActiveTooltip = Rc<RefCell<Option<Weak<RefCell<Option<ActiveTooltip>>>>>>;
4654
4655 struct TooltipCaptureElement {
4656 child: AnyElement,
4657 captured_active_tooltip: CapturedActiveTooltip,
4658 }
4659
4660 impl IntoElement for TooltipCaptureElement {
4661 type Element = Self;
4662
4663 fn into_element(self) -> Self::Element {
4664 self
4665 }
4666 }
4667
4668 impl Element for TooltipCaptureElement {
4669 type RequestLayoutState = ();
4670 type PrepaintState = ();
4671
4672 fn id(&self) -> Option<ElementId> {
4673 None
4674 }
4675
4676 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
4677 None
4678 }
4679
4680 fn request_layout(
4681 &mut self,
4682 _id: Option<&GlobalElementId>,
4683 _inspector_id: Option<&InspectorElementId>,
4684 window: &mut Window,
4685 cx: &mut App,
4686 ) -> (LayoutId, Self::RequestLayoutState) {
4687 (self.child.request_layout(window, cx), ())
4688 }
4689
4690 fn prepaint(
4691 &mut self,
4692 _id: Option<&GlobalElementId>,
4693 _inspector_id: Option<&InspectorElementId>,
4694 _bounds: Bounds<Pixels>,
4695 _request_layout: &mut Self::RequestLayoutState,
4696 window: &mut Window,
4697 cx: &mut App,
4698 ) -> Self::PrepaintState {
4699 self.child.prepaint(window, cx);
4700 }
4701
4702 fn paint(
4703 &mut self,
4704 _id: Option<&GlobalElementId>,
4705 _inspector_id: Option<&InspectorElementId>,
4706 _bounds: Bounds<Pixels>,
4707 _request_layout: &mut Self::RequestLayoutState,
4708 _prepaint: &mut Self::PrepaintState,
4709 window: &mut Window,
4710 cx: &mut App,
4711 ) {
4712 self.child.paint(window, cx);
4713 window.with_global_id("target".into(), |global_id, window| {
4714 window.with_element_state::<InteractiveElementState, _>(
4715 global_id,
4716 |state, _window| {
4717 let state = state.unwrap();
4718 *self.captured_active_tooltip.borrow_mut() =
4719 state.active_tooltip.as_ref().map(Rc::downgrade);
4720 ((), state)
4721 },
4722 )
4723 });
4724 }
4725 }
4726
4727 struct TooltipOwner {
4728 captured_active_tooltip: CapturedActiveTooltip,
4729 show_delay_override: Option<Duration>,
4730 }
4731
4732 impl Render for TooltipOwner {
4733 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4734 TooltipCaptureElement {
4735 child: div()
4736 .size_full()
4737 .child(
4738 div()
4739 .id("target")
4740 .w(px(50.))
4741 .h(px(50.))
4742 .tooltip(|_, cx| cx.new(|_| TestTooltipView).into())
4743 .when_some(self.show_delay_override, |this, delay| {
4744 this.tooltip_show_delay(delay)
4745 }),
4746 )
4747 .into_any_element(),
4748 captured_active_tooltip: self.captured_active_tooltip.clone(),
4749 }
4750 }
4751 }
4752
4753 #[test]
4754 fn scroll_handle_aligns_wide_children_to_left_edge() {
4755 let handle = ScrollHandle::new();
4756 {
4757 let mut state = handle.0.borrow_mut();
4758 state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(80.), px(20.)));
4759 state.child_bounds = vec![Bounds::new(point(px(25.), px(0.)), size(px(200.), px(20.)))];
4760 state.overflow.x = Overflow::Scroll;
4761 state.active_item = Some(ScrollActiveItem {
4762 index: 0,
4763 strategy: ScrollStrategy::default(),
4764 });
4765 }
4766
4767 handle.scroll_to_active_item();
4768
4769 assert_eq!(handle.offset().x, px(-25.));
4770 }
4771
4772 #[test]
4773 fn scroll_handle_aligns_tall_children_to_top_edge() {
4774 let handle = ScrollHandle::new();
4775 {
4776 let mut state = handle.0.borrow_mut();
4777 state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(20.), px(80.)));
4778 state.child_bounds = vec![Bounds::new(point(px(0.), px(25.)), size(px(20.), px(200.)))];
4779 state.overflow.y = Overflow::Scroll;
4780 state.active_item = Some(ScrollActiveItem {
4781 index: 0,
4782 strategy: ScrollStrategy::default(),
4783 });
4784 }
4785
4786 handle.scroll_to_active_item();
4787
4788 assert_eq!(handle.offset().y, px(-25.));
4789 }
4790
4791 fn setup_tooltip_owner_test(
4792 show_delay_override: Option<Duration>,
4793 ) -> (
4794 TestAppContext,
4795 crate::AnyWindowHandle,
4796 CapturedActiveTooltip,
4797 ) {
4798 let mut test_app = TestAppContext::single();
4799 let captured_active_tooltip: CapturedActiveTooltip = Rc::new(RefCell::new(None));
4800 let window = test_app.add_window({
4801 let captured_active_tooltip = captured_active_tooltip.clone();
4802 move |_, _| TooltipOwner {
4803 captured_active_tooltip,
4804 show_delay_override,
4805 }
4806 });
4807 let any_window = window.into();
4808
4809 test_app
4810 .update_window(any_window, |_, window, cx| {
4811 window.draw(cx).clear(cx);
4812 })
4813 .unwrap();
4814
4815 test_app
4816 .update_window(any_window, |_, window, cx| {
4817 window.dispatch_event(
4818 MouseMoveEvent {
4819 position: point(px(10.), px(10.)),
4820 modifiers: Default::default(),
4821 pressed_button: None,
4822 }
4823 .to_platform_input(),
4824 cx,
4825 );
4826 })
4827 .unwrap();
4828
4829 test_app
4830 .update_window(any_window, |_, window, cx| {
4831 window.draw(cx).clear(cx);
4832 })
4833 .unwrap();
4834
4835 (test_app, any_window, captured_active_tooltip)
4836 }
4837
4838 #[test]
4839 fn tooltip_waiting_for_show_is_released_when_its_owner_disappears() {
4840 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None);
4841
4842 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4843 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4844 assert!(matches!(
4845 active_tooltip.borrow().as_ref(),
4846 Some(ActiveTooltip::WaitingForShow { .. })
4847 ));
4848
4849 test_app
4850 .update_window(any_window, |_, window, _| {
4851 window.remove_window();
4852 })
4853 .unwrap();
4854 test_app.run_until_parked();
4855 drop(active_tooltip);
4856
4857 assert!(weak_active_tooltip.upgrade().is_none());
4858 }
4859
4860 #[test]
4861 fn tooltip_respects_custom_show_delay() {
4862 let extra_delay = Duration::from_secs(1);
4863 let show_delay_override = DEFAULT_TOOLTIP_SHOW_DELAY + extra_delay;
4864 let (mut test_app, _any_window, captured_active_tooltip) =
4865 setup_tooltip_owner_test(Some(show_delay_override));
4866
4867 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4868 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4869
4870 test_app
4871 .dispatcher
4872 .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY);
4873 test_app.run_until_parked();
4874
4875 assert!(matches!(
4876 active_tooltip.borrow().as_ref(),
4877 Some(ActiveTooltip::WaitingForShow { .. })
4878 ));
4879
4880 test_app.dispatcher.advance_clock(extra_delay);
4881 test_app.run_until_parked();
4882
4883 assert!(matches!(
4884 active_tooltip.borrow().as_ref(),
4885 Some(ActiveTooltip::Visible { .. })
4886 ));
4887 }
4888
4889 #[test]
4890 fn tooltip_is_released_when_its_owner_disappears() {
4891 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None);
4892
4893 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4894 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4895
4896 test_app
4897 .dispatcher
4898 .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY);
4899 test_app.run_until_parked();
4900
4901 assert!(matches!(
4902 active_tooltip.borrow().as_ref(),
4903 Some(ActiveTooltip::Visible { .. })
4904 ));
4905
4906 test_app
4907 .update_window(any_window, |_, window, _| {
4908 window.remove_window();
4909 })
4910 .unwrap();
4911 test_app.run_until_parked();
4912 drop(active_tooltip);
4913
4914 assert!(weak_active_tooltip.upgrade().is_none());
4915 }
4916
4917 #[test]
4918 fn tooltip_hides_after_mouse_leaves_origin() {
4919 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None);
4920
4921 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4922 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4923
4924 test_app
4925 .dispatcher
4926 .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY);
4927 test_app.run_until_parked();
4928
4929 assert!(matches!(
4930 active_tooltip.borrow().as_ref(),
4931 Some(ActiveTooltip::Visible { .. })
4932 ));
4933
4934 test_app
4935 .update_window(any_window, |_, window, cx| {
4936 window.dispatch_event(
4937 MouseMoveEvent {
4938 position: point(px(75.), px(75.)),
4939 modifiers: Default::default(),
4940 pressed_button: None,
4941 }
4942 .to_platform_input(),
4943 cx,
4944 );
4945 })
4946 .unwrap();
4947
4948 assert!(active_tooltip.borrow().is_none());
4949 }
4950
4951 struct MouseDownOutOwner {
4952 mouse_down_out_count: Rc<RefCell<usize>>,
4953 }
4954
4955 impl Render for MouseDownOutOwner {
4956 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4957 let mouse_down_out_count = self.mouse_down_out_count.clone();
4958 div()
4959 .size_full()
4960 .child(div().id("target").w(px(50.)).h(px(50.)).on_mouse_down_out(
4961 move |_, _, _| {
4962 *mouse_down_out_count.borrow_mut() += 1;
4963 },
4964 ))
4965 }
4966 }
4967
4968 #[test]
4969 fn mouse_down_out_is_suppressed_while_window_prompt_is_active() {
4970 let mut test_app = TestAppContext::single();
4971 let mouse_down_out_count = Rc::new(RefCell::new(0));
4972 let window = test_app.add_window({
4973 let mouse_down_out_count = mouse_down_out_count.clone();
4974 move |_, _| MouseDownOutOwner {
4975 mouse_down_out_count,
4976 }
4977 });
4978 let any_window: AnyWindowHandle = window.into();
4979
4980 fn dispatch_mouse_down_outside_target(
4981 test_app: &mut TestAppContext,
4982 any_window: AnyWindowHandle,
4983 ) {
4984 test_app
4985 .update_window(any_window, |_, window, cx| {
4986 window.dispatch_event(
4987 MouseDownEvent {
4988 position: point(px(75.), px(75.)),
4989 button: MouseButton::Left,
4990 modifiers: Default::default(),
4991 click_count: 1,
4992 first_mouse: false,
4993 }
4994 .to_platform_input(),
4995 cx,
4996 );
4997 })
4998 .unwrap();
4999 }
5000
5001 test_app
5002 .update_window(any_window, |_, window, cx| {
5003 window.draw(cx).clear(cx);
5004 })
5005 .unwrap();
5006
5007 dispatch_mouse_down_outside_target(&mut test_app, any_window);
5008 assert_eq!(
5009 *mouse_down_out_count.borrow(),
5010 1,
5011 "mouse down outside the element should fire mouse-down-out listeners"
5012 );
5013
5014 test_app
5015 .update_window(any_window, |_, window, cx| {
5016 cx.set_prompt_builder(crate::fallback_prompt_renderer);
5017 let _receiver =
5018 window.prompt(crate::PromptLevel::Warning, "message", None, &["Ok"], cx);
5019 assert!(window.has_active_prompt());
5020 window.draw(cx).clear(cx);
5021 })
5022 .unwrap();
5023
5024 dispatch_mouse_down_outside_target(&mut test_app, any_window);
5025 assert_eq!(
5026 *mouse_down_out_count.borrow(),
5027 1,
5028 "mouse down over an active prompt should not fire mouse-down-out listeners"
5029 );
5030 }
5031
5032 #[test]
5033 fn test_accessibility_id_builder_writes_author_id() {
5034 let mut element = div()
5035 .id("buffer-font-size")
5036 .accessibility_id("settings.buffer-font-size");
5037 let mut node = accesskit::Node::new(accesskit::Role::SpinButton);
5038
5039 element.interactivity().write_a11y_info(&mut node);
5040
5041 assert_eq!(node.author_id(), Some("settings.buffer-font-size"));
5042 }
5043
5044 #[test]
5045 fn test_write_a11y_info_string_and_numeric_properties() {
5046 let mut interactivity = Interactivity::default();
5047 interactivity.aria.author_id = Some("settings.buffer-font-size".into());
5048 interactivity.aria.label = Some("Buffer Font Size".into());
5049 interactivity.aria.value = Some("15".into());
5050 interactivity.aria.placeholder = Some("Search".into());
5051 interactivity.aria.numeric_value = Some(15.0);
5052 interactivity.aria.min_numeric_value = Some(6.0);
5053 interactivity.aria.max_numeric_value = Some(72.0);
5054 interactivity.aria.numeric_value_step = Some(1.0);
5055
5056 let mut node = accesskit::Node::new(accesskit::Role::SpinButton);
5057 interactivity.write_a11y_info(&mut node);
5058
5059 assert_eq!(node.author_id(), Some("settings.buffer-font-size"));
5060 assert_eq!(node.label(), Some("Buffer Font Size"));
5061 assert_eq!(node.value(), Some("15"));
5062 assert_eq!(node.placeholder(), Some("Search"));
5063 assert_eq!(node.numeric_value(), Some(15.0));
5064 assert_eq!(node.min_numeric_value(), Some(6.0));
5065 assert_eq!(node.max_numeric_value(), Some(72.0));
5066 assert_eq!(node.numeric_value_step(), Some(1.0));
5067 }
5068
5069 struct KeyboardActivationTest {
5072 focus_a: FocusHandle,
5073 focus_b: FocusHandle,
5074 clicks: Rc<RefCell<Vec<&'static str>>>,
5075 }
5076
5077 impl Render for KeyboardActivationTest {
5078 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
5079 let clicks_a = self.clicks.clone();
5080 let clicks_b = self.clicks.clone();
5081 div()
5082 .size_full()
5083 .child(
5084 div()
5085 .id("a")
5086 .w(px(50.))
5087 .h(px(50.))
5088 .track_focus(&self.focus_a)
5089 .on_click(move |_, _, _| clicks_a.borrow_mut().push("a")),
5090 )
5091 .child(
5092 div()
5093 .id("b")
5094 .w(px(50.))
5095 .h(px(50.))
5096 .track_focus(&self.focus_b)
5097 .on_click(move |_, _, _| clicks_b.borrow_mut().push("b")),
5098 )
5099 }
5100 }
5101
5102 fn setup_keyboard_activation_test() -> (
5103 TestAppContext,
5104 AnyWindowHandle,
5105 Rc<RefCell<Vec<&'static str>>>,
5106 FocusHandle,
5107 FocusHandle,
5108 ) {
5109 let mut cx = TestAppContext::single();
5110 let (focus_a, focus_b) = cx.update(|cx| (cx.focus_handle(), cx.focus_handle()));
5111 let clicks: Rc<RefCell<Vec<&'static str>>> = Rc::new(RefCell::new(Vec::new()));
5112 let window = cx.add_window({
5113 let focus_a = focus_a.clone();
5114 let focus_b = focus_b.clone();
5115 let clicks = clicks.clone();
5116 move |_, _| KeyboardActivationTest {
5117 focus_a,
5118 focus_b,
5119 clicks,
5120 }
5121 });
5122 (cx, window.into(), clicks, focus_a, focus_b)
5123 }
5124
5125 fn focus_and_draw(cx: &mut TestAppContext, window: AnyWindowHandle, handle: &FocusHandle) {
5128 cx.update_window(window, |_, window, cx| window.focus(handle, cx))
5129 .unwrap();
5130 cx.run_until_parked();
5131 cx.update_window(window, |_, window, cx| {
5132 window.draw(cx).clear(cx);
5133 })
5134 .unwrap();
5135 }
5136
5137 fn key_down(cx: &mut TestAppContext, window: AnyWindowHandle, key: &str) {
5138 let keystroke = Keystroke::parse(key).unwrap();
5139 cx.update_window(window, |_, window, cx| {
5140 window.dispatch_event(
5141 KeyDownEvent {
5142 keystroke,
5143 is_held: false,
5144 prefer_character_input: false,
5145 }
5146 .to_platform_input(),
5147 cx,
5148 );
5149 })
5150 .unwrap();
5151 }
5152
5153 fn key_up(cx: &mut TestAppContext, window: AnyWindowHandle, key: &str) {
5154 let keystroke = Keystroke::parse(key).unwrap();
5155 cx.update_window(window, |_, window, cx| {
5156 window.dispatch_event(KeyUpEvent { keystroke }.to_platform_input(), cx);
5157 })
5158 .unwrap();
5159 }
5160
5161 #[test]
5163 fn keyboard_activation_fires_click_on_same_element() {
5164 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
5165
5166 focus_and_draw(&mut cx, window, &focus_a);
5167 key_down(&mut cx, window, "enter");
5168 key_up(&mut cx, window, "enter");
5169
5170 assert_eq!(*clicks.borrow(), vec!["a"]);
5171 }
5172
5173 #[test]
5178 fn keyboard_activation_does_not_leak_across_focus_change() {
5179 let (mut cx, window, clicks, focus_a, focus_b) = setup_keyboard_activation_test();
5180
5181 focus_and_draw(&mut cx, window, &focus_a);
5183 key_down(&mut cx, window, "enter");
5184
5185 focus_and_draw(&mut cx, window, &focus_b);
5187 key_up(&mut cx, window, "enter");
5188
5189 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
5192 }
5193
5194 #[test]
5199 fn keyboard_activation_does_not_leak_when_focus_returns() {
5200 let (mut cx, window, clicks, focus_a, focus_b) = setup_keyboard_activation_test();
5201
5202 focus_and_draw(&mut cx, window, &focus_a);
5204 key_down(&mut cx, window, "enter");
5205
5206 focus_and_draw(&mut cx, window, &focus_b);
5209 focus_and_draw(&mut cx, window, &focus_a);
5210 key_up(&mut cx, window, "enter");
5211
5212 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
5214 }
5215
5216 #[test]
5221 fn keyboard_activation_cleared_by_intervening_key_release() {
5222 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
5223
5224 focus_and_draw(&mut cx, window, &focus_a);
5225 key_down(&mut cx, window, "escape");
5226 key_down(&mut cx, window, "space");
5227 key_up(&mut cx, window, "escape");
5228 key_up(&mut cx, window, "space");
5229
5230 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
5231 }
5232
5233 #[test]
5237 fn keyboard_activation_does_not_distinguish_space_and_enter() {
5238 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
5239
5240 focus_and_draw(&mut cx, window, &focus_a);
5241 key_down(&mut cx, window, "space");
5242 key_up(&mut cx, window, "enter");
5243
5244 assert_eq!(*clicks.borrow(), vec!["a"]);
5245 }
5246
5247 #[test]
5250 fn keyboard_activation_cleared_by_intervening_keydown() {
5251 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
5252
5253 focus_and_draw(&mut cx, window, &focus_a);
5254 key_down(&mut cx, window, "enter");
5255 key_down(&mut cx, window, "a");
5256 key_up(&mut cx, window, "enter");
5257
5258 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
5259 }
5260
5261 #[test]
5264 fn keyboard_activation_ignores_modified_keys() {
5265 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
5266
5267 focus_and_draw(&mut cx, window, &focus_a);
5268 key_down(&mut cx, window, "cmd-enter");
5269 key_up(&mut cx, window, "cmd-enter");
5270
5271 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
5272 }
5273
5274 struct TabGroupFocus {
5278 group_a: FocusHandle,
5279 item_a: FocusHandle,
5280 group_b: FocusHandle,
5281 item_b: FocusHandle,
5282 }
5283
5284 impl Render for TabGroupFocus {
5285 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
5286 fn group(container: &FocusHandle, item: &FocusHandle) -> Div {
5287 div()
5288 .track_focus(container)
5289 .tab_group()
5290 .child(div().track_focus(item))
5291 }
5292 div()
5293 .child(group(&self.group_a, &self.item_a))
5294 .child(group(&self.group_b, &self.item_b))
5295 }
5296 }
5297
5298 #[test]
5302 fn focus_next_from_tab_group_container_enters_that_group() {
5303 let mut cx = TestAppContext::single();
5304 let (group_a, item_a, group_b, item_b) = cx.update(|cx| {
5305 (
5306 cx.focus_handle(),
5307 cx.focus_handle().tab_stop(true),
5308 cx.focus_handle(),
5309 cx.focus_handle().tab_stop(true),
5310 )
5311 });
5312 let window: AnyWindowHandle = cx
5313 .add_window({
5314 let (group_a, item_a, group_b, item_b) =
5315 (group_a, item_a, group_b.clone(), item_b.clone());
5316 move |_, _| TabGroupFocus {
5317 group_a,
5318 item_a,
5319 group_b,
5320 item_b,
5321 }
5322 })
5323 .into();
5324 cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx))
5325 .unwrap();
5326
5327 let focused = cx
5329 .update_window(window, |_, window, cx| {
5330 window.focus(&group_b, cx);
5331 window.focus_next(cx);
5332 window.focused(cx).map(|handle| handle.id)
5333 })
5334 .unwrap();
5335
5336 assert_eq!(focused, Some(item_b.id));
5337 }
5338
5339 #[gpui::test]
5340 fn test_fractional_padding_does_not_make_a_fitting_container_scrollable(
5341 cx: &mut TestAppContext,
5342 ) {
5343 struct PaddedContainer {
5344 scroll_handle: ScrollHandle,
5345 }
5346
5347 impl Render for PaddedContainer {
5348 fn render(
5349 &mut self,
5350 _window: &mut Window,
5351 _cx: &mut Context<Self>,
5352 ) -> impl IntoElement {
5353 div().size_full().child(
5356 div()
5357 .id("container")
5358 .h(px(50.))
5359 .w(px(100.))
5360 .py(px(4.25))
5361 .overflow_y_scroll()
5362 .track_scroll(&self.scroll_handle)
5363 .child(div().w_full().h(px(42.))),
5364 )
5365 }
5366 }
5367
5368 let scroll_handle = ScrollHandle::new();
5369 let window: AnyWindowHandle = cx
5370 .add_window({
5371 let scroll_handle = scroll_handle.clone();
5372 move |_, _| PaddedContainer { scroll_handle }
5373 })
5374 .into();
5375 cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx))
5376 .unwrap();
5377
5378 assert_eq!(scroll_handle.max_offset().y, px(0.));
5379 }
5380
5381 struct ContentSizedGrid;
5382
5383 impl Render for ContentSizedGrid {
5384 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
5385 let widths = [px(100.), px(200.), px(50.)];
5386 div().size_full().child(
5387 div()
5388 .w_full()
5389 .grid()
5390 .grid_cols_max_content(widths.len() as u16)
5391 .children(widths.into_iter().enumerate().map(|(index, width)| {
5392 div()
5393 .debug_selector(move || format!("cell-{index}"))
5394 .w(width)
5395 .h(px(10.))
5396 })),
5397 )
5398 }
5399 }
5400
5401 #[gpui::test]
5402 fn grid_cols_max_content_sizes_columns_to_their_content(cx: &mut TestAppContext) {
5403 let window = cx.add_window(|_, _| ContentSizedGrid);
5404 cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx))
5405 .unwrap();
5406
5407 let mut bounds = |selector: &'static str| {
5408 cx.update_window(window.into(), |_, window, _| {
5409 window.rendered_frame.debug_bounds.get(selector).copied()
5410 })
5411 .unwrap()
5412 .unwrap_or_else(|| panic!("{selector} was not rendered"))
5413 };
5414
5415 assert_eq!(bounds("cell-0").origin.x, px(0.));
5416 assert_eq!(bounds("cell-1").origin.x, px(100.));
5417 assert_eq!(bounds("cell-2").origin.x, px(300.));
5418 }
5419}