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 stacksafe::{StackSafe, stacksafe};
34use std::{
35 any::{Any, TypeId},
36 cell::RefCell,
37 cmp::Ordering,
38 fmt::Debug,
39 marker::PhantomData,
40 mem,
41 rc::Rc,
42 sync::Arc,
43 time::Duration,
44};
45
46use super::ImageCacheProvider;
47
48const DRAG_THRESHOLD: f64 = 2.;
49const DEFAULT_TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);
50const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500);
51
52pub struct GroupStyle {
54 pub group: SharedString,
56
57 pub style: Box<StyleRefinement>,
60}
61
62pub struct DragMoveEvent<T> {
64 pub event: MouseMoveEvent,
66
67 pub bounds: Bounds<Pixels>,
69 drag: PhantomData<T>,
70 dragged_item: Arc<dyn Any>,
71}
72
73impl<T: 'static> DragMoveEvent<T> {
74 pub fn drag<'b>(&self, cx: &'b App) -> &'b T {
76 cx.active_drag
77 .as_ref()
78 .and_then(|drag| drag.value.downcast_ref::<T>())
79 .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
80 }
81
82 pub fn dragged_item(&self) -> &dyn Any {
84 self.dragged_item.as_ref()
85 }
86}
87
88impl Interactivity {
89 #[cfg(any(feature = "inspector", debug_assertions))]
91 #[track_caller]
92 pub fn new() -> Interactivity {
93 Interactivity {
94 source_location: Some(core::panic::Location::caller()),
95 ..Default::default()
96 }
97 }
98
99 #[cfg(not(any(feature = "inspector", debug_assertions)))]
101 pub fn new() -> Interactivity {
102 Interactivity::default()
103 }
104
105 pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
107 #[cfg(any(feature = "inspector", debug_assertions))]
108 {
109 self.source_location
110 }
111
112 #[cfg(not(any(feature = "inspector", debug_assertions)))]
113 {
114 None
115 }
116 }
117
118 pub fn on_mouse_down(
123 &mut self,
124 button: MouseButton,
125 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
126 ) {
127 self.mouse_down_listeners
128 .push(Box::new(move |event, phase, hitbox, window, cx| {
129 if phase == DispatchPhase::Bubble
130 && event.button == button
131 && hitbox.is_hovered(window)
132 {
133 (listener)(event, window, cx)
134 }
135 }));
136 }
137
138 pub fn capture_any_mouse_down(
143 &mut self,
144 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
145 ) {
146 self.mouse_down_listeners
147 .push(Box::new(move |event, phase, hitbox, window, cx| {
148 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
149 (listener)(event, window, cx)
150 }
151 }));
152 }
153
154 pub fn on_any_mouse_down(
159 &mut self,
160 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
161 ) {
162 self.mouse_down_listeners
163 .push(Box::new(move |event, phase, hitbox, window, cx| {
164 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
165 (listener)(event, window, cx)
166 }
167 }));
168 }
169
170 pub fn on_mouse_pressure(
175 &mut self,
176 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
177 ) {
178 self.mouse_pressure_listeners
179 .push(Box::new(move |event, phase, hitbox, window, cx| {
180 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
181 (listener)(event, window, cx)
182 }
183 }));
184 }
185
186 pub fn capture_mouse_pressure(
191 &mut self,
192 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
193 ) {
194 self.mouse_pressure_listeners
195 .push(Box::new(move |event, phase, hitbox, window, cx| {
196 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
197 (listener)(event, window, cx)
198 }
199 }));
200 }
201
202 pub fn on_mouse_up(
207 &mut self,
208 button: MouseButton,
209 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
210 ) {
211 self.mouse_up_listeners
212 .push(Box::new(move |event, phase, hitbox, window, cx| {
213 if phase == DispatchPhase::Bubble
214 && event.button == button
215 && hitbox.is_hovered(window)
216 {
217 (listener)(event, window, cx)
218 }
219 }));
220 }
221
222 pub fn capture_any_mouse_up(
227 &mut self,
228 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
229 ) {
230 self.mouse_up_listeners
231 .push(Box::new(move |event, phase, hitbox, window, cx| {
232 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
233 (listener)(event, window, cx)
234 }
235 }));
236 }
237
238 pub fn on_any_mouse_up(
243 &mut self,
244 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
245 ) {
246 self.mouse_up_listeners
247 .push(Box::new(move |event, phase, hitbox, window, cx| {
248 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
249 (listener)(event, window, cx)
250 }
251 }));
252 }
253
254 pub fn on_mouse_down_out(
260 &mut self,
261 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
262 ) {
263 self.mouse_down_listeners
264 .push(Box::new(move |event, phase, hitbox, window, cx| {
265 if phase == DispatchPhase::Capture
266 && !window.has_active_prompt()
267 && !hitbox.contains(&window.mouse_position())
268 {
269 (listener)(event, window, cx)
270 }
271 }));
272 }
273
274 pub fn on_mouse_down_all(
281 &mut self,
282 listener: impl Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static,
283 ) {
284 self.mouse_down_listeners.push(Box::new(listener));
285 }
286
287 pub fn on_mouse_up_out(
293 &mut self,
294 button: MouseButton,
295 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
296 ) {
297 self.mouse_up_listeners
298 .push(Box::new(move |event, phase, hitbox, window, cx| {
299 if phase == DispatchPhase::Capture
300 && event.button == button
301 && !hitbox.is_hovered(window)
302 {
303 (listener)(event, window, cx);
304 }
305 }));
306 }
307
308 pub fn on_mouse_up_all(
315 &mut self,
316 listener: impl Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static,
317 ) {
318 self.mouse_up_listeners.push(Box::new(listener));
319 }
320
321 pub fn on_mouse_move(
326 &mut self,
327 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
328 ) {
329 self.mouse_move_listeners
330 .push(Box::new(move |event, phase, hitbox, window, cx| {
331 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
332 (listener)(event, window, cx);
333 }
334 }));
335 }
336
337 pub fn on_mouse_exit(
342 &mut self,
343 listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static,
344 ) {
345 self.mouse_exit_listeners
346 .push(Box::new(move |event, phase, hitbox, window, cx| {
347 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
348 (listener)(event, window, cx);
349 }
350 }));
351 }
352
353 pub fn on_drag_move<T>(
361 &mut self,
362 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
363 ) where
364 T: 'static,
365 {
366 self.mouse_move_listeners
367 .push(Box::new(move |event, phase, hitbox, window, cx| {
368 if phase == DispatchPhase::Capture
369 && let Some(drag) = &cx.active_drag
370 && drag.value.as_ref().type_id() == TypeId::of::<T>()
371 {
372 (listener)(
373 &DragMoveEvent {
374 event: event.clone(),
375 bounds: hitbox.bounds,
376 drag: PhantomData,
377 dragged_item: Arc::clone(&drag.value),
378 },
379 window,
380 cx,
381 );
382 }
383 }));
384 }
385
386 pub fn on_scroll_wheel(
391 &mut self,
392 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
393 ) {
394 self.scroll_wheel_listeners
395 .push(Box::new(move |event, phase, hitbox, window, cx| {
396 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
397 (listener)(event, window, cx);
398 }
399 }));
400 }
401
402 pub fn on_pinch(&mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) {
406 self.pinch_listeners
407 .push(Box::new(move |event, phase, hitbox, window, cx| {
408 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
409 (listener)(event, window, cx);
410 }
411 }));
412 }
413
414 pub fn capture_pinch(
418 &mut self,
419 listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
420 ) {
421 self.pinch_listeners
422 .push(Box::new(move |event, phase, _hitbox, window, cx| {
423 if phase == DispatchPhase::Capture {
424 (listener)(event, window, cx);
425 } else {
426 cx.propagate();
427 }
428 }));
429 }
430
431 pub fn capture_action<A: Action>(
436 &mut self,
437 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
438 ) {
439 self.action_listeners.push((
440 TypeId::of::<A>(),
441 Box::new(move |action, phase, window, cx| {
442 let action = action.downcast_ref().unwrap();
443 if phase == DispatchPhase::Capture {
444 (listener)(action, window, cx)
445 } else {
446 cx.propagate();
447 }
448 }),
449 ));
450 }
451
452 #[track_caller]
457 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {
458 self.action_listeners.push((
459 TypeId::of::<A>(),
460 Box::new(move |action, phase, window, cx| {
461 let action = action.downcast_ref().unwrap();
462 if phase == DispatchPhase::Bubble {
463 (listener)(action, window, cx)
464 }
465 }),
466 ));
467 }
468
469 pub fn on_boxed_action(
476 &mut self,
477 action: &dyn Action,
478 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
479 ) {
480 let action = action.boxed_clone();
481 self.action_listeners.push((
482 (*action).type_id(),
483 Box::new(move |_, phase, window, cx| {
484 if phase == DispatchPhase::Bubble {
485 (listener)(&*action, window, cx)
486 }
487 }),
488 ));
489 }
490
491 pub fn on_key_down(
496 &mut self,
497 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
498 ) {
499 self.key_down_listeners
500 .push(Box::new(move |event, phase, window, cx| {
501 if phase == DispatchPhase::Bubble {
502 (listener)(event, window, cx)
503 }
504 }));
505 }
506
507 pub fn capture_key_down(
512 &mut self,
513 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
514 ) {
515 self.key_down_listeners
516 .push(Box::new(move |event, phase, window, cx| {
517 if phase == DispatchPhase::Capture {
518 listener(event, window, cx)
519 }
520 }));
521 }
522
523 pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {
528 self.key_up_listeners
529 .push(Box::new(move |event, phase, window, cx| {
530 if phase == DispatchPhase::Bubble {
531 listener(event, window, cx)
532 }
533 }));
534 }
535
536 pub fn capture_key_up(
541 &mut self,
542 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
543 ) {
544 self.key_up_listeners
545 .push(Box::new(move |event, phase, window, cx| {
546 if phase == DispatchPhase::Capture {
547 listener(event, window, cx)
548 }
549 }));
550 }
551
552 pub fn on_modifiers_changed(
557 &mut self,
558 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
559 ) {
560 self.modifiers_changed_listeners
561 .push(Box::new(move |event, window, cx| {
562 listener(event, window, cx)
563 }));
564 }
565
566 pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {
571 self.drop_listeners.push((
572 TypeId::of::<T>(),
573 Box::new(move |dragged_value, window, cx| {
574 listener(dragged_value.downcast_ref().unwrap(), window, cx);
575 }),
576 ));
577 }
578
579 pub fn can_drop(
582 &mut self,
583 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
584 ) {
585 self.can_drop_predicate = Some(Box::new(predicate));
586 }
587
588 pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
593 where
594 Self: Sized,
595 {
596 self.click_listeners.push(Rc::new(move |event, window, cx| {
597 listener(event, window, cx)
598 }));
599 }
600
601 pub fn on_aux_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
606 where
607 Self: Sized,
608 {
609 self.aux_click_listeners
610 .push(Rc::new(move |event, window, cx| {
611 listener(event, window, cx)
612 }));
613 }
614
615 pub fn on_drag<T, W>(
622 &mut self,
623 value: T,
624 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
625 ) where
626 Self: Sized,
627 T: 'static,
628 W: 'static + Render,
629 {
630 debug_assert!(
631 self.drag_listener.is_none(),
632 "calling on_drag more than once on the same element is not supported"
633 );
634 self.drag_listener = Some(DragListener {
635 value: Arc::new(value),
636 render: Box::new(move |value, offset, window, cx| {
637 constructor(value.downcast_ref().unwrap(), offset, window, cx).into()
638 }),
639 external_payload: None,
640 });
641 }
642
643 pub fn external_drag_payload<T>(
648 &mut self,
649 resolver: impl Fn(&T, &mut Window, &mut App) -> Option<ExternalDragPayload> + 'static,
650 ) where
651 Self: Sized,
652 T: 'static,
653 {
654 let Some(drag_listener) = self.drag_listener.as_mut() else {
655 debug_assert!(false, "external_drag_payload must be called after on_drag");
656 return;
657 };
658 debug_assert!(
659 drag_listener.value.as_ref().type_id() == TypeId::of::<T>(),
660 "external_drag_payload must use the same dragged value type as on_drag"
661 );
662 debug_assert!(
663 drag_listener.external_payload.is_none(),
664 "calling external_drag_payload more than once on the same element is not supported"
665 );
666 drag_listener.external_payload = Some(Box::new(move |value, window, cx| {
667 resolver(value.downcast_ref::<T>()?, window, cx)
668 }));
669 }
670
671 pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static)
677 where
678 Self: Sized,
679 {
680 debug_assert!(
681 self.hover_listener.is_none(),
682 "calling on_hover more than once on the same element is not supported"
683 );
684 self.hover_listener = Some(Box::new(listener));
685 }
686
687 pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static)
690 where
691 Self: Sized,
692 {
693 debug_assert!(
694 self.tooltip_builder.is_none(),
695 "calling tooltip more than once on the same element is not supported"
696 );
697 self.tooltip_builder = Some(TooltipBuilder {
698 build: Rc::new(build_tooltip),
699 hoverable: false,
700 });
701 }
702
703 pub fn hoverable_tooltip(
707 &mut self,
708 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
709 ) where
710 Self: Sized,
711 {
712 debug_assert!(
713 self.tooltip_builder.is_none(),
714 "calling tooltip more than once on the same element is not supported"
715 );
716 self.tooltip_builder = Some(TooltipBuilder {
717 build: Rc::new(build_tooltip),
718 hoverable: true,
719 });
720 }
721
722 pub fn tooltip_show_delay(&mut self, delay: Duration) {
725 self.tooltip_show_delay = Some(delay);
726 }
727
728 pub fn occlude_mouse(&mut self) {
733 self.hitbox_behavior = HitboxBehavior::BlockMouse;
734 }
735
736 pub fn window_control_area(&mut self, area: WindowControlArea) {
739 self.window_control = Some(area);
740 }
741
742 pub fn block_mouse_except_scroll(&mut self) {
747 self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll;
748 }
749
750 fn has_pinch_listeners(&self) -> bool {
751 !self.pinch_listeners.is_empty()
752 }
753}
754
755pub trait InteractiveElement: Sized {
758 fn interactivity(&mut self) -> &mut Interactivity;
760
761 fn group(mut self, group: impl Into<SharedString>) -> Self {
763 self.interactivity().group = Some(group.into());
764 self
765 }
766
767 fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
769 self.interactivity().element_id = Some(id.into());
770
771 Stateful { element: self }
772 }
773
774 fn track_focus(mut self, focus_handle: &FocusHandle) -> Self {
778 self.interactivity().focusable = true;
779 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
780 self
781 }
782
783 fn tab_stop(mut self, tab_stop: bool) -> Self {
790 self.interactivity().tab_stop = tab_stop;
791 self
792 }
793
794 fn tab_index(mut self, index: isize) -> Self {
799 self.interactivity().focusable = true;
800 self.interactivity().tab_index = Some(index);
801 self.interactivity().tab_stop = true;
802 self
803 }
804
805 fn tab_group(mut self) -> Self {
810 self.interactivity().tab_group = true;
811 if self.interactivity().tab_index.is_none() {
812 self.interactivity().tab_index = Some(0);
813 }
814 self
815 }
816
817 fn key_context<C, E>(mut self, key_context: C) -> Self
820 where
821 C: TryInto<KeyContext, Error = E>,
822 E: std::fmt::Display,
823 {
824 if let Some(key_context) = key_context.try_into().log_err() {
825 self.interactivity().key_context = Some(key_context);
826 }
827 self
828 }
829
830 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
832 debug_assert!(
833 self.interactivity().hover_style.is_none(),
834 "hover style already set"
835 );
836 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
837 self
838 }
839
840 fn group_hover(
842 mut self,
843 group_name: impl Into<SharedString>,
844 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
845 ) -> Self {
846 self.interactivity().group_hover_style = Some(GroupStyle {
847 group: group_name.into(),
848 style: Box::new(f(StyleRefinement::default())),
849 });
850 self
851 }
852
853 fn on_mouse_down(
858 mut self,
859 button: MouseButton,
860 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
861 ) -> Self {
862 self.interactivity().on_mouse_down(button, listener);
863 self
864 }
865
866 #[cfg(any(test, feature = "test-support"))]
867 fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
871 self.interactivity().debug_selector = Some(f());
872 self
873 }
874
875 #[cfg(not(any(test, feature = "test-support")))]
876 #[inline]
880 fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
881 self
882 }
883
884 fn capture_any_mouse_down(
889 mut self,
890 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
891 ) -> Self {
892 self.interactivity().capture_any_mouse_down(listener);
893 self
894 }
895
896 fn on_any_mouse_down(
901 mut self,
902 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
903 ) -> Self {
904 self.interactivity().on_any_mouse_down(listener);
905 self
906 }
907
908 fn on_mouse_up(
913 mut self,
914 button: MouseButton,
915 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
916 ) -> Self {
917 self.interactivity().on_mouse_up(button, listener);
918 self
919 }
920
921 fn capture_any_mouse_up(
926 mut self,
927 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
928 ) -> Self {
929 self.interactivity().capture_any_mouse_up(listener);
930 self
931 }
932
933 fn on_mouse_pressure(
938 mut self,
939 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
940 ) -> Self {
941 self.interactivity().on_mouse_pressure(listener);
942 self
943 }
944
945 fn capture_mouse_pressure(
950 mut self,
951 listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,
952 ) -> Self {
953 self.interactivity().capture_mouse_pressure(listener);
954 self
955 }
956
957 fn on_mouse_down_out(
963 mut self,
964 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
965 ) -> Self {
966 self.interactivity().on_mouse_down_out(listener);
967 self
968 }
969
970 fn on_mouse_down_all(
977 mut self,
978 listener: impl Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static,
979 ) -> Self {
980 self.interactivity().on_mouse_down_all(listener);
981 self
982 }
983
984 fn on_mouse_up_out(
990 mut self,
991 button: MouseButton,
992 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
993 ) -> Self {
994 self.interactivity().on_mouse_up_out(button, listener);
995 self
996 }
997
998 fn on_mouse_up_all(
1005 mut self,
1006 listener: impl Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static,
1007 ) -> Self {
1008 self.interactivity().on_mouse_up_all(listener);
1009 self
1010 }
1011
1012 fn on_mouse_move(
1017 mut self,
1018 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
1019 ) -> Self {
1020 self.interactivity().on_mouse_move(listener);
1021 self
1022 }
1023
1024 fn on_mouse_exit(
1029 mut self,
1030 listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static,
1031 ) -> Self {
1032 self.interactivity().on_mouse_exit(listener);
1033 self
1034 }
1035
1036 fn on_drag_move<T: 'static>(
1044 mut self,
1045 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
1046 ) -> Self {
1047 self.interactivity().on_drag_move(listener);
1048 self
1049 }
1050
1051 fn on_scroll_wheel(
1056 mut self,
1057 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
1058 ) -> Self {
1059 self.interactivity().on_scroll_wheel(listener);
1060 self
1061 }
1062
1063 fn on_pinch(mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) -> Self {
1068 self.interactivity().on_pinch(listener);
1069 self
1070 }
1071
1072 fn capture_pinch(
1077 mut self,
1078 listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
1079 ) -> Self {
1080 self.interactivity().capture_pinch(listener);
1081 self
1082 }
1083 fn capture_action<A: Action>(
1088 mut self,
1089 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
1090 ) -> Self {
1091 self.interactivity().capture_action(listener);
1092 self
1093 }
1094
1095 #[track_caller]
1100 fn on_action<A: Action>(
1101 mut self,
1102 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
1103 ) -> Self {
1104 self.interactivity().on_action(listener);
1105 self
1106 }
1107
1108 fn on_boxed_action(
1115 mut self,
1116 action: &dyn Action,
1117 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
1118 ) -> Self {
1119 self.interactivity().on_boxed_action(action, listener);
1120 self
1121 }
1122
1123 fn on_key_down(
1128 mut self,
1129 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
1130 ) -> Self {
1131 self.interactivity().on_key_down(listener);
1132 self
1133 }
1134
1135 fn capture_key_down(
1140 mut self,
1141 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
1142 ) -> Self {
1143 self.interactivity().capture_key_down(listener);
1144 self
1145 }
1146
1147 fn on_key_up(
1152 mut self,
1153 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1154 ) -> Self {
1155 self.interactivity().on_key_up(listener);
1156 self
1157 }
1158
1159 fn capture_key_up(
1164 mut self,
1165 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1166 ) -> Self {
1167 self.interactivity().capture_key_up(listener);
1168 self
1169 }
1170
1171 fn on_modifiers_changed(
1176 mut self,
1177 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
1178 ) -> Self {
1179 self.interactivity().on_modifiers_changed(listener);
1180 self
1181 }
1182
1183 fn drag_over<S: 'static>(
1185 mut self,
1186 f: impl 'static + Fn(StyleRefinement, &S, &mut Window, &mut App) -> StyleRefinement,
1187 ) -> Self {
1188 self.interactivity().drag_over_styles.push((
1189 TypeId::of::<S>(),
1190 Box::new(move |currently_dragged: &dyn Any, window, cx| {
1191 f(
1192 StyleRefinement::default(),
1193 currently_dragged.downcast_ref::<S>().unwrap(),
1194 window,
1195 cx,
1196 )
1197 }),
1198 ));
1199 self
1200 }
1201
1202 fn group_drag_over<S: 'static>(
1204 mut self,
1205 group_name: impl Into<SharedString>,
1206 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1207 ) -> Self {
1208 self.interactivity().group_drag_over_styles.push((
1209 TypeId::of::<S>(),
1210 GroupStyle {
1211 group: group_name.into(),
1212 style: Box::new(f(StyleRefinement::default())),
1213 },
1214 ));
1215 self
1216 }
1217
1218 fn on_drop<T: 'static>(
1223 mut self,
1224 listener: impl Fn(&T, &mut Window, &mut App) + 'static,
1225 ) -> Self {
1226 self.interactivity().on_drop(listener);
1227 self
1228 }
1229
1230 fn can_drop(
1233 mut self,
1234 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
1235 ) -> Self {
1236 self.interactivity().can_drop(predicate);
1237 self
1238 }
1239
1240 fn occlude(mut self) -> Self {
1244 self.interactivity().occlude_mouse();
1245 self
1246 }
1247
1248 fn window_control_area(mut self, area: WindowControlArea) -> Self {
1251 self.interactivity().window_control_area(area);
1252 self
1253 }
1254
1255 fn block_mouse_except_scroll(mut self) -> Self {
1260 self.interactivity().block_mouse_except_scroll();
1261 self
1262 }
1263
1264 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1267 where
1268 Self: Sized,
1269 {
1270 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
1271 self
1272 }
1273
1274 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1277 where
1278 Self: Sized,
1279 {
1280 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
1281 self
1282 }
1283
1284 fn focus_visible(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1289 where
1290 Self: Sized,
1291 {
1292 self.interactivity().focus_visible_style = Some(Box::new(f(StyleRefinement::default())));
1293 self
1294 }
1295}
1296
1297pub trait StatefulInteractiveElement: InteractiveElement {
1300 fn role(mut self, role: accesskit::Role) -> Self {
1304 debug_assert!(
1305 role != accesskit::Role::GenericContainer,
1306 "GenericContainer is filtered out of the a11y tree and has no effect"
1307 );
1308 self.interactivity().override_role = Some(role);
1309 self
1310 }
1311
1312 fn aria_label(mut self, label: impl Into<SharedString>) -> Self {
1314 self.interactivity().aria.label = Some(label.into());
1315 self
1316 }
1317
1318 fn aria_description(mut self, description: impl Into<SharedString>) -> Self {
1323 self.interactivity().aria.description = Some(description.into());
1324 self
1325 }
1326
1327 fn aria_keyshortcuts(mut self, keyshortcuts: impl Into<SharedString>) -> Self {
1333 self.interactivity().aria.keyshortcuts = Some(keyshortcuts.into());
1334 self
1335 }
1336
1337 fn aria_active_descendant(mut self) -> Self {
1353 self.interactivity().report_active_descendant_focus = true;
1354 self
1355 }
1356
1357 fn a11y_synthetic_children(
1367 mut self,
1368 f: impl FnOnce(&mut crate::A11ySubtreeBuilder) + 'static,
1369 ) -> Self {
1370 self.interactivity().a11y_synthetic_children = Some(Box::new(f));
1371 self
1372 }
1373
1374 fn aria_selected(mut self, selected: bool) -> Self {
1376 self.interactivity().aria.selected = Some(selected);
1377 self
1378 }
1379
1380 fn aria_expanded(mut self, expanded: bool) -> Self {
1382 self.interactivity().aria.expanded = Some(expanded);
1383 self
1384 }
1385
1386 fn aria_toggled(mut self, toggled: accesskit::Toggled) -> Self {
1388 self.interactivity().aria.toggled = Some(toggled);
1389 self
1390 }
1391
1392 fn aria_numeric_value(mut self, value: f64) -> Self {
1394 self.interactivity().aria.numeric_value = Some(value);
1395 self
1396 }
1397
1398 fn aria_numeric_value_step(mut self, step: f64) -> Self {
1401 self.interactivity().aria.numeric_value_step = Some(step);
1402 self
1403 }
1404
1405 fn aria_value(mut self, value: impl Into<SharedString>) -> Self {
1408 self.interactivity().aria.value = Some(value.into());
1409 self
1410 }
1411
1412 fn aria_placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
1415 self.interactivity().aria.placeholder = Some(placeholder.into());
1416 self
1417 }
1418
1419 fn aria_min_numeric_value(mut self, value: f64) -> Self {
1421 self.interactivity().aria.min_numeric_value = Some(value);
1422 self
1423 }
1424
1425 fn aria_max_numeric_value(mut self, value: f64) -> Self {
1427 self.interactivity().aria.max_numeric_value = Some(value);
1428 self
1429 }
1430
1431 fn aria_orientation(mut self, orientation: accesskit::Orientation) -> Self {
1433 self.interactivity().aria.orientation = Some(orientation);
1434 self
1435 }
1436
1437 fn aria_level(mut self, level: usize) -> Self {
1439 self.interactivity().aria.level = Some(level);
1440 self
1441 }
1442
1443 fn aria_position_in_set(mut self, position: usize) -> Self {
1445 self.interactivity().aria.position_in_set = Some(position);
1446 self
1447 }
1448
1449 fn aria_size_of_set(mut self, size: usize) -> Self {
1451 self.interactivity().aria.size_of_set = Some(size);
1452 self
1453 }
1454
1455 fn aria_row_index(mut self, index: usize) -> Self {
1457 self.interactivity().aria.row_index = Some(index);
1458 self
1459 }
1460
1461 fn aria_column_index(mut self, index: usize) -> Self {
1463 self.interactivity().aria.column_index = Some(index);
1464 self
1465 }
1466
1467 fn aria_row_count(mut self, count: usize) -> Self {
1469 self.interactivity().aria.row_count = Some(count);
1470 self
1471 }
1472
1473 fn aria_column_count(mut self, count: usize) -> Self {
1475 self.interactivity().aria.column_count = Some(count);
1476 self
1477 }
1478
1479 fn on_a11y_action(
1484 mut self,
1485 action: accesskit::Action,
1486 listener: impl FnMut(Option<&accesskit::ActionData>, &mut crate::Window, &mut crate::App)
1487 + 'static,
1488 ) -> Self {
1489 self.interactivity()
1490 .a11y_action_listeners
1491 .push((action, Box::new(listener)));
1492 self
1493 }
1494
1495 fn focusable(mut self) -> Self {
1497 self.interactivity().focusable = true;
1498 self
1499 }
1500
1501 fn overflow_scroll(mut self) -> Self {
1503 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1504 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1505 self
1506 }
1507
1508 fn overflow_x_scroll(mut self) -> Self {
1510 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1511 self
1512 }
1513
1514 fn overflow_y_scroll(mut self) -> Self {
1516 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1517 self
1518 }
1519
1520 fn restrict_scroll_to_axis(mut self) -> Self {
1524 self.interactivity().base_style.restrict_scroll_to_axis = Some(true);
1525 self
1526 }
1527
1528 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
1530 self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone());
1531 self
1532 }
1533
1534 fn anchor_scroll(mut self, scroll_anchor: Option<ScrollAnchor>) -> Self {
1536 self.interactivity().scroll_anchor = scroll_anchor;
1537 self
1538 }
1539
1540 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1542 where
1543 Self: Sized,
1544 {
1545 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
1546 self
1547 }
1548
1549 fn group_active(
1551 mut self,
1552 group_name: impl Into<SharedString>,
1553 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1554 ) -> Self
1555 where
1556 Self: Sized,
1557 {
1558 self.interactivity().group_active_style = Some(GroupStyle {
1559 group: group_name.into(),
1560 style: Box::new(f(StyleRefinement::default())),
1561 });
1562 self
1563 }
1564
1565 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self
1570 where
1571 Self: Sized,
1572 {
1573 self.interactivity().on_click(listener);
1574 self
1575 }
1576
1577 fn on_aux_click(
1582 mut self,
1583 listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
1584 ) -> Self
1585 where
1586 Self: Sized,
1587 {
1588 self.interactivity().on_aux_click(listener);
1589 self
1590 }
1591
1592 fn on_drag<T, W>(
1600 mut self,
1601 value: T,
1602 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
1603 ) -> Self
1604 where
1605 Self: Sized,
1606 T: 'static,
1607 W: 'static + Render,
1608 {
1609 self.interactivity().on_drag(value, constructor);
1610 self
1611 }
1612
1613 fn external_drag_payload<T>(
1619 mut self,
1620 resolver: impl Fn(&T, &mut Window, &mut App) -> Option<ExternalDragPayload> + 'static,
1621 ) -> Self
1622 where
1623 Self: Sized,
1624 T: 'static,
1625 {
1626 self.interactivity().external_drag_payload(resolver);
1627 self
1628 }
1629
1630 fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self
1636 where
1637 Self: Sized,
1638 {
1639 self.interactivity().on_hover(listener);
1640 self
1641 }
1642
1643 fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self
1646 where
1647 Self: Sized,
1648 {
1649 self.interactivity().tooltip(build_tooltip);
1650 self
1651 }
1652
1653 fn hoverable_tooltip(
1657 mut self,
1658 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
1659 ) -> Self
1660 where
1661 Self: Sized,
1662 {
1663 self.interactivity().hoverable_tooltip(build_tooltip);
1664 self
1665 }
1666
1667 fn tooltip_show_delay(mut self, delay: Duration) -> Self
1670 where
1671 Self: Sized,
1672 {
1673 self.interactivity().tooltip_show_delay(delay);
1674 self
1675 }
1676}
1677
1678pub(crate) type MouseDownListener =
1679 Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1680pub(crate) type MouseUpListener =
1681 Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1682pub(crate) type MousePressureListener =
1683 Box<dyn Fn(&MousePressureEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1684pub(crate) type MouseMoveListener =
1685 Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1686pub(crate) type MouseExitListener =
1687 Box<dyn Fn(&MouseExitEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1688
1689pub(crate) type ScrollWheelListener =
1690 Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1691
1692pub(crate) type PinchListener =
1693 Box<dyn Fn(&PinchEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1694
1695pub(crate) type ClickListener = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
1696
1697pub(crate) struct DragListener {
1698 value: Arc<dyn Any>,
1699 render: Box<dyn Fn(&dyn Any, Point<Pixels>, &mut Window, &mut App) -> AnyView + 'static>,
1700 external_payload: Option<ExternalDragPayloadResolver>,
1701}
1702
1703type ExternalDragPayloadResolver =
1704 Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> Option<ExternalDragPayload> + 'static>;
1705
1706type DropListener = Box<dyn Fn(&dyn Any, &mut Window, &mut App) + 'static>;
1707
1708type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>;
1709
1710pub(crate) struct TooltipBuilder {
1711 build: Rc<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>,
1712 hoverable: bool,
1713}
1714
1715pub(crate) type KeyDownListener =
1716 Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1717
1718pub(crate) type KeyUpListener =
1719 Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1720
1721pub(crate) type ModifiersChangedListener =
1722 Box<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static>;
1723
1724pub(crate) type ActionListener =
1725 Box<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
1726
1727#[track_caller]
1729pub fn div() -> Div {
1730 Div {
1731 interactivity: Interactivity::new(),
1732 children: SmallVec::default(),
1733 prepaint_listener: None,
1734 image_cache: None,
1735 prepaint_order_fn: None,
1736 }
1737}
1738
1739pub struct Div {
1741 interactivity: Interactivity,
1742 children: SmallVec<[StackSafe<AnyElement>; 2]>,
1743 prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static>>,
1744 image_cache: Option<Box<dyn ImageCacheProvider>>,
1745 prepaint_order_fn: Option<Box<dyn Fn(&mut Window, &mut App) -> SmallVec<[usize; 8]>>>,
1746}
1747
1748impl Div {
1749 pub fn on_children_prepainted(
1752 mut self,
1753 listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
1754 ) -> Self {
1755 self.prepaint_listener = Some(Box::new(listener));
1756 self
1757 }
1758
1759 pub fn image_cache(mut self, cache: impl ImageCacheProvider) -> Self {
1761 self.image_cache = Some(Box::new(cache));
1762 self
1763 }
1764
1765 pub fn with_dynamic_prepaint_order(
1774 mut self,
1775 order_fn: impl Fn(&mut Window, &mut App) -> SmallVec<[usize; 8]> + 'static,
1776 ) -> Self {
1777 self.prepaint_order_fn = Some(Box::new(order_fn));
1778 self
1779 }
1780}
1781
1782pub struct DivFrameState {
1789 child_layout_ids: SmallVec<[LayoutId; 2]>,
1790}
1791
1792#[derive(Clone)]
1794pub struct DivInspectorState {
1795 #[cfg(any(feature = "inspector", debug_assertions))]
1799 pub base_style: Box<StyleRefinement>,
1800 pub bounds: Bounds<Pixels>,
1802 pub content_size: Size<Pixels>,
1804}
1805
1806impl Styled for Div {
1807 fn style(&mut self) -> &mut StyleRefinement {
1808 &mut self.interactivity.base_style
1809 }
1810}
1811
1812impl InteractiveElement for Div {
1813 fn interactivity(&mut self) -> &mut Interactivity {
1814 &mut self.interactivity
1815 }
1816}
1817
1818impl ParentElement for Div {
1819 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1820 self.children
1821 .extend(elements.into_iter().map(StackSafe::new))
1822 }
1823}
1824
1825impl Element for Div {
1826 type RequestLayoutState = DivFrameState;
1827 type PrepaintState = Option<Hitbox>;
1828
1829 fn id(&self) -> Option<ElementId> {
1830 self.interactivity.element_id.clone()
1831 }
1832
1833 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
1834 self.interactivity.source_location()
1835 }
1836
1837 fn a11y_role(&self) -> Option<accesskit::Role> {
1838 self.interactivity
1841 .override_role
1842 .filter(|role| *role != accesskit::Role::GenericContainer)
1843 }
1844
1845 fn write_a11y_info(&self, node: &mut accesskit::Node) {
1846 self.interactivity.write_a11y_info(node);
1847 }
1848
1849 fn a11y_synthetic_children(
1850 &mut self,
1851 _prepaint: &mut Self::PrepaintState,
1852 builder: &mut crate::A11ySubtreeBuilder,
1853 ) {
1854 if let Some(f) = self.interactivity.a11y_synthetic_children.take() {
1855 f(builder);
1856 }
1857 }
1858
1859 #[stacksafe]
1860 fn request_layout(
1861 &mut self,
1862 global_id: Option<&GlobalElementId>,
1863 inspector_id: Option<&InspectorElementId>,
1864 window: &mut Window,
1865 cx: &mut App,
1866 ) -> (LayoutId, Self::RequestLayoutState) {
1867 let mut child_layout_ids = SmallVec::new();
1868 let image_cache = self
1869 .image_cache
1870 .as_mut()
1871 .map(|provider| provider.provide(window, cx));
1872
1873 let layout_id = window.with_image_cache(image_cache, |window| {
1874 self.interactivity.request_layout(
1875 global_id,
1876 inspector_id,
1877 window,
1878 cx,
1879 |style, window, cx| {
1880 window.with_text_style(style.text_style().cloned(), |window| {
1881 child_layout_ids = self
1882 .children
1883 .iter_mut()
1884 .map(|child| child.request_layout(window, cx))
1885 .collect::<SmallVec<_>>();
1886 window.request_layout(style, child_layout_ids.iter().copied(), cx)
1887 })
1888 },
1889 )
1890 });
1891
1892 (layout_id, DivFrameState { child_layout_ids })
1893 }
1894
1895 #[stacksafe]
1896 fn prepaint(
1897 &mut self,
1898 global_id: Option<&GlobalElementId>,
1899 inspector_id: Option<&InspectorElementId>,
1900 bounds: Bounds<Pixels>,
1901 request_layout: &mut Self::RequestLayoutState,
1902 window: &mut Window,
1903 cx: &mut App,
1904 ) -> Option<Hitbox> {
1905 let image_cache = self
1906 .image_cache
1907 .as_mut()
1908 .map(|provider| provider.provide(window, cx));
1909
1910 let has_prepaint_listener = self.prepaint_listener.is_some();
1911 let mut children_bounds = Vec::with_capacity(if has_prepaint_listener {
1912 request_layout.child_layout_ids.len()
1913 } else {
1914 0
1915 });
1916
1917 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1918 let mut child_max = Point::default();
1919 if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1920 *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
1921 }
1922 let content_size = if request_layout.child_layout_ids.is_empty() {
1923 bounds.size
1924 } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1925 let mut state = scroll_handle.0.borrow_mut();
1926 state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1927 for child_layout_id in &request_layout.child_layout_ids {
1928 let child_bounds = window.layout_bounds(*child_layout_id);
1929 child_min = child_min.min(&child_bounds.origin);
1930 child_max = child_max.max(&child_bounds.bottom_right());
1931 state.child_bounds.push(child_bounds);
1932 }
1933 (child_max - child_min).into()
1934 } else {
1935 for child_layout_id in &request_layout.child_layout_ids {
1936 let child_bounds = window.layout_bounds(*child_layout_id);
1937 child_min = child_min.min(&child_bounds.origin);
1938 child_max = child_max.max(&child_bounds.bottom_right());
1939
1940 if has_prepaint_listener {
1941 children_bounds.push(child_bounds);
1942 }
1943 }
1944 (child_max - child_min).into()
1945 };
1946
1947 if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1948 scroll_handle.scroll_to_active_item();
1949 }
1950
1951 self.interactivity.prepaint(
1952 global_id,
1953 inspector_id,
1954 bounds,
1955 content_size,
1956 window,
1957 cx,
1958 |style, scroll_offset, hitbox, window, cx| {
1959 if style.display == Display::None {
1961 return hitbox;
1962 }
1963
1964 window.with_image_cache(image_cache, |window| {
1965 window.with_element_offset(scroll_offset, |window| {
1966 if let Some(order_fn) = &self.prepaint_order_fn {
1967 let order = order_fn(window, cx);
1968 for idx in order {
1969 if let Some(child) = self.children.get_mut(idx) {
1970 child.prepaint(window, cx);
1971 }
1972 }
1973 } else {
1974 for child in &mut self.children {
1975 child.prepaint(window, cx);
1976 }
1977 }
1978 });
1979
1980 if let Some(listener) = self.prepaint_listener.as_ref() {
1981 listener(children_bounds, window, cx);
1982 }
1983 });
1984
1985 hitbox
1986 },
1987 )
1988 }
1989
1990 #[stacksafe]
1991 fn paint(
1992 &mut self,
1993 global_id: Option<&GlobalElementId>,
1994 inspector_id: Option<&InspectorElementId>,
1995 bounds: Bounds<Pixels>,
1996 _request_layout: &mut Self::RequestLayoutState,
1997 hitbox: &mut Option<Hitbox>,
1998 window: &mut Window,
1999 cx: &mut App,
2000 ) {
2001 let image_cache = self
2002 .image_cache
2003 .as_mut()
2004 .map(|provider| provider.provide(window, cx));
2005
2006 window.with_image_cache(image_cache, |window| {
2007 self.interactivity.paint(
2008 global_id,
2009 inspector_id,
2010 bounds,
2011 hitbox.as_ref(),
2012 window,
2013 cx,
2014 |style, window, cx| {
2015 if style.display == Display::None {
2017 return;
2018 }
2019
2020 for child in &mut self.children {
2021 child.paint(window, cx);
2022 }
2023 },
2024 )
2025 });
2026 }
2027}
2028
2029impl IntoElement for Div {
2030 type Element = Self;
2031
2032 fn into_element(self) -> Self::Element {
2033 self
2034 }
2035}
2036
2037#[derive(Default)]
2038pub(crate) struct AriaProperties {
2039 pub(crate) label: Option<SharedString>,
2040 pub(crate) description: Option<SharedString>,
2041 pub(crate) keyshortcuts: Option<SharedString>,
2042 pub(crate) selected: Option<bool>,
2043 pub(crate) expanded: Option<bool>,
2044 pub(crate) toggled: Option<accesskit::Toggled>,
2045 pub(crate) numeric_value: Option<f64>,
2046 pub(crate) min_numeric_value: Option<f64>,
2047 pub(crate) max_numeric_value: Option<f64>,
2048 pub(crate) numeric_value_step: Option<f64>,
2049 pub(crate) value: Option<SharedString>,
2050 pub(crate) placeholder: Option<SharedString>,
2051 pub(crate) orientation: Option<accesskit::Orientation>,
2052 pub(crate) level: Option<usize>,
2053 pub(crate) position_in_set: Option<usize>,
2054 pub(crate) size_of_set: Option<usize>,
2055 pub(crate) row_index: Option<usize>,
2056 pub(crate) column_index: Option<usize>,
2057 pub(crate) row_count: Option<usize>,
2058 pub(crate) column_count: Option<usize>,
2059}
2060
2061#[derive(Default)]
2064pub struct Interactivity {
2065 pub element_id: Option<ElementId>,
2067 pub active: Option<bool>,
2069 pub hovered: Option<bool>,
2072 pub(crate) tooltip_id: Option<TooltipId>,
2073 pub(crate) content_size: Size<Pixels>,
2074 pub(crate) key_context: Option<KeyContext>,
2075 pub(crate) focusable: bool,
2076 pub(crate) tracked_focus_handle: Option<FocusHandle>,
2077 pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
2078 pub(crate) scroll_anchor: Option<ScrollAnchor>,
2079 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2080 pub(crate) ongoing_scroll: Option<Rc<RefCell<OngoingScroll>>>,
2081 pub(crate) group: Option<SharedString>,
2082 pub base_style: Box<StyleRefinement>,
2085 pub(crate) focus_style: Option<Box<StyleRefinement>>,
2086 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
2087 pub(crate) focus_visible_style: Option<Box<StyleRefinement>>,
2088 pub(crate) hover_style: Option<Box<StyleRefinement>>,
2089 pub(crate) group_hover_style: Option<GroupStyle>,
2090 pub(crate) active_style: Option<Box<StyleRefinement>>,
2091 pub(crate) group_active_style: Option<GroupStyle>,
2092 pub(crate) drag_over_styles: Vec<(
2093 TypeId,
2094 Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> StyleRefinement>,
2095 )>,
2096 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
2097 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
2098 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
2099 pub(crate) mouse_pressure_listeners: Vec<MousePressureListener>,
2100 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
2101 pub(crate) mouse_exit_listeners: Vec<MouseExitListener>,
2102 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
2103 pub(crate) pinch_listeners: Vec<PinchListener>,
2104 pub(crate) key_down_listeners: Vec<KeyDownListener>,
2105 pub(crate) key_up_listeners: Vec<KeyUpListener>,
2106 pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
2107 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
2108 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
2109 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
2110 pub(crate) click_listeners: Vec<ClickListener>,
2111 pub(crate) aux_click_listeners: Vec<ClickListener>,
2112 pub(crate) drag_listener: Option<DragListener>,
2113 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
2114 pub(crate) tooltip_builder: Option<TooltipBuilder>,
2115 pub(crate) tooltip_show_delay: Option<Duration>,
2116 pub(crate) window_control: Option<WindowControlArea>,
2117 pub(crate) hitbox_behavior: HitboxBehavior,
2118 pub(crate) tab_index: Option<isize>,
2119 pub(crate) tab_group: bool,
2120 pub(crate) tab_stop: bool,
2121
2122 pub(crate) a11y_action_listeners:
2123 Vec<(accesskit::Action, crate::window::a11y::A11yActionListener)>,
2124 pub(crate) a11y_synthetic_children: Option<Box<dyn FnOnce(&mut crate::A11ySubtreeBuilder)>>,
2125 pub(crate) report_active_descendant_focus: bool,
2126 pub(crate) override_role: Option<accesskit::Role>,
2127 pub(crate) aria: AriaProperties,
2128
2129 #[cfg(any(feature = "inspector", debug_assertions))]
2130 pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
2131
2132 #[cfg(any(test, feature = "test-support"))]
2133 pub(crate) debug_selector: Option<String>,
2134}
2135
2136impl Interactivity {
2137 pub fn set_scroll_offset(
2142 &self,
2143 global_id: Option<&GlobalElementId>,
2144 window: &mut Window,
2145 point: Point<Pixels>,
2146 ) {
2147 window.with_optional_element_state::<gpui::InteractiveElementState, _>(
2148 global_id,
2149 |element_state, _window| {
2150 let mut element_state =
2151 element_state.map(|element_state| element_state.unwrap_or_default());
2152 let overflow = &self.base_style.overflow;
2153 if (overflow.x == Some(Overflow::Scroll) || overflow.y == Some(Overflow::Scroll))
2154 && let Some(element_state) = element_state.as_mut()
2155 {
2156 let scroll_offset = element_state.scroll_offset.get_or_insert_with(Rc::default);
2157 *scroll_offset.borrow_mut() = point;
2158 }
2159 ((), element_state)
2160 },
2161 );
2162 }
2163}
2164
2165impl Interactivity {
2166 pub fn request_layout(
2168 &mut self,
2169 global_id: Option<&GlobalElementId>,
2170 _inspector_id: Option<&InspectorElementId>,
2171 window: &mut Window,
2172 cx: &mut App,
2173 f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
2174 ) -> LayoutId {
2175 #[cfg(any(feature = "inspector", debug_assertions))]
2176 window.with_inspector_state(
2177 _inspector_id,
2178 cx,
2179 |inspector_state: &mut Option<DivInspectorState>, _window| {
2180 if let Some(inspector_state) = inspector_state {
2181 self.base_style = inspector_state.base_style.clone();
2182 } else {
2183 *inspector_state = Some(DivInspectorState {
2184 base_style: self.base_style.clone(),
2185 bounds: Default::default(),
2186 content_size: Default::default(),
2187 })
2188 }
2189 },
2190 );
2191
2192 window.with_optional_element_state::<InteractiveElementState, _>(
2193 global_id,
2194 |element_state, window| {
2195 let mut element_state =
2196 element_state.map(|element_state| element_state.unwrap_or_default());
2197
2198 if let Some(element_state) = element_state.as_ref()
2199 && cx.has_active_drag()
2200 {
2201 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
2202 *pending_mouse_down.borrow_mut() = None;
2203 }
2204 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
2205 *clicked_state.borrow_mut() = ElementClickedState::default();
2206 }
2207 }
2208
2209 if self.focusable
2214 && self.tracked_focus_handle.is_none()
2215 && let Some(element_state) = element_state.as_mut()
2216 {
2217 let mut handle = element_state
2218 .focus_handle
2219 .get_or_insert_with(|| cx.focus_handle())
2220 .clone()
2221 .tab_stop(self.tab_stop);
2222
2223 if let Some(index) = self.tab_index {
2224 handle = handle.tab_index(index);
2225 }
2226
2227 self.tracked_focus_handle = Some(handle);
2228 }
2229
2230 if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
2231 let scroll_handle_state = scroll_handle.0.borrow();
2232 self.scroll_offset = Some(scroll_handle_state.offset.clone());
2233 self.ongoing_scroll = Some(scroll_handle_state.ongoing_scroll.clone());
2234 } else if (self.base_style.overflow.x == Some(Overflow::Scroll)
2235 || self.base_style.overflow.y == Some(Overflow::Scroll))
2236 && let Some(element_state) = element_state.as_mut()
2237 {
2238 self.scroll_offset = Some(
2239 element_state
2240 .scroll_offset
2241 .get_or_insert_with(Rc::default)
2242 .clone(),
2243 );
2244 self.ongoing_scroll = Some(
2245 element_state
2246 .ongoing_scroll
2247 .get_or_insert_with(|| Rc::new(RefCell::new(OngoingScroll::default())))
2248 .clone(),
2249 );
2250 }
2251
2252 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
2253 let layout_id = f(style, window, cx);
2254 (layout_id, element_state)
2255 },
2256 )
2257 }
2258
2259 pub fn prepaint<R>(
2261 &mut self,
2262 global_id: Option<&GlobalElementId>,
2263 _inspector_id: Option<&InspectorElementId>,
2264 bounds: Bounds<Pixels>,
2265 content_size: Size<Pixels>,
2266 window: &mut Window,
2267 cx: &mut App,
2268 f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
2269 ) -> R {
2270 self.content_size = content_size;
2271
2272 #[cfg(any(feature = "inspector", debug_assertions))]
2273 window.with_inspector_state(
2274 _inspector_id,
2275 cx,
2276 |inspector_state: &mut Option<DivInspectorState>, _window| {
2277 if let Some(inspector_state) = inspector_state {
2278 inspector_state.bounds = bounds;
2279 inspector_state.content_size = content_size;
2280 }
2281 },
2282 );
2283
2284 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2285 window.set_focus_handle(focus_handle, cx);
2286
2287 if window.a11y.is_active() {
2288 if let Some(global_id) = global_id {
2289 let node_id = global_id.accesskit_node_id();
2290 window.a11y.set_focusable(node_id, focus_handle.id);
2291 if focus_handle.is_focused(window) {
2292 window.a11y.set_focus(node_id);
2293 }
2294 } else if focus_handle.is_focused(window) {
2295 window
2299 .a11y
2300 .note_focus_without_node(focus_handle.id, "it has no element id");
2301 }
2302 }
2303 }
2304
2305 if self.report_active_descendant_focus && window.a11y.is_active() {
2306 if let Some(global_id) = global_id {
2307 window
2308 .a11y
2309 .set_active_descendant(global_id.accesskit_node_id());
2310 }
2311 }
2312 window.with_optional_element_state::<InteractiveElementState, _>(
2313 global_id,
2314 |element_state, window| {
2315 let mut element_state =
2316 element_state.map(|element_state| element_state.unwrap_or_default());
2317 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
2318
2319 if let Some(element_state) = element_state.as_mut() {
2320 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
2321 let clicked_state = clicked_state.borrow();
2322 self.active = Some(clicked_state.element);
2323 }
2324 if self.hover_style.is_some() || self.group_hover_style.is_some() {
2325 element_state
2326 .hover_state
2327 .get_or_insert_with(Default::default);
2328 }
2329 if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
2330 if self.tooltip_builder.is_some() {
2331 self.tooltip_id = set_tooltip_on_window(active_tooltip, window);
2332 } else {
2333 element_state.active_tooltip.take();
2335 }
2336 }
2337 }
2338
2339 window.with_text_style(style.text_style().cloned(), |window| {
2340 window.with_content_mask(
2341 style.overflow_mask(bounds, window.rem_size()),
2342 |window| {
2343 let hitbox = if self.should_insert_hitbox(&style, window, cx) {
2344 Some(window.insert_hitbox(bounds, self.hitbox_behavior))
2345 } else {
2346 None
2347 };
2348
2349 let scroll_offset =
2350 self.clamp_scroll_position(bounds, &style, window, cx);
2351 let result = f(&style, scroll_offset, hitbox, window, cx);
2352 (result, element_state)
2353 },
2354 )
2355 })
2356 },
2357 )
2358 }
2359
2360 fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
2361 self.hitbox_behavior != HitboxBehavior::Normal
2362 || self.window_control.is_some()
2363 || style.mouse_cursor.is_some()
2364 || self.group.is_some()
2365 || self.scroll_offset.is_some()
2366 || self.tracked_focus_handle.is_some()
2367 || self.hover_style.is_some()
2368 || self.group_hover_style.is_some()
2369 || self.hover_listener.is_some()
2370 || !self.mouse_up_listeners.is_empty()
2371 || !self.mouse_pressure_listeners.is_empty()
2372 || !self.mouse_down_listeners.is_empty()
2373 || !self.mouse_move_listeners.is_empty()
2374 || !self.mouse_exit_listeners.is_empty()
2375 || !self.click_listeners.is_empty()
2376 || !self.aux_click_listeners.is_empty()
2377 || !self.scroll_wheel_listeners.is_empty()
2378 || self.has_pinch_listeners()
2379 || self.drag_listener.is_some()
2380 || !self.drop_listeners.is_empty()
2381 || !self.drag_over_styles.is_empty()
2382 || self.tooltip_builder.is_some()
2383 || window.is_inspector_picking(cx)
2384 }
2385
2386 fn clamp_scroll_position(
2387 &self,
2388 bounds: Bounds<Pixels>,
2389 style: &Style,
2390 window: &mut Window,
2391 _cx: &mut App,
2392 ) -> Point<Pixels> {
2393 fn round_to_two_decimals(pixels: Pixels) -> Pixels {
2394 const ROUNDING_FACTOR: f32 = 100.0;
2395 (pixels * ROUNDING_FACTOR).round() / ROUNDING_FACTOR
2396 }
2397
2398 if let Some(scroll_offset) = self.scroll_offset.as_ref() {
2399 let mut scroll_to_bottom = false;
2400 let mut tracked_scroll_handle = self
2401 .tracked_scroll_handle
2402 .as_ref()
2403 .map(|handle| handle.0.borrow_mut());
2404 if let Some(mut scroll_handle_state) = tracked_scroll_handle.as_deref_mut() {
2405 scroll_handle_state.overflow = style.overflow;
2406 scroll_to_bottom = mem::take(&mut scroll_handle_state.scroll_to_bottom);
2407 }
2408
2409 let rem_size = window.rem_size();
2410 let padding = style.padding.to_pixels(bounds.size.into(), rem_size);
2411 let padding_size = size(padding.left + padding.right, padding.top + padding.bottom);
2412 let padded_content_size = self.content_size + padding_size;
2419 let scroll_max = Point::from(padded_content_size - bounds.size)
2420 .map(round_to_two_decimals)
2421 .max(&Default::default());
2422 let mut scroll_offset = scroll_offset.borrow_mut();
2425
2426 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.x, px(0.));
2427 if scroll_to_bottom {
2428 scroll_offset.y = -scroll_max.y;
2429 } else {
2430 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.y, px(0.));
2431 }
2432
2433 if let Some(mut scroll_handle_state) = tracked_scroll_handle {
2434 scroll_handle_state.max_offset = scroll_max;
2435 scroll_handle_state.bounds = bounds;
2436 }
2437
2438 *scroll_offset
2439 } else {
2440 Point::default()
2441 }
2442 }
2443
2444 pub fn paint(
2453 &mut self,
2454 global_id: Option<&GlobalElementId>,
2455 _inspector_id: Option<&InspectorElementId>,
2456 bounds: Bounds<Pixels>,
2457 hitbox: Option<&Hitbox>,
2458 window: &mut Window,
2459 cx: &mut App,
2460 f: impl FnOnce(&Style, &mut Window, &mut App),
2461 ) {
2462 self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window));
2463 window.with_optional_element_state::<InteractiveElementState, _>(
2464 global_id,
2465 |element_state, window| {
2466 let mut element_state =
2467 element_state.map(|element_state| element_state.unwrap_or_default());
2468
2469 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2470
2471 #[cfg(any(feature = "test-support", test))]
2472 if let Some(debug_selector) = &self.debug_selector {
2473 window
2474 .next_frame
2475 .debug_bounds
2476 .insert(debug_selector.clone(), bounds);
2477 }
2478
2479 self.paint_hover_group_handler(window, cx);
2480
2481 if style.visibility == Visibility::Hidden {
2482 return ((), element_state);
2483 }
2484
2485 let mut tab_group = None;
2486 if self.tab_group {
2487 tab_group = self.tab_index;
2488 }
2489
2490 window.with_element_opacity(style.opacity, |window| {
2491 style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
2492 window.with_text_style(style.text_style().cloned(), |window| {
2493 window.with_content_mask(
2494 style.overflow_mask(bounds, window.rem_size()),
2495 |window| {
2496 window.with_tab_group(tab_group, |window| {
2497 if let Some(focus_handle) = &self.tracked_focus_handle {
2506 window.next_frame.tab_stops.insert(focus_handle);
2507 }
2508 if let Some(hitbox) = hitbox {
2509 #[cfg(debug_assertions)]
2510 self.paint_debug_info(
2511 global_id, hitbox, &style, window, cx,
2512 );
2513
2514 if let Some(drag) = cx.active_drag.as_ref() {
2515 if let Some(mouse_cursor) = drag.cursor_style {
2516 window.set_window_cursor_style(mouse_cursor);
2517 }
2518 } else {
2519 if let Some(mouse_cursor) = style.mouse_cursor {
2520 window.set_cursor_style(mouse_cursor, hitbox);
2521 }
2522 }
2523
2524 if let Some(group) = self.group.clone() {
2525 GroupHitboxes::push(group, hitbox.id, cx);
2526 }
2527
2528 if let Some(area) = self.window_control {
2529 window.insert_window_control_hitbox(
2530 area,
2531 hitbox.clone(),
2532 );
2533 }
2534
2535 self.paint_mouse_listeners(
2536 hitbox,
2537 element_state.as_mut(),
2538 window,
2539 cx,
2540 );
2541 self.paint_scroll_listener(hitbox, &style, window, cx);
2542 }
2543
2544 self.paint_keyboard_listeners(window, cx);
2545
2546 if window.a11y.is_active() {
2547 if let Some(global_id) = global_id {
2548 if !self.a11y_action_listeners.is_empty() {
2549 let node_id = global_id.accesskit_node_id();
2550 for (action, listener) in
2551 self.a11y_action_listeners.drain(..)
2552 {
2553 window.on_a11y_action(
2554 node_id, action, listener,
2555 );
2556 }
2557 }
2558 }
2559 }
2560
2561 f(&style, window, cx);
2562
2563 if let Some(_hitbox) = hitbox {
2564 #[cfg(any(feature = "inspector", debug_assertions))]
2565 window.insert_inspector_hitbox(
2566 _hitbox.id,
2567 _inspector_id,
2568 cx,
2569 );
2570
2571 if let Some(group) = self.group.as_ref() {
2572 GroupHitboxes::pop(group, cx);
2573 }
2574 }
2575 })
2576 },
2577 );
2578 });
2579 });
2580 });
2581
2582 ((), element_state)
2583 },
2584 );
2585 }
2586
2587 #[cfg(debug_assertions)]
2588 fn paint_debug_info(
2589 &self,
2590 global_id: Option<&GlobalElementId>,
2591 hitbox: &Hitbox,
2592 style: &Style,
2593 window: &mut Window,
2594 cx: &mut App,
2595 ) {
2596 use crate::{BorderStyle, TextAlign};
2597
2598 if let Some(global_id) = global_id
2599 && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
2600 && hitbox.is_hovered(window)
2601 {
2602 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
2603 let element_id = format!("{global_id:?}");
2604 let str_len = element_id.len();
2605
2606 let render_debug_text = |window: &mut Window| {
2607 if let Some(text) = window
2608 .text_system()
2609 .shape_text(
2610 &element_id,
2611 FONT_SIZE,
2612 &[window.text_style().to_run(str_len)],
2613 None,
2614 None,
2615 )
2616 .ok()
2617 .and_then(|mut text| text.pop())
2618 {
2619 text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
2620 .ok();
2621
2622 let text_bounds = crate::Bounds {
2623 origin: hitbox.origin,
2624 size: text.size(FONT_SIZE),
2625 };
2626 if let Some(source_location) = self.source_location
2627 && text_bounds.contains(&window.mouse_position())
2628 && window.modifiers().secondary()
2629 {
2630 let secondary_held = window.modifiers().secondary();
2631 window.on_key_event({
2632 move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| {
2633 if e.modifiers.secondary() != secondary_held
2634 && text_bounds.contains(&window.mouse_position())
2635 {
2636 window.refresh();
2637 }
2638 }
2639 });
2640
2641 let was_hovered = hitbox.is_hovered(window);
2642 let current_view = window.current_view();
2643 window.on_mouse_event({
2644 let hitbox = hitbox.clone();
2645 move |_: &MouseMoveEvent, phase, window, cx| {
2646 if phase == DispatchPhase::Capture {
2647 let hovered = hitbox.is_hovered(window);
2648 if hovered != was_hovered {
2649 cx.notify(current_view)
2650 }
2651 }
2652 }
2653 });
2654
2655 window.on_mouse_event({
2656 let hitbox = hitbox.clone();
2657 move |e: &crate::MouseDownEvent, phase, window, cx| {
2658 if text_bounds.contains(&e.position)
2659 && phase.capture()
2660 && hitbox.is_hovered(window)
2661 {
2662 cx.stop_propagation();
2663 let Ok(dir) = std::env::current_dir() else {
2664 return;
2665 };
2666
2667 eprintln!(
2668 "This element was created at:\n{}:{}:{}",
2669 dir.join(source_location.file()).to_string_lossy(),
2670 source_location.line(),
2671 source_location.column()
2672 );
2673 }
2674 }
2675 });
2676 window.paint_quad(crate::outline(
2677 crate::Bounds {
2678 origin: hitbox.origin
2679 + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
2680 size: crate::Size {
2681 width: text_bounds.size.width,
2682 height: crate::px(1.),
2683 },
2684 },
2685 crate::red(),
2686 BorderStyle::default(),
2687 ))
2688 }
2689 }
2690 };
2691
2692 window.with_text_style(
2693 Some(crate::TextStyleRefinement {
2694 color: Some(crate::red()),
2695 line_height: Some(FONT_SIZE.into()),
2696 background_color: Some(crate::white()),
2697 ..Default::default()
2698 }),
2699 render_debug_text,
2700 )
2701 }
2702 }
2703
2704 fn paint_mouse_listeners(
2705 &mut self,
2706 hitbox: &Hitbox,
2707 element_state: Option<&mut InteractiveElementState>,
2708 window: &mut Window,
2709 cx: &mut App,
2710 ) {
2711 let is_focused = self
2712 .tracked_focus_handle
2713 .as_ref()
2714 .map(|handle| handle.is_focused(window))
2715 .unwrap_or(false);
2716
2717 if let Some(focus_handle) = self.tracked_focus_handle.clone() {
2721 let hitbox = hitbox.clone();
2722 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, cx| {
2723 if phase == DispatchPhase::Bubble
2724 && hitbox.is_hovered(window)
2725 && !window.default_prevented()
2726 {
2727 window.focus(&focus_handle, cx);
2728 window.prevent_default();
2731 }
2732 });
2733 }
2734
2735 for listener in self.mouse_down_listeners.drain(..) {
2736 let hitbox = hitbox.clone();
2737 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
2738 listener(event, phase, &hitbox, window, cx);
2739 })
2740 }
2741
2742 for listener in self.mouse_up_listeners.drain(..) {
2743 let hitbox = hitbox.clone();
2744 window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
2745 listener(event, phase, &hitbox, window, cx);
2746 })
2747 }
2748
2749 for listener in self.mouse_pressure_listeners.drain(..) {
2750 let hitbox = hitbox.clone();
2751 window.on_mouse_event(move |event: &MousePressureEvent, phase, window, cx| {
2752 listener(event, phase, &hitbox, window, cx);
2753 })
2754 }
2755
2756 for listener in self.mouse_move_listeners.drain(..) {
2757 let hitbox = hitbox.clone();
2758 window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
2759 listener(event, phase, &hitbox, window, cx);
2760 })
2761 }
2762
2763 for listener in self.mouse_exit_listeners.drain(..) {
2764 let hitbox = hitbox.clone();
2765 window.on_mouse_event(move |event: &MouseExitEvent, phase, window, cx| {
2766 listener(event, phase, &hitbox, window, cx);
2767 })
2768 }
2769
2770 for listener in self.scroll_wheel_listeners.drain(..) {
2771 let hitbox = hitbox.clone();
2772 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2773 listener(event, phase, &hitbox, window, cx);
2774 })
2775 }
2776
2777 for listener in self.pinch_listeners.drain(..) {
2778 let hitbox = hitbox.clone();
2779 window.on_mouse_event(move |event: &PinchEvent, phase, window, cx| {
2780 listener(event, phase, &hitbox, window, cx);
2781 })
2782 }
2783
2784 if self.hover_style.is_some()
2785 || self.base_style.mouse_cursor.is_some()
2786 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
2787 {
2788 let hitbox = hitbox.clone();
2789 let hover_state = self.hover_style.as_ref().and_then(|_| {
2790 element_state
2791 .as_ref()
2792 .and_then(|state| state.hover_state.as_ref())
2793 .cloned()
2794 });
2795 let current_view = window.current_view();
2796
2797 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2798 let hovered = hitbox.is_hovered(window);
2799 let was_hovered = hover_state
2800 .as_ref()
2801 .is_some_and(|state| state.borrow().element);
2802 if phase == DispatchPhase::Capture && hovered != was_hovered {
2803 if let Some(hover_state) = &hover_state {
2804 hover_state.borrow_mut().element = hovered;
2805 cx.notify(current_view);
2806 }
2807 }
2808 });
2809 }
2810
2811 if let Some(group_hover) = self.group_hover_style.as_ref() {
2812 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
2813 let hover_state = element_state
2814 .as_ref()
2815 .and_then(|element| element.hover_state.as_ref())
2816 .cloned();
2817 let current_view = window.current_view();
2818
2819 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2820 let group_hovered = group_hitbox_id.is_hovered(window);
2821 let was_group_hovered = hover_state
2822 .as_ref()
2823 .is_some_and(|state| state.borrow().group);
2824 if phase == DispatchPhase::Capture && group_hovered != was_group_hovered {
2825 if let Some(hover_state) = &hover_state {
2826 hover_state.borrow_mut().group = group_hovered;
2827 cx.notify(current_view);
2828 }
2829 }
2830 });
2831 }
2832 }
2833
2834 let drag_cursor_style = self.base_style.as_ref().mouse_cursor;
2835
2836 let mut drag_listener = mem::take(&mut self.drag_listener);
2837 let drop_listeners = mem::take(&mut self.drop_listeners);
2838 let click_listeners = mem::take(&mut self.click_listeners);
2839 let aux_click_listeners = mem::take(&mut self.aux_click_listeners);
2840 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
2841
2842 if !drop_listeners.is_empty() {
2843 let hitbox = hitbox.clone();
2844 window.on_mouse_event({
2845 move |_: &MouseUpEvent, phase, window, cx| {
2846 if let Some(drag) = &cx.active_drag
2847 && phase == DispatchPhase::Bubble
2848 && hitbox.is_hovered(window)
2849 {
2850 let drag_state_type = drag.value.as_ref().type_id();
2851 for (drop_state_type, listener) in &drop_listeners {
2852 if *drop_state_type == drag_state_type {
2853 let drag = cx
2854 .active_drag
2855 .take()
2856 .expect("checked for type drag state type above");
2857
2858 let mut can_drop = true;
2859 if let Some(predicate) = &can_drop_predicate {
2860 can_drop = predicate(drag.value.as_ref(), window, cx);
2861 }
2862
2863 if can_drop {
2864 listener(drag.value.as_ref(), window, cx);
2865 window.refresh();
2866 cx.stop_propagation();
2867 }
2868 }
2869 }
2870 }
2871 }
2872 });
2873 }
2874
2875 if let Some(element_state) = element_state {
2876 if !click_listeners.is_empty()
2877 || !aux_click_listeners.is_empty()
2878 || drag_listener.is_some()
2879 {
2880 let pending_mouse_down = element_state
2881 .pending_mouse_down
2882 .get_or_insert_with(Default::default)
2883 .clone();
2884
2885 let pending_keyboard_down = element_state
2886 .pending_keyboard_down
2887 .get_or_insert_with(Default::default)
2888 .clone();
2889
2890 let clicked_state = element_state
2891 .clicked_state
2892 .get_or_insert_with(Default::default)
2893 .clone();
2894
2895 window.on_mouse_event({
2896 let pending_mouse_down = pending_mouse_down.clone();
2897 let hitbox = hitbox.clone();
2898 let has_aux_click_listeners = !aux_click_listeners.is_empty();
2899 move |event: &MouseDownEvent, phase, window, _cx| {
2900 if phase == DispatchPhase::Bubble
2901 && (event.button == MouseButton::Left || has_aux_click_listeners)
2902 && hitbox.is_hovered(window)
2903 {
2904 *pending_mouse_down.borrow_mut() = Some(event.clone());
2905 window.refresh();
2906 }
2907 }
2908 });
2909
2910 window.on_mouse_event({
2911 let pending_mouse_down = pending_mouse_down.clone();
2912 let hitbox = hitbox.clone();
2913 move |event: &MouseMoveEvent, phase, window, cx| {
2914 if phase == DispatchPhase::Capture {
2915 return;
2916 }
2917
2918 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2919 if let Some(mouse_down) = pending_mouse_down.clone()
2920 && !cx.has_active_drag()
2921 && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
2922 && let Some(listener) = drag_listener.take()
2923 && mouse_down.button == MouseButton::Left
2924 {
2925 *clicked_state.borrow_mut() = ElementClickedState::default();
2926 let cursor_offset = event.position - hitbox.origin;
2927 let drag = (listener.render)(
2928 listener.value.as_ref(),
2929 cursor_offset,
2930 window,
2931 cx,
2932 );
2933 let external_payload_source =
2934 listener.external_payload.map(|external_payload| {
2935 let value = listener.value.clone();
2936 Box::new(move |window: &mut Window, cx: &mut App| {
2937 external_payload(value.as_ref(), window, cx)
2938 })
2939 as ExternalDragPayloadSource
2940 });
2941 cx.active_drag = Some(AnyDrag {
2942 view: drag,
2943 value: listener.value,
2944 cursor_offset,
2945 cursor_style: drag_cursor_style,
2946 external_payload_source,
2947 });
2948 pending_mouse_down.take();
2949 window.refresh();
2950 cx.stop_propagation();
2951 }
2952 }
2953 });
2954
2955 if is_focused {
2956 window.on_key_event({
2966 let pending_keyboard_down = pending_keyboard_down.clone();
2967 move |event: &KeyDownEvent, phase, window, _cx| {
2968 if phase.bubble() && !window.default_prevented() {
2969 let stroke = &event.keystroke;
2970 let is_activation_key = (stroke.key.eq("enter")
2971 || stroke.key.eq("space"))
2972 && !stroke.modifiers.modified();
2973 *pending_keyboard_down.borrow_mut() =
2974 is_activation_key.then_some(window.focus_generation);
2975 }
2976 }
2977 });
2978
2979 window.on_key_event({
2981 let click_listeners = click_listeners.clone();
2982 let hitbox = hitbox.clone();
2983 move |event: &KeyUpEvent, phase, window, cx| {
2984 if phase.bubble() && !window.default_prevented() {
2985 let stroke = &event.keystroke;
2986 let keyboard_button = if stroke.key.eq("enter") {
2987 Some(KeyboardButton::Enter)
2988 } else if stroke.key.eq("space") {
2989 Some(KeyboardButton::Space)
2990 } else {
2991 None
2992 };
2993
2994 if let Some(button) = keyboard_button
2995 && !stroke.modifiers.modified()
2996 {
2997 let pending =
2998 std::mem::take(&mut *pending_keyboard_down.borrow_mut());
2999 if pending != Some(window.focus_generation) {
3000 return;
3001 }
3002
3003 let click_event = ClickEvent::Keyboard(KeyboardClickEvent {
3004 button,
3005 bounds: hitbox.bounds,
3006 });
3007
3008 for listener in &click_listeners {
3009 listener(&click_event, window, cx);
3010 }
3011 } else {
3012 *pending_keyboard_down.borrow_mut() = None;
3016 }
3017 }
3018 }
3019 });
3020 }
3021
3022 window.on_mouse_event({
3023 let mut captured_mouse_down = None;
3024 let hitbox = hitbox.clone();
3025 move |event: &MouseUpEvent, phase, window, cx| match phase {
3026 DispatchPhase::Capture => {
3030 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
3031 if pending_mouse_down.is_some() && hitbox.is_hovered(window) {
3032 captured_mouse_down = pending_mouse_down.take();
3033 window.refresh();
3034 } else if pending_mouse_down.is_some() {
3035 pending_mouse_down.take();
3041 window.refresh();
3042 }
3043 }
3044 DispatchPhase::Bubble => {
3046 if let Some(mouse_down) = captured_mouse_down.take() {
3047 let btn = mouse_down.button;
3048
3049 let mouse_click = ClickEvent::Mouse(MouseClickEvent {
3050 down: mouse_down,
3051 up: event.clone(),
3052 });
3053
3054 match btn {
3055 MouseButton::Left => {
3056 for listener in &click_listeners {
3057 listener(&mouse_click, window, cx);
3058 }
3059 }
3060 _ => {
3061 for listener in &aux_click_listeners {
3062 listener(&mouse_click, window, cx);
3063 }
3064 }
3065 }
3066 }
3067 }
3068 }
3069 });
3070 }
3071
3072 if let Some(hover_listener) = self.hover_listener.take() {
3073 let was_hovered = element_state
3074 .hover_listener_state
3075 .get_or_insert_with(Default::default)
3076 .clone();
3077 let has_mouse_down = element_state
3078 .pending_mouse_down
3079 .get_or_insert_with(Default::default)
3080 .clone();
3081 let hover_listener = Rc::new(hover_listener);
3082 let update_hover = move |is_hovered: bool, window: &mut Window, cx: &mut App| {
3083 let mut was_hovered = was_hovered.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
3091 window.on_mouse_event({
3092 let update_hover = update_hover.clone();
3093 let hitbox = hitbox.clone();
3094 move |_: &MouseMoveEvent, phase, window, cx| {
3095 if phase == DispatchPhase::Bubble {
3096 let is_hovered = has_mouse_down.borrow().is_none()
3097 && !cx.has_active_drag()
3098 && hitbox.is_hovered(window);
3099 update_hover(is_hovered, window, cx);
3100 }
3101 }
3102 });
3103
3104 window.on_mouse_event(move |_: &MouseExitEvent, phase, window, cx| {
3107 if phase == DispatchPhase::Bubble {
3108 update_hover(false, window, cx);
3109 }
3110 });
3111 }
3112
3113 if let Some(tooltip_builder) = self.tooltip_builder.take() {
3114 let active_tooltip = element_state
3115 .active_tooltip
3116 .get_or_insert_with(Default::default)
3117 .clone();
3118 let pending_mouse_down = element_state
3119 .pending_mouse_down
3120 .get_or_insert_with(Default::default)
3121 .clone();
3122
3123 let tooltip_is_hoverable = tooltip_builder.hoverable;
3124 let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| {
3125 Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable))
3126 });
3127 let check_is_hovered_during_prepaint = Rc::new({
3129 let pending_mouse_down = pending_mouse_down.clone();
3130 let source_bounds = hitbox.bounds;
3131 move |window: &Window| {
3132 !window.last_input_was_keyboard()
3133 && pending_mouse_down.borrow().is_none()
3134 && source_bounds.contains(&window.mouse_position())
3135 }
3136 });
3137 let check_is_hovered = Rc::new({
3138 let hitbox = hitbox.clone();
3139 move |window: &Window| {
3140 pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window)
3141 }
3142 });
3143 register_tooltip_mouse_handlers(
3144 &active_tooltip,
3145 self.tooltip_id,
3146 build_tooltip,
3147 check_is_hovered,
3148 check_is_hovered_during_prepaint,
3149 self.tooltip_show_delay,
3150 window,
3151 );
3152 }
3153
3154 let active_state = element_state
3157 .clicked_state
3158 .get_or_insert_with(Default::default)
3159 .clone();
3160
3161 {
3162 let active_state = active_state.clone();
3163 window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| {
3164 if phase == DispatchPhase::Capture && active_state.borrow().is_clicked() {
3165 *active_state.borrow_mut() = ElementClickedState::default();
3166 window.refresh();
3167 }
3168 });
3169 }
3170
3171 {
3172 let active_group_hitbox = self
3173 .group_active_style
3174 .as_ref()
3175 .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
3176 let hitbox = hitbox.clone();
3177 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| {
3178 if phase == DispatchPhase::Bubble && !window.default_prevented() {
3179 let group_hovered = active_group_hitbox
3180 .is_some_and(|group_hitbox_id| group_hitbox_id.is_hovered(window));
3181 let element_hovered = hitbox.is_hovered(window);
3182 if group_hovered || element_hovered {
3183 *active_state.borrow_mut() = ElementClickedState {
3184 group: group_hovered,
3185 element: element_hovered,
3186 };
3187 window.refresh();
3188 }
3189 }
3190 });
3191 }
3192 }
3193 }
3194
3195 fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) {
3196 let key_down_listeners = mem::take(&mut self.key_down_listeners);
3197 let key_up_listeners = mem::take(&mut self.key_up_listeners);
3198 let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
3199 let action_listeners = mem::take(&mut self.action_listeners);
3200 if let Some(context) = self.key_context.clone() {
3201 window.set_key_context(context);
3202 }
3203
3204 for listener in key_down_listeners {
3205 window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| {
3206 listener(event, phase, window, cx);
3207 })
3208 }
3209
3210 for listener in key_up_listeners {
3211 window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| {
3212 listener(event, phase, window, cx);
3213 })
3214 }
3215
3216 for listener in modifiers_changed_listeners {
3217 window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| {
3218 listener(event, window, cx);
3219 })
3220 }
3221
3222 for (action_type, listener) in action_listeners {
3223 window.on_action(action_type, listener)
3224 }
3225 }
3226
3227 fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) {
3228 let group_hitbox = self
3229 .group_hover_style
3230 .as_ref()
3231 .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
3232
3233 if let Some(group_hitbox) = group_hitbox {
3234 let was_hovered = group_hitbox.is_hovered(window);
3235 let current_view = window.current_view();
3236 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
3237 let hovered = group_hitbox.is_hovered(window);
3238 if phase == DispatchPhase::Capture && hovered != was_hovered {
3239 cx.notify(current_view);
3240 }
3241 });
3242 }
3243 }
3244
3245 fn paint_scroll_listener(
3246 &self,
3247 hitbox: &Hitbox,
3248 style: &Style,
3249 window: &mut Window,
3250 _cx: &mut App,
3251 ) {
3252 if let Some(scroll_offset) = self.scroll_offset.clone() {
3253 let ongoing_scroll = self.ongoing_scroll.clone();
3254 let overflow = style.overflow;
3255 let allow_concurrent_scroll = style.allow_concurrent_scroll;
3256 let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
3257 let line_height = window.line_height();
3258 let hitbox = hitbox.clone();
3259 let current_view = window.current_view();
3260 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
3261 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
3262 let mut scroll_offset = scroll_offset.borrow_mut();
3263 let old_scroll_offset = *scroll_offset;
3264 let mut delta = event.delta.pixel_delta(line_height);
3265
3266 if restrict_scroll_to_axis
3267 && event.delta.precise()
3268 && let Some(ongoing_scroll) = &ongoing_scroll
3269 {
3270 ongoing_scroll
3271 .borrow_mut()
3272 .filter(&mut delta, event.touch_phase);
3273 }
3274
3275 let mut delta_x = match overflow.x {
3276 Overflow::Scroll if !delta.x.is_zero() => delta.x,
3277 Overflow::Scroll
3278 if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll =>
3279 {
3280 delta.y
3281 }
3282 _ => Pixels::ZERO,
3283 };
3284 let mut delta_y = match overflow.y {
3285 Overflow::Scroll if !delta.y.is_zero() => delta.y,
3286 Overflow::Scroll
3287 if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll =>
3288 {
3289 delta.x
3290 }
3291 _ => Pixels::ZERO,
3292 };
3293 if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
3294 if delta_x.abs() > delta_y.abs() {
3295 delta_y = Pixels::ZERO;
3296 } else {
3297 delta_x = Pixels::ZERO;
3298 }
3299 }
3300 scroll_offset.y += delta_y;
3301 scroll_offset.x += delta_x;
3302 if *scroll_offset != old_scroll_offset {
3303 cx.notify(current_view);
3304 }
3305 }
3306 });
3307 }
3308 }
3309
3310 pub fn compute_style(
3312 &self,
3313 global_id: Option<&GlobalElementId>,
3314 hitbox: Option<&Hitbox>,
3315 window: &mut Window,
3316 cx: &mut App,
3317 ) -> Style {
3318 window.with_optional_element_state(global_id, |element_state, window| {
3319 let mut element_state =
3320 element_state.map(|element_state| element_state.unwrap_or_default());
3321 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
3322 (style, element_state)
3323 })
3324 }
3325
3326 fn compute_style_internal(
3328 &self,
3329 hitbox: Option<&Hitbox>,
3330 element_state: Option<&mut InteractiveElementState>,
3331 window: &mut Window,
3332 cx: &mut App,
3333 ) -> Style {
3334 let mut style = Style::default();
3335 style.refine(&self.base_style);
3336
3337 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
3338 if let Some(in_focus_style) = self.in_focus_style.as_ref()
3339 && focus_handle.within_focused(window, cx)
3340 {
3341 style.refine(in_focus_style);
3342 }
3343
3344 if let Some(focus_style) = self.focus_style.as_ref()
3345 && focus_handle.is_focused(window)
3346 {
3347 style.refine(focus_style);
3348 }
3349
3350 if let Some(focus_visible_style) = self.focus_visible_style.as_ref()
3351 && focus_handle.is_focused(window)
3352 && window.last_input_was_keyboard()
3353 {
3354 style.refine(focus_visible_style);
3355 }
3356 }
3357
3358 if !cx.has_active_drag() {
3359 if let Some(group_hover) = self.group_hover_style.as_ref() {
3360 let is_group_hovered =
3361 if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) {
3362 group_hitbox_id.is_hovered(window)
3363 } else if let Some(element_state) = element_state.as_ref() {
3364 element_state
3365 .hover_state
3366 .as_ref()
3367 .map(|state| state.borrow().group)
3368 .unwrap_or(false)
3369 } else {
3370 false
3371 };
3372
3373 if is_group_hovered {
3374 style.refine(&group_hover.style);
3375 }
3376 }
3377
3378 if let Some(hover_style) = self.hover_style.as_ref() {
3379 let is_hovered = if let Some(hitbox) = hitbox {
3380 hitbox.is_hovered(window)
3381 } else if let Some(element_state) = element_state.as_ref() {
3382 element_state
3383 .hover_state
3384 .as_ref()
3385 .map(|state| state.borrow().element)
3386 .unwrap_or(false)
3387 } else {
3388 false
3389 };
3390
3391 if is_hovered {
3392 style.refine(hover_style);
3393 }
3394 }
3395 }
3396
3397 if let Some(hitbox) = hitbox {
3398 if let Some(drag) = cx.active_drag.take() {
3399 let mut can_drop = true;
3400 if let Some(can_drop_predicate) = &self.can_drop_predicate {
3401 can_drop = can_drop_predicate(drag.value.as_ref(), window, cx);
3402 }
3403
3404 if can_drop {
3405 for (state_type, group_drag_style) in &self.group_drag_over_styles {
3406 if let Some(group_hitbox_id) =
3407 GroupHitboxes::get(&group_drag_style.group, cx)
3408 && *state_type == drag.value.as_ref().type_id()
3409 && group_hitbox_id.is_hovered(window)
3410 {
3411 style.refine(&group_drag_style.style);
3412 }
3413 }
3414
3415 for (state_type, build_drag_over_style) in &self.drag_over_styles {
3416 if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window)
3417 {
3418 style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx));
3419 }
3420 }
3421 }
3422
3423 style.mouse_cursor = drag.cursor_style;
3424 cx.active_drag = Some(drag);
3425 }
3426 }
3427
3428 if let Some(element_state) = element_state {
3429 let clicked_state = element_state
3430 .clicked_state
3431 .get_or_insert_with(Default::default)
3432 .borrow();
3433 if clicked_state.group
3434 && let Some(group) = self.group_active_style.as_ref()
3435 {
3436 style.refine(&group.style)
3437 }
3438
3439 if let Some(active_style) = self.active_style.as_ref()
3440 && clicked_state.element
3441 {
3442 style.refine(active_style)
3443 }
3444 }
3445
3446 style
3447 }
3448
3449 pub(crate) fn write_a11y_info(&self, node: &mut accesskit::Node) {
3450 if let Some(label) = &self.aria.label {
3451 node.set_label(label.to_string());
3452 }
3453 if let Some(description) = &self.aria.description {
3454 node.set_description(description.to_string());
3455 }
3456 if let Some(keyshortcuts) = &self.aria.keyshortcuts {
3457 node.set_keyboard_shortcut(keyshortcuts.to_string());
3458 }
3459 if let Some(selected) = self.aria.selected {
3460 node.set_selected(selected);
3461 }
3462 if let Some(expanded) = self.aria.expanded {
3463 node.set_expanded(expanded);
3464 }
3465 if let Some(toggled) = self.aria.toggled {
3466 node.set_toggled(toggled);
3467 }
3468 if let Some(value) = self.aria.numeric_value {
3469 node.set_numeric_value(value);
3470 }
3471 if let Some(value) = self.aria.min_numeric_value {
3472 node.set_min_numeric_value(value);
3473 }
3474 if let Some(value) = self.aria.max_numeric_value {
3475 node.set_max_numeric_value(value);
3476 }
3477 if let Some(step) = self.aria.numeric_value_step {
3478 node.set_numeric_value_step(step);
3479 }
3480 if let Some(value) = &self.aria.value {
3481 node.set_value(value.to_string());
3482 }
3483 if let Some(placeholder) = &self.aria.placeholder {
3484 node.set_placeholder(placeholder.to_string());
3485 }
3486 if let Some(orientation) = self.aria.orientation {
3487 node.set_orientation(orientation);
3488 }
3489 if let Some(level) = self.aria.level {
3490 node.set_level(level);
3491 }
3492 if let Some(position) = self.aria.position_in_set {
3493 node.set_position_in_set(position);
3494 }
3495 if let Some(size) = self.aria.size_of_set {
3496 node.set_size_of_set(size);
3497 }
3498 if let Some(index) = self.aria.row_index {
3499 node.set_row_index(index);
3500 }
3501 if let Some(index) = self.aria.column_index {
3502 node.set_column_index(index);
3503 }
3504 if let Some(count) = self.aria.row_count {
3505 node.set_row_count(count);
3506 }
3507 if let Some(count) = self.aria.column_count {
3508 node.set_column_count(count);
3509 }
3510 if !self.click_listeners.is_empty() {
3511 node.add_action(accesskit::Action::Click);
3512 }
3513 if self.tracked_focus_handle.is_some() || self.focusable {
3514 node.add_action(accesskit::Action::Focus);
3515 }
3516 for (action, _) in &self.a11y_action_listeners {
3517 node.add_action(*action);
3518 }
3519 }
3520}
3521
3522#[derive(Default)]
3525pub struct InteractiveElementState {
3526 pub(crate) focus_handle: Option<FocusHandle>,
3527 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
3528 pub(crate) hover_state: Option<Rc<RefCell<ElementHoverState>>>,
3529 pub(crate) hover_listener_state: Option<Rc<RefCell<bool>>>,
3530 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
3531 pub(crate) pending_keyboard_down: Option<Rc<RefCell<Option<u64>>>>,
3539 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
3540 ongoing_scroll: Option<Rc<RefCell<OngoingScroll>>>,
3541 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
3542}
3543
3544#[derive(Copy, Clone, Default, Eq, PartialEq)]
3546pub struct ElementClickedState {
3547 pub group: bool,
3549
3550 pub element: bool,
3552}
3553
3554impl ElementClickedState {
3555 fn is_clicked(&self) -> bool {
3556 self.group || self.element
3557 }
3558}
3559
3560#[derive(Copy, Clone, Default, Eq, PartialEq)]
3562pub struct ElementHoverState {
3563 pub group: bool,
3565
3566 pub element: bool,
3568}
3569
3570pub(crate) enum ActiveTooltip {
3571 WaitingForShow { _task: Task<()> },
3573 Visible {
3575 tooltip: AnyTooltip,
3576 is_hoverable: bool,
3577 },
3578 WaitingForHide {
3581 tooltip: AnyTooltip,
3582 _task: Task<()>,
3583 },
3584}
3585
3586pub(crate) fn clear_active_tooltip(
3587 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3588 window: &mut Window,
3589) {
3590 match active_tooltip.borrow_mut().take() {
3591 None => {}
3592 Some(ActiveTooltip::WaitingForShow { .. }) => {}
3593 Some(ActiveTooltip::Visible { .. }) => window.refresh(),
3594 Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(),
3595 }
3596}
3597
3598pub(crate) fn clear_active_tooltip_if_not_hoverable(
3599 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3600 window: &mut Window,
3601) {
3602 let should_clear = match active_tooltip.borrow().as_ref() {
3603 None => false,
3604 Some(ActiveTooltip::WaitingForShow { .. }) => false,
3605 Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable,
3606 Some(ActiveTooltip::WaitingForHide { .. }) => false,
3607 };
3608 if should_clear {
3609 active_tooltip.borrow_mut().take();
3610 window.refresh();
3611 }
3612}
3613
3614pub(crate) fn set_tooltip_on_window(
3615 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3616 window: &mut Window,
3617) -> Option<TooltipId> {
3618 let tooltip = match active_tooltip.borrow().as_ref() {
3619 None => return None,
3620 Some(ActiveTooltip::WaitingForShow { .. }) => return None,
3621 Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(),
3622 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(),
3623 };
3624 Some(window.set_tooltip(tooltip))
3625}
3626
3627pub(crate) fn register_tooltip_mouse_handlers(
3628 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3629 tooltip_id: Option<TooltipId>,
3630 build_tooltip: Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3631 check_is_hovered: Rc<dyn Fn(&Window) -> bool>,
3632 check_is_hovered_during_prepaint: Rc<dyn Fn(&Window) -> bool>,
3633 show_delay: Option<Duration>,
3634 window: &mut Window,
3635) {
3636 let current_view = window.current_view();
3637 let show_delay = show_delay.unwrap_or(DEFAULT_TOOLTIP_SHOW_DELAY);
3638
3639 window.on_mouse_event({
3640 let active_tooltip = active_tooltip.clone();
3641 let build_tooltip = build_tooltip.clone();
3642 let check_is_hovered = check_is_hovered.clone();
3643 move |_: &MouseMoveEvent, phase, window, cx| {
3644 handle_tooltip_mouse_move(
3645 &active_tooltip,
3646 &build_tooltip,
3647 &check_is_hovered,
3648 &check_is_hovered_during_prepaint,
3649 tooltip_id,
3650 current_view,
3651 phase,
3652 show_delay,
3653 window,
3654 cx,
3655 )
3656 }
3657 });
3658
3659 window.on_mouse_event({
3660 let active_tooltip = active_tooltip.clone();
3661 move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| {
3662 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3663 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3664 }
3665 }
3666 });
3667
3668 window.on_mouse_event({
3669 let active_tooltip = active_tooltip.clone();
3670 move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| {
3671 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3672 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3673 }
3674 }
3675 });
3676}
3677
3678fn handle_tooltip_mouse_move(
3690 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3691 build_tooltip: &Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3692 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3693 check_is_hovered_during_prepaint: &Rc<dyn Fn(&Window) -> bool>,
3694 tooltip_id: Option<TooltipId>,
3695 current_view: EntityId,
3696 phase: DispatchPhase,
3697 show_delay: Duration,
3698 window: &mut Window,
3699 cx: &mut App,
3700) {
3701 enum Action {
3704 None,
3705 CancelShow,
3706 ScheduleShow,
3707 CheckVisible,
3708 }
3709
3710 let action = match active_tooltip.borrow().as_ref() {
3711 None => {
3712 let is_hovered = check_is_hovered(window);
3713 if is_hovered && phase.bubble() {
3714 Action::ScheduleShow
3715 } else {
3716 Action::None
3717 }
3718 }
3719 Some(ActiveTooltip::WaitingForShow { .. }) => {
3720 let is_hovered = check_is_hovered(window);
3721 if is_hovered {
3722 Action::None
3723 } else {
3724 Action::CancelShow
3725 }
3726 }
3727 Some(ActiveTooltip::Visible { is_hoverable, .. }) => {
3728 if phase.capture()
3729 && !check_is_hovered(window)
3730 && (!*is_hoverable
3731 || !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)))
3732 {
3733 Action::CheckVisible
3734 } else {
3735 Action::None
3736 }
3737 }
3738 Some(ActiveTooltip::WaitingForHide { .. }) => {
3739 if phase.capture()
3740 && (check_is_hovered(window)
3741 || tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)))
3742 {
3743 Action::CheckVisible
3744 } else {
3745 Action::None
3746 }
3747 }
3748 };
3749
3750 match action {
3751 Action::None => {}
3752 Action::CancelShow => {
3753 active_tooltip.borrow_mut().take();
3755 }
3756 Action::ScheduleShow => {
3757 let delayed_show_task = window.spawn(cx, {
3758 let weak_active_tooltip = Rc::downgrade(active_tooltip);
3759 let build_tooltip = build_tooltip.clone();
3760 let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone();
3761 async move |cx| {
3762 cx.background_executor().timer(show_delay).await;
3763 let Some(active_tooltip) = weak_active_tooltip.upgrade() else {
3764 return;
3765 };
3766 cx.update(|window, cx| {
3767 let new_tooltip =
3768 build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| {
3769 let weak_active_tooltip = Rc::downgrade(&active_tooltip);
3770 ActiveTooltip::Visible {
3771 tooltip: AnyTooltip {
3772 view,
3773 mouse_position: window.mouse_position(),
3774 check_visible_and_update: Rc::new(
3775 move |tooltip_bounds, window, cx| {
3776 let Some(active_tooltip) =
3777 weak_active_tooltip.upgrade()
3778 else {
3779 return false;
3780 };
3781 handle_tooltip_check_visible_and_update(
3782 &active_tooltip,
3783 tooltip_is_hoverable,
3784 &check_is_hovered_during_prepaint,
3785 tooltip_bounds,
3786 window,
3787 cx,
3788 )
3789 },
3790 ),
3791 },
3792 is_hoverable: tooltip_is_hoverable,
3793 }
3794 });
3795 *active_tooltip.borrow_mut() = new_tooltip;
3796 window.refresh();
3797 })
3798 .ok();
3799 }
3800 });
3801 active_tooltip
3802 .borrow_mut()
3803 .replace(ActiveTooltip::WaitingForShow {
3804 _task: delayed_show_task,
3805 });
3806 }
3807 Action::CheckVisible => cx.notify(current_view),
3808 }
3809}
3810
3811fn handle_tooltip_check_visible_and_update(
3815 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3816 tooltip_is_hoverable: bool,
3817 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3818 tooltip_bounds: Bounds<Pixels>,
3819 window: &mut Window,
3820 cx: &mut App,
3821) -> bool {
3822 enum Action {
3825 None,
3826 Hide,
3827 ScheduleHide(AnyTooltip),
3828 CancelHide(AnyTooltip),
3829 }
3830
3831 let is_hovered = check_is_hovered(window)
3832 || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position()));
3833 let action = match active_tooltip.borrow().as_ref() {
3834 Some(ActiveTooltip::Visible { tooltip, .. }) => {
3835 if is_hovered {
3836 Action::None
3837 } else {
3838 if tooltip_is_hoverable {
3839 Action::ScheduleHide(tooltip.clone())
3840 } else {
3841 Action::Hide
3842 }
3843 }
3844 }
3845 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => {
3846 if is_hovered {
3847 Action::CancelHide(tooltip.clone())
3848 } else {
3849 Action::None
3850 }
3851 }
3852 None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None,
3853 };
3854
3855 match action {
3856 Action::None => {}
3857 Action::Hide => clear_active_tooltip(active_tooltip, window),
3858 Action::ScheduleHide(tooltip) => {
3859 let delayed_hide_task = window.spawn(cx, {
3860 let weak_active_tooltip = Rc::downgrade(active_tooltip);
3861 async move |cx| {
3862 cx.background_executor()
3863 .timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
3864 .await;
3865 let Some(active_tooltip) = weak_active_tooltip.upgrade() else {
3866 return;
3867 };
3868 if active_tooltip.borrow_mut().take().is_some() {
3869 cx.update(|window, _cx| window.refresh()).ok();
3870 }
3871 }
3872 });
3873 active_tooltip
3874 .borrow_mut()
3875 .replace(ActiveTooltip::WaitingForHide {
3876 tooltip,
3877 _task: delayed_hide_task,
3878 });
3879 }
3880 Action::CancelHide(tooltip) => {
3881 active_tooltip.borrow_mut().replace(ActiveTooltip::Visible {
3883 tooltip,
3884 is_hoverable: true,
3885 });
3886 }
3887 }
3888
3889 active_tooltip.borrow().is_some()
3890}
3891
3892#[derive(Default)]
3893pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
3894
3895impl Global for GroupHitboxes {}
3896
3897impl GroupHitboxes {
3898 pub fn get(name: &SharedString, cx: &mut App) -> Option<HitboxId> {
3899 cx.default_global::<Self>()
3900 .0
3901 .get(name)
3902 .and_then(|bounds_stack| bounds_stack.last())
3903 .cloned()
3904 }
3905
3906 pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) {
3907 cx.default_global::<Self>()
3908 .0
3909 .entry(name)
3910 .or_default()
3911 .push(hitbox_id);
3912 }
3913
3914 pub fn pop(name: &SharedString, cx: &mut App) {
3915 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
3916 }
3917}
3918
3919pub struct Stateful<E> {
3921 pub(crate) element: E,
3922}
3923
3924impl<E> Styled for Stateful<E>
3925where
3926 E: Styled,
3927{
3928 fn style(&mut self) -> &mut StyleRefinement {
3929 self.element.style()
3930 }
3931}
3932
3933impl<E> StatefulInteractiveElement for Stateful<E>
3934where
3935 E: Element,
3936 Self: InteractiveElement,
3937{
3938}
3939
3940impl<E> InteractiveElement for Stateful<E>
3941where
3942 E: InteractiveElement,
3943{
3944 fn interactivity(&mut self) -> &mut Interactivity {
3945 self.element.interactivity()
3946 }
3947}
3948
3949impl<E> Element for Stateful<E>
3950where
3951 E: Element,
3952{
3953 type RequestLayoutState = E::RequestLayoutState;
3954 type PrepaintState = E::PrepaintState;
3955
3956 fn id(&self) -> Option<ElementId> {
3957 self.element.id()
3958 }
3959
3960 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
3961 self.element.source_location()
3962 }
3963
3964 fn a11y_role(&self) -> Option<accesskit::Role> {
3965 self.element.a11y_role()
3966 }
3967
3968 fn write_a11y_info(&self, node: &mut accesskit::Node) {
3969 self.element.write_a11y_info(node);
3970 }
3971
3972 fn a11y_synthetic_children(
3973 &mut self,
3974 prepaint: &mut Self::PrepaintState,
3975 builder: &mut crate::A11ySubtreeBuilder,
3976 ) {
3977 self.element.a11y_synthetic_children(prepaint, builder);
3978 }
3979
3980 fn request_layout(
3981 &mut self,
3982 id: Option<&GlobalElementId>,
3983 inspector_id: Option<&InspectorElementId>,
3984 window: &mut Window,
3985 cx: &mut App,
3986 ) -> (LayoutId, Self::RequestLayoutState) {
3987 self.element.request_layout(id, inspector_id, window, cx)
3988 }
3989
3990 fn prepaint(
3991 &mut self,
3992 id: Option<&GlobalElementId>,
3993 inspector_id: Option<&InspectorElementId>,
3994 bounds: Bounds<Pixels>,
3995 state: &mut Self::RequestLayoutState,
3996 window: &mut Window,
3997 cx: &mut App,
3998 ) -> E::PrepaintState {
3999 self.element
4000 .prepaint(id, inspector_id, bounds, state, window, cx)
4001 }
4002
4003 fn paint(
4004 &mut self,
4005 id: Option<&GlobalElementId>,
4006 inspector_id: Option<&InspectorElementId>,
4007 bounds: Bounds<Pixels>,
4008 request_layout: &mut Self::RequestLayoutState,
4009 prepaint: &mut Self::PrepaintState,
4010 window: &mut Window,
4011 cx: &mut App,
4012 ) {
4013 self.element.paint(
4014 id,
4015 inspector_id,
4016 bounds,
4017 request_layout,
4018 prepaint,
4019 window,
4020 cx,
4021 );
4022 }
4023}
4024
4025impl<E> IntoElement for Stateful<E>
4026where
4027 E: Element,
4028{
4029 type Element = Self;
4030
4031 fn into_element(self) -> Self::Element {
4032 self
4033 }
4034}
4035
4036impl<E> ParentElement for Stateful<E>
4037where
4038 E: ParentElement,
4039{
4040 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
4041 self.element.extend(elements)
4042 }
4043}
4044
4045#[derive(Clone)]
4048pub struct ScrollAnchor {
4049 handle: ScrollHandle,
4050 last_origin: Rc<RefCell<Point<Pixels>>>,
4051}
4052
4053impl ScrollAnchor {
4054 pub fn for_handle(handle: ScrollHandle) -> Self {
4056 Self {
4057 handle,
4058 last_origin: Default::default(),
4059 }
4060 }
4061 pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) {
4063 let this = self.clone();
4064
4065 window.on_next_frame(move |_, _| {
4066 let viewport_bounds = this.handle.bounds();
4067 let self_bounds = *this.last_origin.borrow();
4068 this.handle.set_offset(viewport_bounds.origin - self_bounds);
4069 });
4070 }
4071}
4072
4073#[derive(Default, Debug)]
4074struct ScrollHandleState {
4075 offset: Rc<RefCell<Point<Pixels>>>,
4076 ongoing_scroll: Rc<RefCell<OngoingScroll>>,
4077 bounds: Bounds<Pixels>,
4078 max_offset: Point<Pixels>,
4079 child_bounds: Vec<Bounds<Pixels>>,
4080 scroll_to_bottom: bool,
4081 overflow: Point<Overflow>,
4082 active_item: Option<ScrollActiveItem>,
4083}
4084
4085#[derive(Default, Debug, Clone, Copy)]
4086struct ScrollActiveItem {
4087 index: usize,
4088 strategy: ScrollStrategy,
4089}
4090
4091#[derive(Default, Debug, Clone, Copy)]
4092enum ScrollStrategy {
4093 #[default]
4094 FirstVisible,
4095 Top,
4096}
4097
4098#[derive(Clone, Debug)]
4102pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
4103
4104impl Default for ScrollHandle {
4105 fn default() -> Self {
4106 Self::new()
4107 }
4108}
4109
4110impl ScrollHandle {
4111 pub fn new() -> Self {
4113 Self(Rc::default())
4114 }
4115
4116 pub fn offset(&self) -> Point<Pixels> {
4118 *self.0.borrow().offset.borrow()
4119 }
4120
4121 pub fn max_offset(&self) -> Point<Pixels> {
4123 self.0.borrow().max_offset
4124 }
4125
4126 pub fn top_item(&self) -> usize {
4128 let state = self.0.borrow();
4129 let top = state.bounds.top() - state.offset.borrow().y;
4130
4131 match state.child_bounds.binary_search_by(|bounds| {
4132 if top < bounds.top() {
4133 Ordering::Greater
4134 } else if top > bounds.bottom() {
4135 Ordering::Less
4136 } else {
4137 Ordering::Equal
4138 }
4139 }) {
4140 Ok(ix) => ix,
4141 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
4142 }
4143 }
4144
4145 pub fn bottom_item(&self) -> usize {
4147 let state = self.0.borrow();
4148 let bottom = state.bounds.bottom() - state.offset.borrow().y;
4149
4150 match state.child_bounds.binary_search_by(|bounds| {
4151 if bottom < bounds.top() {
4152 Ordering::Greater
4153 } else if bottom > bounds.bottom() {
4154 Ordering::Less
4155 } else {
4156 Ordering::Equal
4157 }
4158 }) {
4159 Ok(ix) => ix,
4160 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
4161 }
4162 }
4163
4164 pub fn bounds(&self) -> Bounds<Pixels> {
4166 self.0.borrow().bounds
4167 }
4168
4169 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
4171 self.0.borrow().child_bounds.get(ix).cloned()
4172 }
4173
4174 pub fn scroll_to_item(&self, ix: usize) {
4176 let mut state = self.0.borrow_mut();
4177 state.active_item = Some(ScrollActiveItem {
4178 index: ix,
4179 strategy: ScrollStrategy::default(),
4180 });
4181 }
4182
4183 pub fn scroll_to_top_of_item(&self, ix: usize) {
4186 let mut state = self.0.borrow_mut();
4187 state.active_item = Some(ScrollActiveItem {
4188 index: ix,
4189 strategy: ScrollStrategy::Top,
4190 });
4191 }
4192
4193 fn scroll_to_active_item(&self) {
4197 let mut state = self.0.borrow_mut();
4198
4199 let Some(active_item) = state.active_item else {
4200 return;
4201 };
4202
4203 let active_item = match state.child_bounds.get(active_item.index) {
4204 Some(bounds) => {
4205 let mut scroll_offset = state.offset.borrow_mut();
4206
4207 match active_item.strategy {
4208 ScrollStrategy::FirstVisible => {
4209 if state.overflow.y == Overflow::Scroll {
4210 let child_height = bounds.size.height;
4211 let viewport_height = state.bounds.size.height;
4212 if child_height > viewport_height {
4213 scroll_offset.y = state.bounds.top() - bounds.top();
4214 } else if bounds.top() + scroll_offset.y < state.bounds.top() {
4215 scroll_offset.y = state.bounds.top() - bounds.top();
4216 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
4217 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
4218 }
4219 }
4220 }
4221 ScrollStrategy::Top => {
4222 scroll_offset.y = state.bounds.top() - bounds.top();
4223 }
4224 }
4225
4226 if state.overflow.x == Overflow::Scroll {
4227 let child_width = bounds.size.width;
4228 let viewport_width = state.bounds.size.width;
4229 if child_width > viewport_width {
4230 scroll_offset.x = state.bounds.left() - bounds.left();
4231 } else if bounds.left() + scroll_offset.x < state.bounds.left() {
4232 scroll_offset.x = state.bounds.left() - bounds.left();
4233 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
4234 scroll_offset.x = state.bounds.right() - bounds.right();
4235 }
4236 }
4237 None
4238 }
4239 None => Some(active_item),
4240 };
4241 state.active_item = active_item;
4242 }
4243
4244 pub fn scroll_to_bottom(&self) {
4246 let mut state = self.0.borrow_mut();
4247 state.scroll_to_bottom = true;
4248 }
4249
4250 pub fn set_offset(&self, mut position: Point<Pixels>) {
4254 let state = self.0.borrow();
4255 *state.offset.borrow_mut() = position;
4256 }
4257
4258 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
4260 let ix = self.top_item();
4261 let state = self.0.borrow();
4262
4263 if let Some(child_bounds) = state.child_bounds.get(ix) {
4264 (
4265 ix,
4266 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
4267 )
4268 } else {
4269 (ix, px(0.))
4270 }
4271 }
4272
4273 pub fn logical_scroll_bottom(&self) -> (usize, Pixels) {
4275 let ix = self.bottom_item();
4276 let state = self.0.borrow();
4277
4278 if let Some(child_bounds) = state.child_bounds.get(ix) {
4279 (
4280 ix,
4281 child_bounds.bottom() + state.offset.borrow().y - state.bounds.bottom(),
4282 )
4283 } else {
4284 (ix, px(0.))
4285 }
4286 }
4287
4288 pub fn children_count(&self) -> usize {
4290 self.0.borrow().child_bounds.len()
4291 }
4292}
4293
4294#[cfg(test)]
4295mod tests {
4296 use super::*;
4297 use crate::{
4298 AnyWindowHandle, AppContext as _, Context, InputEvent, Keystroke, MouseMoveEvent,
4299 TestAppContext, canvas, util::FluentBuilder as _,
4300 };
4301 use std::{cell::Cell, rc::Weak};
4302
4303 struct GroupHoverTestView {
4304 render_count: Rc<Cell<usize>>,
4305 anonymous_paint_count: Rc<Cell<usize>>,
4306 stateful_width: Rc<Cell<Pixels>>,
4307 }
4308
4309 impl Render for GroupHoverTestView {
4310 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4311 self.render_count.set(self.render_count.get() + 1);
4312 let anonymous_paint_count = self.anonymous_paint_count.clone();
4313 let stateful_width = self.stateful_width.clone();
4314 div().size_full().child(
4315 div()
4316 .ml(px(20.))
4317 .mt(px(20.))
4318 .size(px(50.))
4319 .relative()
4320 .group("hover-group")
4321 .child(
4322 div()
4323 .absolute()
4324 .size_full()
4325 .invisible()
4326 .group_hover("hover-group", |style| style.visible())
4327 .child(canvas(
4328 |_, _, _| {},
4329 move |_, _, _, _| {
4330 anonymous_paint_count.set(anonymous_paint_count.get() + 1)
4331 },
4332 )),
4333 )
4334 .child(
4335 div()
4336 .id("stateful-group-hover-target")
4337 .absolute()
4338 .top_0()
4339 .left_0()
4340 .size(px(10.))
4341 .group_hover("hover-group", |style| style.size(px(20.)))
4342 .child(canvas(
4343 move |bounds, _, _| stateful_width.set(bounds.size.width),
4344 |_, _, _, _| {},
4345 )),
4346 ),
4347 )
4348 }
4349 }
4350
4351 #[gpui::test]
4352 fn group_hover_styles_update_only_on_transitions(cx: &mut TestAppContext) {
4353 let render_count = Rc::new(Cell::new(0));
4354 let anonymous_paint_count = Rc::new(Cell::new(0));
4355 let stateful_width = Rc::new(Cell::new(px(0.)));
4356 let window = cx.add_window({
4357 let render_count = render_count.clone();
4358 let anonymous_paint_count = anonymous_paint_count.clone();
4359 let stateful_width = stateful_width.clone();
4360 move |_, _| GroupHoverTestView {
4361 render_count,
4362 anonymous_paint_count,
4363 stateful_width,
4364 }
4365 });
4366 let window = AnyWindowHandle::from(window);
4367
4368 cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx))
4369 .unwrap();
4370 assert_eq!(anonymous_paint_count.get(), 0);
4371 assert_eq!(stateful_width.get(), px(10.));
4372
4373 let move_mouse = |cx: &mut TestAppContext, position| {
4374 cx.update_window(window, |_, window, cx| {
4375 window.simulate_mouse_move(position, cx)
4376 })
4377 .unwrap();
4378 };
4379
4380 let initial_render_count = render_count.get();
4381 move_mouse(cx, point(px(25.), px(25.)));
4382 assert_eq!(render_count.get(), initial_render_count + 1);
4383 assert_eq!(anonymous_paint_count.get(), 1);
4384 assert_eq!(stateful_width.get(), px(20.));
4385
4386 move_mouse(cx, point(px(30.), px(30.)));
4387 assert_eq!(render_count.get(), initial_render_count + 1);
4388 assert_eq!(anonymous_paint_count.get(), 1);
4389 assert_eq!(stateful_width.get(), px(20.));
4390
4391 move_mouse(cx, point(px(5.), px(5.)));
4392 assert_eq!(render_count.get(), initial_render_count + 2);
4393 assert_eq!(anonymous_paint_count.get(), 1);
4394 assert_eq!(stateful_width.get(), px(10.));
4395
4396 move_mouse(cx, point(px(10.), px(10.)));
4397 assert_eq!(render_count.get(), initial_render_count + 2);
4398 assert_eq!(anonymous_paint_count.get(), 1);
4399 assert_eq!(stateful_width.get(), px(10.));
4400 }
4401
4402 struct TestTooltipView;
4403
4404 impl Render for TestTooltipView {
4405 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4406 div().w(px(20.)).h(px(20.)).child("tooltip")
4407 }
4408 }
4409
4410 type CapturedActiveTooltip = Rc<RefCell<Option<Weak<RefCell<Option<ActiveTooltip>>>>>>;
4411
4412 struct TooltipCaptureElement {
4413 child: AnyElement,
4414 captured_active_tooltip: CapturedActiveTooltip,
4415 }
4416
4417 impl IntoElement for TooltipCaptureElement {
4418 type Element = Self;
4419
4420 fn into_element(self) -> Self::Element {
4421 self
4422 }
4423 }
4424
4425 impl Element for TooltipCaptureElement {
4426 type RequestLayoutState = ();
4427 type PrepaintState = ();
4428
4429 fn id(&self) -> Option<ElementId> {
4430 None
4431 }
4432
4433 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
4434 None
4435 }
4436
4437 fn request_layout(
4438 &mut self,
4439 _id: Option<&GlobalElementId>,
4440 _inspector_id: Option<&InspectorElementId>,
4441 window: &mut Window,
4442 cx: &mut App,
4443 ) -> (LayoutId, Self::RequestLayoutState) {
4444 (self.child.request_layout(window, cx), ())
4445 }
4446
4447 fn prepaint(
4448 &mut self,
4449 _id: Option<&GlobalElementId>,
4450 _inspector_id: Option<&InspectorElementId>,
4451 _bounds: Bounds<Pixels>,
4452 _request_layout: &mut Self::RequestLayoutState,
4453 window: &mut Window,
4454 cx: &mut App,
4455 ) -> Self::PrepaintState {
4456 self.child.prepaint(window, cx);
4457 }
4458
4459 fn paint(
4460 &mut self,
4461 _id: Option<&GlobalElementId>,
4462 _inspector_id: Option<&InspectorElementId>,
4463 _bounds: Bounds<Pixels>,
4464 _request_layout: &mut Self::RequestLayoutState,
4465 _prepaint: &mut Self::PrepaintState,
4466 window: &mut Window,
4467 cx: &mut App,
4468 ) {
4469 self.child.paint(window, cx);
4470 window.with_global_id("target".into(), |global_id, window| {
4471 window.with_element_state::<InteractiveElementState, _>(
4472 global_id,
4473 |state, _window| {
4474 let state = state.unwrap();
4475 *self.captured_active_tooltip.borrow_mut() =
4476 state.active_tooltip.as_ref().map(Rc::downgrade);
4477 ((), state)
4478 },
4479 )
4480 });
4481 }
4482 }
4483
4484 struct TooltipOwner {
4485 captured_active_tooltip: CapturedActiveTooltip,
4486 show_delay_override: Option<Duration>,
4487 }
4488
4489 impl Render for TooltipOwner {
4490 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4491 TooltipCaptureElement {
4492 child: div()
4493 .size_full()
4494 .child(
4495 div()
4496 .id("target")
4497 .w(px(50.))
4498 .h(px(50.))
4499 .tooltip(|_, cx| cx.new(|_| TestTooltipView).into())
4500 .when_some(self.show_delay_override, |this, delay| {
4501 this.tooltip_show_delay(delay)
4502 }),
4503 )
4504 .into_any_element(),
4505 captured_active_tooltip: self.captured_active_tooltip.clone(),
4506 }
4507 }
4508 }
4509
4510 #[test]
4511 fn scroll_handle_aligns_wide_children_to_left_edge() {
4512 let handle = ScrollHandle::new();
4513 {
4514 let mut state = handle.0.borrow_mut();
4515 state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(80.), px(20.)));
4516 state.child_bounds = vec![Bounds::new(point(px(25.), px(0.)), size(px(200.), px(20.)))];
4517 state.overflow.x = Overflow::Scroll;
4518 state.active_item = Some(ScrollActiveItem {
4519 index: 0,
4520 strategy: ScrollStrategy::default(),
4521 });
4522 }
4523
4524 handle.scroll_to_active_item();
4525
4526 assert_eq!(handle.offset().x, px(-25.));
4527 }
4528
4529 #[test]
4530 fn scroll_handle_aligns_tall_children_to_top_edge() {
4531 let handle = ScrollHandle::new();
4532 {
4533 let mut state = handle.0.borrow_mut();
4534 state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(20.), px(80.)));
4535 state.child_bounds = vec![Bounds::new(point(px(0.), px(25.)), size(px(20.), px(200.)))];
4536 state.overflow.y = Overflow::Scroll;
4537 state.active_item = Some(ScrollActiveItem {
4538 index: 0,
4539 strategy: ScrollStrategy::default(),
4540 });
4541 }
4542
4543 handle.scroll_to_active_item();
4544
4545 assert_eq!(handle.offset().y, px(-25.));
4546 }
4547
4548 fn setup_tooltip_owner_test(
4549 show_delay_override: Option<Duration>,
4550 ) -> (
4551 TestAppContext,
4552 crate::AnyWindowHandle,
4553 CapturedActiveTooltip,
4554 ) {
4555 let mut test_app = TestAppContext::single();
4556 let captured_active_tooltip: CapturedActiveTooltip = Rc::new(RefCell::new(None));
4557 let window = test_app.add_window({
4558 let captured_active_tooltip = captured_active_tooltip.clone();
4559 move |_, _| TooltipOwner {
4560 captured_active_tooltip,
4561 show_delay_override,
4562 }
4563 });
4564 let any_window = window.into();
4565
4566 test_app
4567 .update_window(any_window, |_, window, cx| {
4568 window.draw(cx).clear(cx);
4569 })
4570 .unwrap();
4571
4572 test_app
4573 .update_window(any_window, |_, window, cx| {
4574 window.dispatch_event(
4575 MouseMoveEvent {
4576 position: point(px(10.), px(10.)),
4577 modifiers: Default::default(),
4578 pressed_button: None,
4579 }
4580 .to_platform_input(),
4581 cx,
4582 );
4583 })
4584 .unwrap();
4585
4586 test_app
4587 .update_window(any_window, |_, window, cx| {
4588 window.draw(cx).clear(cx);
4589 })
4590 .unwrap();
4591
4592 (test_app, any_window, captured_active_tooltip)
4593 }
4594
4595 #[test]
4596 fn tooltip_waiting_for_show_is_released_when_its_owner_disappears() {
4597 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None);
4598
4599 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4600 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4601 assert!(matches!(
4602 active_tooltip.borrow().as_ref(),
4603 Some(ActiveTooltip::WaitingForShow { .. })
4604 ));
4605
4606 test_app
4607 .update_window(any_window, |_, window, _| {
4608 window.remove_window();
4609 })
4610 .unwrap();
4611 test_app.run_until_parked();
4612 drop(active_tooltip);
4613
4614 assert!(weak_active_tooltip.upgrade().is_none());
4615 }
4616
4617 #[test]
4618 fn tooltip_respects_custom_show_delay() {
4619 let extra_delay = Duration::from_secs(1);
4620 let show_delay_override = DEFAULT_TOOLTIP_SHOW_DELAY + extra_delay;
4621 let (mut test_app, _any_window, captured_active_tooltip) =
4622 setup_tooltip_owner_test(Some(show_delay_override));
4623
4624 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4625 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4626
4627 test_app
4628 .dispatcher
4629 .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY);
4630 test_app.run_until_parked();
4631
4632 assert!(matches!(
4633 active_tooltip.borrow().as_ref(),
4634 Some(ActiveTooltip::WaitingForShow { .. })
4635 ));
4636
4637 test_app.dispatcher.advance_clock(extra_delay);
4638 test_app.run_until_parked();
4639
4640 assert!(matches!(
4641 active_tooltip.borrow().as_ref(),
4642 Some(ActiveTooltip::Visible { .. })
4643 ));
4644 }
4645
4646 #[test]
4647 fn tooltip_is_released_when_its_owner_disappears() {
4648 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None);
4649
4650 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4651 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4652
4653 test_app
4654 .dispatcher
4655 .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY);
4656 test_app.run_until_parked();
4657
4658 assert!(matches!(
4659 active_tooltip.borrow().as_ref(),
4660 Some(ActiveTooltip::Visible { .. })
4661 ));
4662
4663 test_app
4664 .update_window(any_window, |_, window, _| {
4665 window.remove_window();
4666 })
4667 .unwrap();
4668 test_app.run_until_parked();
4669 drop(active_tooltip);
4670
4671 assert!(weak_active_tooltip.upgrade().is_none());
4672 }
4673
4674 #[test]
4675 fn tooltip_hides_after_mouse_leaves_origin() {
4676 let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None);
4677
4678 let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap();
4679 let active_tooltip = weak_active_tooltip.upgrade().unwrap();
4680
4681 test_app
4682 .dispatcher
4683 .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY);
4684 test_app.run_until_parked();
4685
4686 assert!(matches!(
4687 active_tooltip.borrow().as_ref(),
4688 Some(ActiveTooltip::Visible { .. })
4689 ));
4690
4691 test_app
4692 .update_window(any_window, |_, window, cx| {
4693 window.dispatch_event(
4694 MouseMoveEvent {
4695 position: point(px(75.), px(75.)),
4696 modifiers: Default::default(),
4697 pressed_button: None,
4698 }
4699 .to_platform_input(),
4700 cx,
4701 );
4702 })
4703 .unwrap();
4704
4705 assert!(active_tooltip.borrow().is_none());
4706 }
4707
4708 struct MouseDownOutOwner {
4709 mouse_down_out_count: Rc<RefCell<usize>>,
4710 }
4711
4712 impl Render for MouseDownOutOwner {
4713 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4714 let mouse_down_out_count = self.mouse_down_out_count.clone();
4715 div()
4716 .size_full()
4717 .child(div().id("target").w(px(50.)).h(px(50.)).on_mouse_down_out(
4718 move |_, _, _| {
4719 *mouse_down_out_count.borrow_mut() += 1;
4720 },
4721 ))
4722 }
4723 }
4724
4725 #[test]
4726 fn mouse_down_out_is_suppressed_while_window_prompt_is_active() {
4727 let mut test_app = TestAppContext::single();
4728 let mouse_down_out_count = Rc::new(RefCell::new(0));
4729 let window = test_app.add_window({
4730 let mouse_down_out_count = mouse_down_out_count.clone();
4731 move |_, _| MouseDownOutOwner {
4732 mouse_down_out_count,
4733 }
4734 });
4735 let any_window: AnyWindowHandle = window.into();
4736
4737 fn dispatch_mouse_down_outside_target(
4738 test_app: &mut TestAppContext,
4739 any_window: AnyWindowHandle,
4740 ) {
4741 test_app
4742 .update_window(any_window, |_, window, cx| {
4743 window.dispatch_event(
4744 MouseDownEvent {
4745 position: point(px(75.), px(75.)),
4746 button: MouseButton::Left,
4747 modifiers: Default::default(),
4748 click_count: 1,
4749 first_mouse: false,
4750 }
4751 .to_platform_input(),
4752 cx,
4753 );
4754 })
4755 .unwrap();
4756 }
4757
4758 test_app
4759 .update_window(any_window, |_, window, cx| {
4760 window.draw(cx).clear(cx);
4761 })
4762 .unwrap();
4763
4764 dispatch_mouse_down_outside_target(&mut test_app, any_window);
4765 assert_eq!(
4766 *mouse_down_out_count.borrow(),
4767 1,
4768 "mouse down outside the element should fire mouse-down-out listeners"
4769 );
4770
4771 test_app
4772 .update_window(any_window, |_, window, cx| {
4773 cx.set_prompt_builder(crate::fallback_prompt_renderer);
4774 let _receiver =
4775 window.prompt(crate::PromptLevel::Warning, "message", None, &["Ok"], cx);
4776 assert!(window.has_active_prompt());
4777 window.draw(cx).clear(cx);
4778 })
4779 .unwrap();
4780
4781 dispatch_mouse_down_outside_target(&mut test_app, any_window);
4782 assert_eq!(
4783 *mouse_down_out_count.borrow(),
4784 1,
4785 "mouse down over an active prompt should not fire mouse-down-out listeners"
4786 );
4787 }
4788
4789 #[test]
4790 fn test_write_a11y_info_string_and_numeric_properties() {
4791 let mut interactivity = Interactivity::default();
4792 interactivity.aria.label = Some("Buffer Font Size".into());
4793 interactivity.aria.value = Some("15".into());
4794 interactivity.aria.placeholder = Some("Search".into());
4795 interactivity.aria.numeric_value = Some(15.0);
4796 interactivity.aria.min_numeric_value = Some(6.0);
4797 interactivity.aria.max_numeric_value = Some(72.0);
4798 interactivity.aria.numeric_value_step = Some(1.0);
4799
4800 let mut node = accesskit::Node::new(accesskit::Role::SpinButton);
4801 interactivity.write_a11y_info(&mut node);
4802
4803 assert_eq!(node.label(), Some("Buffer Font Size"));
4804 assert_eq!(node.value(), Some("15"));
4805 assert_eq!(node.placeholder(), Some("Search"));
4806 assert_eq!(node.numeric_value(), Some(15.0));
4807 assert_eq!(node.min_numeric_value(), Some(6.0));
4808 assert_eq!(node.max_numeric_value(), Some(72.0));
4809 assert_eq!(node.numeric_value_step(), Some(1.0));
4810 }
4811
4812 struct KeyboardActivationTest {
4815 focus_a: FocusHandle,
4816 focus_b: FocusHandle,
4817 clicks: Rc<RefCell<Vec<&'static str>>>,
4818 }
4819
4820 impl Render for KeyboardActivationTest {
4821 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4822 let clicks_a = self.clicks.clone();
4823 let clicks_b = self.clicks.clone();
4824 div()
4825 .size_full()
4826 .child(
4827 div()
4828 .id("a")
4829 .w(px(50.))
4830 .h(px(50.))
4831 .track_focus(&self.focus_a)
4832 .on_click(move |_, _, _| clicks_a.borrow_mut().push("a")),
4833 )
4834 .child(
4835 div()
4836 .id("b")
4837 .w(px(50.))
4838 .h(px(50.))
4839 .track_focus(&self.focus_b)
4840 .on_click(move |_, _, _| clicks_b.borrow_mut().push("b")),
4841 )
4842 }
4843 }
4844
4845 fn setup_keyboard_activation_test() -> (
4846 TestAppContext,
4847 AnyWindowHandle,
4848 Rc<RefCell<Vec<&'static str>>>,
4849 FocusHandle,
4850 FocusHandle,
4851 ) {
4852 let mut cx = TestAppContext::single();
4853 let (focus_a, focus_b) = cx.update(|cx| (cx.focus_handle(), cx.focus_handle()));
4854 let clicks: Rc<RefCell<Vec<&'static str>>> = Rc::new(RefCell::new(Vec::new()));
4855 let window = cx.add_window({
4856 let focus_a = focus_a.clone();
4857 let focus_b = focus_b.clone();
4858 let clicks = clicks.clone();
4859 move |_, _| KeyboardActivationTest {
4860 focus_a,
4861 focus_b,
4862 clicks,
4863 }
4864 });
4865 (cx, window.into(), clicks, focus_a, focus_b)
4866 }
4867
4868 fn focus_and_draw(cx: &mut TestAppContext, window: AnyWindowHandle, handle: &FocusHandle) {
4871 cx.update_window(window, |_, window, cx| window.focus(handle, cx))
4872 .unwrap();
4873 cx.run_until_parked();
4874 cx.update_window(window, |_, window, cx| {
4875 window.draw(cx).clear(cx);
4876 })
4877 .unwrap();
4878 }
4879
4880 fn key_down(cx: &mut TestAppContext, window: AnyWindowHandle, key: &str) {
4881 let keystroke = Keystroke::parse(key).unwrap();
4882 cx.update_window(window, |_, window, cx| {
4883 window.dispatch_event(
4884 KeyDownEvent {
4885 keystroke,
4886 is_held: false,
4887 prefer_character_input: false,
4888 }
4889 .to_platform_input(),
4890 cx,
4891 );
4892 })
4893 .unwrap();
4894 }
4895
4896 fn key_up(cx: &mut TestAppContext, window: AnyWindowHandle, key: &str) {
4897 let keystroke = Keystroke::parse(key).unwrap();
4898 cx.update_window(window, |_, window, cx| {
4899 window.dispatch_event(KeyUpEvent { keystroke }.to_platform_input(), cx);
4900 })
4901 .unwrap();
4902 }
4903
4904 #[test]
4906 fn keyboard_activation_fires_click_on_same_element() {
4907 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
4908
4909 focus_and_draw(&mut cx, window, &focus_a);
4910 key_down(&mut cx, window, "enter");
4911 key_up(&mut cx, window, "enter");
4912
4913 assert_eq!(*clicks.borrow(), vec!["a"]);
4914 }
4915
4916 #[test]
4921 fn keyboard_activation_does_not_leak_across_focus_change() {
4922 let (mut cx, window, clicks, focus_a, focus_b) = setup_keyboard_activation_test();
4923
4924 focus_and_draw(&mut cx, window, &focus_a);
4926 key_down(&mut cx, window, "enter");
4927
4928 focus_and_draw(&mut cx, window, &focus_b);
4930 key_up(&mut cx, window, "enter");
4931
4932 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
4935 }
4936
4937 #[test]
4942 fn keyboard_activation_does_not_leak_when_focus_returns() {
4943 let (mut cx, window, clicks, focus_a, focus_b) = setup_keyboard_activation_test();
4944
4945 focus_and_draw(&mut cx, window, &focus_a);
4947 key_down(&mut cx, window, "enter");
4948
4949 focus_and_draw(&mut cx, window, &focus_b);
4952 focus_and_draw(&mut cx, window, &focus_a);
4953 key_up(&mut cx, window, "enter");
4954
4955 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
4957 }
4958
4959 #[test]
4964 fn keyboard_activation_cleared_by_intervening_key_release() {
4965 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
4966
4967 focus_and_draw(&mut cx, window, &focus_a);
4968 key_down(&mut cx, window, "escape");
4969 key_down(&mut cx, window, "space");
4970 key_up(&mut cx, window, "escape");
4971 key_up(&mut cx, window, "space");
4972
4973 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
4974 }
4975
4976 #[test]
4980 fn keyboard_activation_does_not_distinguish_space_and_enter() {
4981 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
4982
4983 focus_and_draw(&mut cx, window, &focus_a);
4984 key_down(&mut cx, window, "space");
4985 key_up(&mut cx, window, "enter");
4986
4987 assert_eq!(*clicks.borrow(), vec!["a"]);
4988 }
4989
4990 #[test]
4993 fn keyboard_activation_cleared_by_intervening_keydown() {
4994 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
4995
4996 focus_and_draw(&mut cx, window, &focus_a);
4997 key_down(&mut cx, window, "enter");
4998 key_down(&mut cx, window, "a");
4999 key_up(&mut cx, window, "enter");
5000
5001 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
5002 }
5003
5004 #[test]
5007 fn keyboard_activation_ignores_modified_keys() {
5008 let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test();
5009
5010 focus_and_draw(&mut cx, window, &focus_a);
5011 key_down(&mut cx, window, "cmd-enter");
5012 key_up(&mut cx, window, "cmd-enter");
5013
5014 assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow());
5015 }
5016
5017 struct TabGroupFocus {
5021 group_a: FocusHandle,
5022 item_a: FocusHandle,
5023 group_b: FocusHandle,
5024 item_b: FocusHandle,
5025 }
5026
5027 impl Render for TabGroupFocus {
5028 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
5029 fn group(container: &FocusHandle, item: &FocusHandle) -> Div {
5030 div()
5031 .track_focus(container)
5032 .tab_group()
5033 .child(div().track_focus(item))
5034 }
5035 div()
5036 .child(group(&self.group_a, &self.item_a))
5037 .child(group(&self.group_b, &self.item_b))
5038 }
5039 }
5040
5041 #[test]
5045 fn focus_next_from_tab_group_container_enters_that_group() {
5046 let mut cx = TestAppContext::single();
5047 let (group_a, item_a, group_b, item_b) = cx.update(|cx| {
5048 (
5049 cx.focus_handle(),
5050 cx.focus_handle().tab_stop(true),
5051 cx.focus_handle(),
5052 cx.focus_handle().tab_stop(true),
5053 )
5054 });
5055 let window: AnyWindowHandle = cx
5056 .add_window({
5057 let (group_a, item_a, group_b, item_b) =
5058 (group_a, item_a, group_b.clone(), item_b.clone());
5059 move |_, _| TabGroupFocus {
5060 group_a,
5061 item_a,
5062 group_b,
5063 item_b,
5064 }
5065 })
5066 .into();
5067 cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx))
5068 .unwrap();
5069
5070 let focused = cx
5072 .update_window(window, |_, window, cx| {
5073 window.focus(&group_b, cx);
5074 window.focus_next(cx);
5075 window.focused(cx).map(|handle| handle.id)
5076 })
5077 .unwrap();
5078
5079 assert_eq!(focused, Some(item_b.id));
5080 }
5081}