Skip to main content

gpui_core/elements/
div.rs

1//! Div is the central, reusable element that most GPUI trees will be built from.
2//! It functions as a container for other elements, and provides a number of
3//! useful features for laying out and styling its children as well as binding
4//! mouse events and action handlers. It is meant to be similar to the HTML `<div>`
5//! element, but for GPUI.
6//!
7//! # Build your own div
8//!
9//! GPUI does not directly provide APIs for stateful, multi step events like `click`
10//! and `drag`. We want GPUI users to be able to build their own abstractions for
11//! their own needs. However, as a UI framework, we're also obliged to provide some
12//! building blocks to make the process of building your own elements easier.
13//! For this we have the [`Interactivity`] and the [`StyleRefinement`] structs, as well
14//! as several associated traits. Together, these provide the full suite of Dom-like events
15//! and Tailwind-like styling that you can use to build your own custom elements. Div is
16//! constructed by combining these two systems into an all-in-one element.
17
18use crate::{
19    AbsoluteLength, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent,
20    DispatchPhase, Element, ElementId, Entity, EntityId, FocusHandle, Global, GlobalElementId,
21    Hitbox, HitboxBehavior, HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext,
22    KeyDownEvent, KeyUpEvent, KeyboardButton, KeyboardClickEvent, LayoutId, ModifiersChangedEvent,
23    MouseButton, MouseClickEvent, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent,
24    OngoingScroll, Overflow, ParentElement, PinchEvent, Pixels, Point, Render, ScrollWheelEvent,
25    SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId, Visibility, Window,
26    WindowControlArea, point, px, size,
27};
28use collections::HashMap;
29use refineable::Refineable;
30use smallvec::SmallVec;
31use stacksafe::{StackSafe, stacksafe};
32use std::{
33    any::{Any, TypeId},
34    cell::RefCell,
35    cmp::Ordering,
36    fmt::Debug,
37    marker::PhantomData,
38    mem,
39    rc::Rc,
40    sync::Arc,
41    time::Duration,
42};
43use util::ResultExt;
44
45use super::ImageCacheProvider;
46
47const DRAG_THRESHOLD: f64 = 2.;
48const DEFAULT_TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);
49const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500);
50
51/// The styling information for a given group.
52pub struct GroupStyle {
53    /// The identifier for this group.
54    pub group: SharedString,
55
56    /// The specific style refinement that this group would apply
57    /// to its children.
58    pub style: Box<StyleRefinement>,
59}
60
61/// An event for when a drag is moving over this element, with the given state type.
62pub struct DragMoveEvent<T> {
63    /// The mouse move event that triggered this drag move event.
64    pub event: MouseMoveEvent,
65
66    /// The bounds of this element.
67    pub bounds: Bounds<Pixels>,
68    drag: PhantomData<T>,
69    dragged_item: Arc<dyn Any>,
70}
71
72impl<T: 'static> DragMoveEvent<T> {
73    /// Returns the drag state for this event.
74    pub fn drag<'b>(&self, cx: &'b App) -> &'b T {
75        cx.active_drag
76            .as_ref()
77            .and_then(|drag| drag.value.downcast_ref::<T>())
78            .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
79    }
80
81    /// An item that is about to be dropped.
82    pub fn dragged_item(&self) -> &dyn Any {
83        self.dragged_item.as_ref()
84    }
85}
86
87/// An event that fires when an element's bounds change size.
88#[derive(Clone, Debug)]
89pub struct ResizeEvent {
90    /// The new size of the element.
91    pub size: Size<Pixels>,
92    /// The new bounds of the element.
93    pub bounds: Bounds<Pixels>,
94}
95
96impl Interactivity {
97    /// Create an `Interactivity`, capturing the caller location in debug mode.
98    #[cfg(any(feature = "inspector", debug_assertions))]
99    #[track_caller]
100    pub fn new() -> Interactivity {
101        Interactivity {
102            source_location: Some(core::panic::Location::caller()),
103            ..Default::default()
104        }
105    }
106
107    /// Create an `Interactivity`, capturing the caller location in debug mode.
108    #[cfg(not(any(feature = "inspector", debug_assertions)))]
109    pub fn new() -> Interactivity {
110        Interactivity::default()
111    }
112
113    /// Gets the source location of construction. Returns `None` when not in debug mode.
114    pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
115        #[cfg(any(feature = "inspector", debug_assertions))]
116        {
117            self.source_location
118        }
119
120        #[cfg(not(any(feature = "inspector", debug_assertions)))]
121        {
122            None
123        }
124    }
125
126    /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase
127    /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]
128    ///
129    /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback.
130    pub fn on_mouse_down(
131        &mut self,
132        button: MouseButton,
133        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
134    ) {
135        self.mouse_down_listeners
136            .push(Box::new(move |event, phase, hitbox, window, cx| {
137                if phase == DispatchPhase::Bubble
138                    && event.button == button
139                    && hitbox.is_hovered(window)
140                {
141                    (listener)(event, window, cx)
142                }
143            }));
144    }
145
146    /// Bind the given callback to the mouse down event for any button, during the capture phase
147    /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]
148    ///
149    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
150    pub fn capture_any_mouse_down(
151        &mut self,
152        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
153    ) {
154        self.mouse_down_listeners
155            .push(Box::new(move |event, phase, hitbox, window, cx| {
156                if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
157                    (listener)(event, window, cx)
158                }
159            }));
160    }
161
162    /// Bind the given callback to the mouse down event for any button, during the bubble phase
163    /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]
164    ///
165    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
166    pub fn on_any_mouse_down(
167        &mut self,
168        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
169    ) {
170        self.mouse_down_listeners
171            .push(Box::new(move |event, phase, hitbox, window, cx| {
172                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
173                    (listener)(event, window, cx)
174                }
175            }));
176    }
177
178    /// Bind the given callback to the mouse up event for the given button, during the bubble phase
179    /// the imperative API equivalent to [`InteractiveElement::on_mouse_up`]
180    ///
181    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
182    pub fn on_mouse_up(
183        &mut self,
184        button: MouseButton,
185        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
186    ) {
187        self.mouse_up_listeners
188            .push(Box::new(move |event, phase, hitbox, window, cx| {
189                if phase == DispatchPhase::Bubble
190                    && event.button == button
191                    && hitbox.is_hovered(window)
192                {
193                    (listener)(event, window, cx)
194                }
195            }));
196    }
197
198    /// Bind the given callback to the mouse up event for any button, during the capture phase
199    /// the imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]
200    ///
201    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
202    pub fn capture_any_mouse_up(
203        &mut self,
204        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
205    ) {
206        self.mouse_up_listeners
207            .push(Box::new(move |event, phase, hitbox, window, cx| {
208                if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
209                    (listener)(event, window, cx)
210                }
211            }));
212    }
213
214    /// Bind the given callback to the mouse up event for any button, during the bubble phase
215    /// the imperative API equivalent to [`Interactivity::on_any_mouse_up`]
216    ///
217    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
218    pub fn on_any_mouse_up(
219        &mut self,
220        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
221    ) {
222        self.mouse_up_listeners
223            .push(Box::new(move |event, phase, hitbox, window, cx| {
224                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
225                    (listener)(event, window, cx)
226                }
227            }));
228    }
229
230    /// Bind the given callback to the mouse down event, on any button, during the capture phase,
231    /// when the mouse is outside of the bounds of this element.
232    /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]
233    ///
234    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
235    pub fn on_mouse_down_out(
236        &mut self,
237        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
238    ) {
239        self.mouse_down_listeners
240            .push(Box::new(move |event, phase, hitbox, window, cx| {
241                if phase == DispatchPhase::Capture
242                    && !window.has_active_prompt()
243                    && !hitbox.contains(&window.mouse_position())
244                {
245                    (listener)(event, window, cx)
246                }
247            }));
248    }
249
250    /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
251    /// when the mouse is outside of the bounds of this element.
252    /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]
253    ///
254    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
255    pub fn on_mouse_up_out(
256        &mut self,
257        button: MouseButton,
258        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
259    ) {
260        self.mouse_up_listeners
261            .push(Box::new(move |event, phase, hitbox, window, cx| {
262                if phase == DispatchPhase::Capture
263                    && event.button == button
264                    && !hitbox.is_hovered(window)
265                {
266                    (listener)(event, window, cx);
267                }
268            }));
269    }
270
271    /// Bind the given callback to the mouse move event, during the bubble phase
272    /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]
273    ///
274    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
275    pub fn on_mouse_move(
276        &mut self,
277        listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
278    ) {
279        self.mouse_move_listeners
280            .push(Box::new(move |event, phase, hitbox, window, cx| {
281                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
282                    (listener)(event, window, cx);
283                }
284            }));
285    }
286
287    /// Bind the given callback to the mouse exit event, during the bubble phase.
288    /// The imperative API equivalent to [`InteractiveElement::on_mouse_exit`].
289    ///
290    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
291    pub fn on_mouse_exit(
292        &mut self,
293        listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static,
294    ) {
295        self.mouse_exit_listeners
296            .push(Box::new(move |event, phase, hitbox, window, cx| {
297                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
298                    (listener)(event, window, cx);
299                }
300            }));
301    }
302
303    /// Bind the given callback to the mouse drag event of the given type. Note that this
304    /// will be called for all move events, inside or outside of this element, as long as the
305    /// drag was started with this element under the mouse. Useful for implementing draggable
306    /// UIs that don't conform to a drag and drop style interaction, like resizing.
307    /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]
308    ///
309    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
310    pub fn on_drag_move<T>(
311        &mut self,
312        listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
313    ) where
314        T: 'static,
315    {
316        self.mouse_move_listeners
317            .push(Box::new(move |event, phase, hitbox, window, cx| {
318                if phase == DispatchPhase::Capture
319                    && let Some(drag) = &cx.active_drag
320                    && drag.value.as_ref().type_id() == TypeId::of::<T>()
321                {
322                    (listener)(
323                        &DragMoveEvent {
324                            event: event.clone(),
325                            bounds: hitbox.bounds,
326                            drag: PhantomData,
327                            dragged_item: Arc::clone(&drag.value),
328                        },
329                        window,
330                        cx,
331                    );
332                }
333            }));
334    }
335
336    /// Bind the given callback to scroll wheel events during the bubble phase
337    /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]
338    ///
339    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
340    pub fn on_scroll_wheel(
341        &mut self,
342        listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
343    ) {
344        self.scroll_wheel_listeners
345            .push(Box::new(move |event, phase, hitbox, window, cx| {
346                if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
347                    (listener)(event, window, cx);
348                }
349            }));
350    }
351
352    /// Bind the given callback to pinch gesture events during the bubble phase.
353    /// The imperative API equivalent to [`InteractiveElement::on_pinch`].
354    ///
355    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
356    pub fn on_pinch(&mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) {
357        self.pinch_listeners
358            .push(Box::new(move |event, phase, hitbox, window, cx| {
359                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
360                    (listener)(event, window, cx);
361                }
362            }));
363    }
364
365    /// Bind the given callback to pinch gesture events during the capture phase.
366    /// The imperative API equivalent to [`InteractiveElement::capture_pinch`].
367    ///
368    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
369    pub fn capture_pinch(
370        &mut self,
371        listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
372    ) {
373        self.pinch_listeners
374            .push(Box::new(move |event, phase, _hitbox, window, cx| {
375                if phase == DispatchPhase::Capture {
376                    (listener)(event, window, cx);
377                } else {
378                    cx.propagate();
379                }
380            }));
381    }
382
383    /// Bind the given callback to an action dispatch during the capture phase
384    /// The imperative API equivalent to [`InteractiveElement::capture_action`]
385    ///
386    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
387    pub fn capture_action<A: Action>(
388        &mut self,
389        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
390    ) {
391        self.action_listeners.push((
392            TypeId::of::<A>(),
393            Box::new(move |action, phase, window, cx| {
394                let action = action.downcast_ref().unwrap();
395                if phase == DispatchPhase::Capture {
396                    (listener)(action, window, cx)
397                } else {
398                    cx.propagate();
399                }
400            }),
401        ));
402    }
403
404    /// Bind the given callback to an action dispatch during the bubble phase
405    /// The imperative API equivalent to [`InteractiveElement::on_action`]
406    ///
407    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
408    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {
409        self.action_listeners.push((
410            TypeId::of::<A>(),
411            Box::new(move |action, phase, window, cx| {
412                let action = action.downcast_ref().unwrap();
413                if phase == DispatchPhase::Bubble {
414                    (listener)(action, window, cx)
415                }
416            }),
417        ));
418    }
419
420    /// Bind the given callback to an action dispatch, based on a dynamic action parameter
421    /// instead of a type parameter. Useful for component libraries that want to expose
422    /// action bindings to their users.
423    /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]
424    ///
425    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
426    pub fn on_boxed_action(
427        &mut self,
428        action: &dyn Action,
429        listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
430    ) {
431        let action = action.boxed_clone();
432        self.action_listeners.push((
433            (*action).type_id(),
434            Box::new(move |_, phase, window, cx| {
435                if phase == DispatchPhase::Bubble {
436                    (listener)(&*action, window, cx)
437                }
438            }),
439        ));
440    }
441
442    /// Bind the given callback to key down events during the bubble phase
443    /// The imperative API equivalent to [`InteractiveElement::on_key_down`]
444    ///
445    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
446    pub fn on_key_down(
447        &mut self,
448        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
449    ) {
450        self.key_down_listeners
451            .push(Box::new(move |event, phase, window, cx| {
452                if phase == DispatchPhase::Bubble {
453                    (listener)(event, window, cx)
454                }
455            }));
456    }
457
458    /// Bind the given callback to key down events during the capture phase
459    /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]
460    ///
461    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
462    pub fn capture_key_down(
463        &mut self,
464        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
465    ) {
466        self.key_down_listeners
467            .push(Box::new(move |event, phase, window, cx| {
468                if phase == DispatchPhase::Capture {
469                    listener(event, window, cx)
470                }
471            }));
472    }
473
474    /// Bind the given callback to key up events during the bubble phase
475    /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
476    ///
477    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
478    pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {
479        self.key_up_listeners
480            .push(Box::new(move |event, phase, window, cx| {
481                if phase == DispatchPhase::Bubble {
482                    listener(event, window, cx)
483                }
484            }));
485    }
486
487    /// Bind the given callback to key up events during the capture phase
488    /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
489    ///
490    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
491    pub fn capture_key_up(
492        &mut self,
493        listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
494    ) {
495        self.key_up_listeners
496            .push(Box::new(move |event, phase, window, cx| {
497                if phase == DispatchPhase::Capture {
498                    listener(event, window, cx)
499                }
500            }));
501    }
502
503    /// Bind the given callback to modifiers changing events.
504    /// The imperative API equivalent to [`InteractiveElement::on_modifiers_changed`]
505    ///
506    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
507    pub fn on_modifiers_changed(
508        &mut self,
509        listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
510    ) {
511        self.modifiers_changed_listeners
512            .push(Box::new(move |event, window, cx| {
513                listener(event, window, cx)
514            }));
515    }
516
517    /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
518    /// The imperative API equivalent to [`InteractiveElement::on_drop`]
519    ///
520    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
521    pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {
522        self.drop_listeners.push((
523            TypeId::of::<T>(),
524            Box::new(move |dragged_value, window, cx| {
525                listener(dragged_value.downcast_ref().unwrap(), window, cx);
526            }),
527        ));
528    }
529
530    /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
531    /// The imperative API equivalent to [`InteractiveElement::can_drop`]
532    pub fn can_drop(
533        &mut self,
534        predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
535    ) {
536        self.can_drop_predicate = Some(Box::new(predicate));
537    }
538
539    /// Bind the given callback to click events of this element
540    /// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]
541    ///
542    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
543    pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
544    where
545        Self: Sized,
546    {
547        self.click_listeners.push(Rc::new(move |event, window, cx| {
548            listener(event, window, cx)
549        }));
550    }
551
552    /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
553    /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
554    /// the [`Self::on_drag_move`] API
555    /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
556    ///
557    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
558    pub fn on_drag<T, W>(
559        &mut self,
560        value: T,
561        constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
562    ) where
563        Self: Sized,
564        T: 'static,
565        W: 'static + Render,
566    {
567        debug_assert!(
568            self.drag_listener.is_none(),
569            "calling on_drag more than once on the same element is not supported"
570        );
571        self.drag_listener = Some((
572            Arc::new(value),
573            Box::new(move |value, offset, window, cx| {
574                constructor(value.downcast_ref().unwrap(), offset, window, cx).into()
575            }),
576        ));
577    }
578
579    /// Bind the given callback on the hover start and end events of this element. Note that the boolean
580    /// passed to the callback is true when the hover starts and false when it ends.
581    /// Transitions caused by layout changes under a stationary mouse also invoke the callback.
582    /// The imperative API equivalent to [`StatefulInteractiveElement::on_hover`]
583    ///
584    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
585    pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static)
586    where
587        Self: Sized,
588    {
589        debug_assert!(
590            self.hover_listener.is_none(),
591            "calling on_hover more than once on the same element is not supported"
592        );
593        self.hover_listener = Some(Box::new(listener));
594    }
595
596    /// Bind the given callback to be called when this element's bounds change size.
597    /// The imperative API equivalent of [`StatefulInteractiveElement::on_resize`]
598    ///
599    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
600    pub fn on_resize(&mut self, listener: impl Fn(&ResizeEvent, &mut Window, &mut App) + 'static)
601    where
602        Self: Sized,
603    {
604        self.resize_listeners.push(Box::new(listener));
605    }
606
607    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
608    /// The imperative API equivalent to [`StatefulInteractiveElement::tooltip`]
609    pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static)
610    where
611        Self: Sized,
612    {
613        debug_assert!(
614            self.tooltip_builder.is_none(),
615            "calling tooltip more than once on the same element is not supported"
616        );
617        self.tooltip_builder = Some(TooltipBuilder {
618            build: Rc::new(build_tooltip),
619            hoverable: false,
620        });
621    }
622
623    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
624    /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into
625    /// the tooltip. The imperative API equivalent to [`StatefulInteractiveElement::hoverable_tooltip`]
626    pub fn hoverable_tooltip(
627        &mut self,
628        build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
629    ) where
630        Self: Sized,
631    {
632        debug_assert!(
633            self.tooltip_builder.is_none(),
634            "calling tooltip more than once on the same element is not supported"
635        );
636        self.tooltip_builder = Some(TooltipBuilder {
637            build: Rc::new(build_tooltip),
638            hoverable: true,
639        });
640    }
641
642    /// Set the delay before this element's tooltip is shown.
643    /// The imperative API equivalent to [`StatefulInteractiveElement::tooltip_show_delay`]
644    pub fn tooltip_show_delay(&mut self, delay: Duration) {
645        self.tooltip_show_delay = Some(delay);
646    }
647
648    /// Block the mouse from all interactions with elements behind this element's hitbox. Typically
649    /// `block_mouse_except_scroll` should be preferred.
650    ///
651    /// The imperative API equivalent to [`InteractiveElement::occlude`]
652    pub fn occlude_mouse(&mut self) {
653        self.hitbox_behavior = HitboxBehavior::BlockMouse;
654    }
655
656    /// Set the bounds of this element as a window control area for the platform window.
657    /// The imperative API equivalent to [`InteractiveElement::window_control_area`]
658    pub fn window_control_area(&mut self, area: WindowControlArea) {
659        self.window_control = Some(area);
660    }
661
662    /// Block non-scroll mouse interactions with elements behind this element's hitbox. See
663    /// [`Hitbox::is_hovered`] for details.
664    ///
665    /// The imperative API equivalent to [`InteractiveElement::block_mouse_except_scroll`]
666    pub fn block_mouse_except_scroll(&mut self) {
667        self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll;
668    }
669}
670
671/// A trait for elements that want to use the standard GPUI event handlers that don't
672/// require any state.
673pub trait InteractiveElement: Sized {
674    /// Retrieve the interactivity state associated with this element
675    fn interactivity(&mut self) -> &mut Interactivity;
676
677    /// Assign this element to a group of elements that can be styled together
678    fn group(mut self, group: impl Into<SharedString>) -> Self {
679        self.interactivity().group = Some(group.into());
680        self
681    }
682
683    /// Assign this element an ID, so that it can be used with interactivity
684    fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
685        self.interactivity().element_id = Some(id.into());
686
687        Stateful { element: self }
688    }
689
690    /// Track the focus state of the given focus handle on this element.
691    /// If the focus handle is focused by the application, this element will
692    /// apply its focused styles.
693    fn track_focus(mut self, focus_handle: &FocusHandle) -> Self {
694        self.interactivity().focusable = true;
695        self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
696        self
697    }
698
699    /// Set whether this element is a tab stop.
700    ///
701    /// When false, the element remains in tab-index order but cannot be reached via keyboard navigation.
702    /// Useful for container elements: focus the container, then call `window.focus_next()` to focus
703    /// the first tab stop inside it while having the container element itself be unreachable via the keyboard.
704    /// Should only be used with `tab_index`.
705    fn tab_stop(mut self, tab_stop: bool) -> Self {
706        self.interactivity().tab_stop = tab_stop;
707        self
708    }
709
710    /// Set index of the tab stop order, and set this node as a tab stop.
711    /// This will default the element to being a tab stop. See [`Self::tab_stop`] for more information.
712    /// This should only be used in conjunction with `tab_group`
713    /// in order to not interfere with the tab index of other elements.
714    fn tab_index(mut self, index: isize) -> Self {
715        self.interactivity().focusable = true;
716        self.interactivity().tab_index = Some(index);
717        self.interactivity().tab_stop = true;
718        self
719    }
720
721    /// Designate this div as a "tab group". Tab groups have their own location in the tab-index order,
722    /// but for children of the tab group, the tab index is reset to 0. This can be useful for swapping
723    /// the order of tab stops within the group, without having to renumber all the tab stops in the whole
724    /// application.
725    fn tab_group(mut self) -> Self {
726        self.interactivity().tab_group = true;
727        if self.interactivity().tab_index.is_none() {
728            self.interactivity().tab_index = Some(0);
729        }
730        self
731    }
732
733    /// Set the keymap context for this element. This will be used to determine
734    /// which action to dispatch from the keymap.
735    fn key_context<C, E>(mut self, key_context: C) -> Self
736    where
737        C: TryInto<KeyContext, Error = E>,
738        E: Debug,
739    {
740        if let Some(key_context) = key_context.try_into().log_err() {
741            self.interactivity().key_context = Some(key_context);
742        }
743        self
744    }
745
746    /// Apply the given style to this element when the mouse hovers over it
747    fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
748        debug_assert!(
749            self.interactivity().hover_style.is_none(),
750            "hover style already set"
751        );
752        self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
753        self
754    }
755
756    /// Apply the given style to this element when the mouse hovers over a group member
757    fn group_hover(
758        mut self,
759        group_name: impl Into<SharedString>,
760        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
761    ) -> Self {
762        self.interactivity().group_hover_style = Some(GroupStyle {
763            group: group_name.into(),
764            style: Box::new(f(StyleRefinement::default())),
765        });
766        self
767    }
768
769    /// Bind the given callback to the mouse down event for the given mouse button,
770    /// the fluent API equivalent to [`Interactivity::on_mouse_down`]
771    ///
772    /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback.
773    fn on_mouse_down(
774        mut self,
775        button: MouseButton,
776        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
777    ) -> Self {
778        self.interactivity().on_mouse_down(button, listener);
779        self
780    }
781
782    #[cfg(any(test, feature = "test-support"))]
783    /// Set a key that can be used to look up this element's bounds
784    /// in the [`crate::VisualTestContext::debug_bounds`] map
785    /// This is a noop in release builds
786    fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
787        self.interactivity().debug_selector = Some(f());
788        self
789    }
790
791    #[cfg(not(any(test, feature = "test-support")))]
792    /// Set a key that can be used to look up this element's bounds
793    /// in the `VisualTestContext::debug_bounds` map
794    /// This is a noop in release builds
795    #[inline]
796    fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
797        self
798    }
799
800    /// Bind the given callback to the mouse down event for any button, during the capture phase
801    /// the fluent API equivalent to [`Interactivity::capture_any_mouse_down`]
802    ///
803    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
804    fn capture_any_mouse_down(
805        mut self,
806        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
807    ) -> Self {
808        self.interactivity().capture_any_mouse_down(listener);
809        self
810    }
811
812    /// Bind the given callback to the mouse down event for any button, during the capture phase
813    /// the fluent API equivalent to [`Interactivity::on_any_mouse_down`]
814    ///
815    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
816    fn on_any_mouse_down(
817        mut self,
818        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
819    ) -> Self {
820        self.interactivity().on_any_mouse_down(listener);
821        self
822    }
823
824    /// Bind the given callback to the mouse up event for the given button, during the bubble phase
825    /// the fluent API equivalent to [`Interactivity::on_mouse_up`]
826    ///
827    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
828    fn on_mouse_up(
829        mut self,
830        button: MouseButton,
831        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
832    ) -> Self {
833        self.interactivity().on_mouse_up(button, listener);
834        self
835    }
836
837    /// Bind the given callback to the mouse up event for any button, during the capture phase
838    /// the fluent API equivalent to [`Interactivity::capture_any_mouse_up`]
839    ///
840    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
841    fn capture_any_mouse_up(
842        mut self,
843        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
844    ) -> Self {
845        self.interactivity().capture_any_mouse_up(listener);
846        self
847    }
848
849    /// Bind the given callback to the mouse down event, on any button, during the capture phase,
850    /// when the mouse is outside of the bounds of this element.
851    /// The fluent API equivalent to [`Interactivity::on_mouse_down_out`]
852    ///
853    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
854    fn on_mouse_down_out(
855        mut self,
856        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
857    ) -> Self {
858        self.interactivity().on_mouse_down_out(listener);
859        self
860    }
861
862    /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
863    /// when the mouse is outside of the bounds of this element.
864    /// The fluent API equivalent to [`Interactivity::on_mouse_up_out`]
865    ///
866    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
867    fn on_mouse_up_out(
868        mut self,
869        button: MouseButton,
870        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
871    ) -> Self {
872        self.interactivity().on_mouse_up_out(button, listener);
873        self
874    }
875
876    /// Bind the given callback to the mouse move event, during the bubble phase
877    /// The fluent API equivalent to [`Interactivity::on_mouse_move`]
878    ///
879    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
880    fn on_mouse_move(
881        mut self,
882        listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
883    ) -> Self {
884        self.interactivity().on_mouse_move(listener);
885        self
886    }
887
888    /// Bind the given callback to the mouse exit event, during the bubble phase.
889    /// The fluent API equivalent to [`Interactivity::on_mouse_exit`].
890    ///
891    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
892    fn on_mouse_exit(
893        mut self,
894        listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static,
895    ) -> Self {
896        self.interactivity().on_mouse_exit(listener);
897        self
898    }
899
900    /// Bind the given callback to the mouse drag event of the given type. Note that this
901    /// will be called for all move events, inside or outside of this element, as long as the
902    /// drag was started with this element under the mouse. Useful for implementing draggable
903    /// UIs that don't conform to a drag and drop style interaction, like resizing.
904    /// The fluent API equivalent to [`Interactivity::on_drag_move`]
905    ///
906    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
907    fn on_drag_move<T: 'static>(
908        mut self,
909        listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
910    ) -> Self {
911        self.interactivity().on_drag_move(listener);
912        self
913    }
914
915    /// Bind the given callback to scroll wheel events during the bubble phase
916    /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
917    ///
918    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
919    fn on_scroll_wheel(
920        mut self,
921        listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
922    ) -> Self {
923        self.interactivity().on_scroll_wheel(listener);
924        self
925    }
926
927    /// Bind the given callback to pinch gesture events during the bubble phase.
928    /// The fluent API equivalent to [`Interactivity::on_pinch`].
929    ///
930    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
931    fn on_pinch(mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) -> Self {
932        self.interactivity().on_pinch(listener);
933        self
934    }
935
936    /// Bind the given callback to pinch gesture events during the capture phase.
937    /// The fluent API equivalent to [`Interactivity::capture_pinch`].
938    ///
939    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
940    fn capture_pinch(
941        mut self,
942        listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static,
943    ) -> Self {
944        self.interactivity().capture_pinch(listener);
945        self
946    }
947
948    /// Capture the given action, before normal action dispatch can fire
949    /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
950    ///
951    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
952    fn capture_action<A: Action>(
953        mut self,
954        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
955    ) -> Self {
956        self.interactivity().capture_action(listener);
957        self
958    }
959
960    /// Bind the given callback to an action dispatch during the bubble phase
961    /// The fluent API equivalent to [`Interactivity::on_action`]
962    ///
963    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
964    fn on_action<A: Action>(
965        mut self,
966        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
967    ) -> Self {
968        self.interactivity().on_action(listener);
969        self
970    }
971
972    /// Bind the given callback to an action dispatch, based on a dynamic action parameter
973    /// instead of a type parameter. Useful for component libraries that want to expose
974    /// action bindings to their users.
975    /// The fluent API equivalent to [`Interactivity::on_boxed_action`]
976    ///
977    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
978    fn on_boxed_action(
979        mut self,
980        action: &dyn Action,
981        listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
982    ) -> Self {
983        self.interactivity().on_boxed_action(action, listener);
984        self
985    }
986
987    /// Bind the given callback to key down events during the bubble phase
988    /// The fluent API equivalent to [`Interactivity::on_key_down`]
989    ///
990    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
991    fn on_key_down(
992        mut self,
993        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
994    ) -> Self {
995        self.interactivity().on_key_down(listener);
996        self
997    }
998
999    /// Bind the given callback to key down events during the capture phase
1000    /// The fluent API equivalent to [`Interactivity::capture_key_down`]
1001    ///
1002    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1003    fn capture_key_down(
1004        mut self,
1005        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
1006    ) -> Self {
1007        self.interactivity().capture_key_down(listener);
1008        self
1009    }
1010
1011    /// Bind the given callback to key up events during the bubble phase
1012    /// The fluent API equivalent to [`Interactivity::on_key_up`]
1013    ///
1014    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1015    fn on_key_up(
1016        mut self,
1017        listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1018    ) -> Self {
1019        self.interactivity().on_key_up(listener);
1020        self
1021    }
1022
1023    /// Bind the given callback to key up events during the capture phase
1024    /// The fluent API equivalent to [`Interactivity::capture_key_up`]
1025    ///
1026    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1027    fn capture_key_up(
1028        mut self,
1029        listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
1030    ) -> Self {
1031        self.interactivity().capture_key_up(listener);
1032        self
1033    }
1034
1035    /// Bind the given callback to modifiers changing events.
1036    /// The fluent API equivalent to [`Interactivity::on_modifiers_changed`]
1037    ///
1038    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1039    fn on_modifiers_changed(
1040        mut self,
1041        listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
1042    ) -> Self {
1043        self.interactivity().on_modifiers_changed(listener);
1044        self
1045    }
1046
1047    /// Apply the given style when the given data type is dragged over this element
1048    fn drag_over<S: 'static>(
1049        mut self,
1050        f: impl 'static + Fn(StyleRefinement, &S, &mut Window, &mut App) -> StyleRefinement,
1051    ) -> Self {
1052        self.interactivity().drag_over_styles.push((
1053            TypeId::of::<S>(),
1054            Box::new(move |currently_dragged: &dyn Any, window, cx| {
1055                f(
1056                    StyleRefinement::default(),
1057                    currently_dragged.downcast_ref::<S>().unwrap(),
1058                    window,
1059                    cx,
1060                )
1061            }),
1062        ));
1063        self
1064    }
1065
1066    /// Apply the given style when the given data type is dragged over this element's group
1067    fn group_drag_over<S: 'static>(
1068        mut self,
1069        group_name: impl Into<SharedString>,
1070        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1071    ) -> Self {
1072        self.interactivity().group_drag_over_styles.push((
1073            TypeId::of::<S>(),
1074            GroupStyle {
1075                group: group_name.into(),
1076                style: Box::new(f(StyleRefinement::default())),
1077            },
1078        ));
1079        self
1080    }
1081
1082    /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
1083    /// The fluent API equivalent to [`Interactivity::on_drop`]
1084    ///
1085    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1086    fn on_drop<T: 'static>(
1087        mut self,
1088        listener: impl Fn(&T, &mut Window, &mut App) + 'static,
1089    ) -> Self {
1090        self.interactivity().on_drop(listener);
1091        self
1092    }
1093
1094    /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
1095    /// The fluent API equivalent to [`Interactivity::can_drop`]
1096    fn can_drop(
1097        mut self,
1098        predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
1099    ) -> Self {
1100        self.interactivity().can_drop(predicate);
1101        self
1102    }
1103
1104    /// Block the mouse from all interactions with elements behind this element's hitbox. Typically
1105    /// `block_mouse_except_scroll` should be preferred.
1106    /// The fluent API equivalent to [`Interactivity::occlude_mouse`]
1107    fn occlude(mut self) -> Self {
1108        self.interactivity().occlude_mouse();
1109        self
1110    }
1111
1112    /// Set the bounds of this element as a window control area for the platform window.
1113    /// The fluent API equivalent to [`Interactivity::window_control_area`]
1114    fn window_control_area(mut self, area: WindowControlArea) -> Self {
1115        self.interactivity().window_control_area(area);
1116        self
1117    }
1118
1119    /// Block non-scroll mouse interactions with elements behind this element's hitbox. See
1120    /// [`Hitbox::is_hovered`] for details.
1121    ///
1122    /// The fluent API equivalent to [`Interactivity::block_mouse_except_scroll`]
1123    fn block_mouse_except_scroll(mut self) -> Self {
1124        self.interactivity().block_mouse_except_scroll();
1125        self
1126    }
1127
1128    /// Set the given styles to be applied when this element, specifically, is focused.
1129    /// Requires that the element is focusable. Elements can be made focusable using [`InteractiveElement::track_focus`].
1130    fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1131    where
1132        Self: Sized,
1133    {
1134        self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
1135        self
1136    }
1137
1138    /// Set the given styles to be applied when this element is inside another element that is focused.
1139    /// Requires that the element is focusable. Elements can be made focusable using [`InteractiveElement::track_focus`].
1140    fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1141    where
1142        Self: Sized,
1143    {
1144        self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
1145        self
1146    }
1147}
1148
1149/// A trait for elements that want to use the standard GPUI interactivity features
1150/// that require state.
1151pub trait StatefulInteractiveElement: InteractiveElement {
1152    /// Set the accessible role for this element.
1153    fn role(mut self, role: accesskit::Role) -> Self {
1154        debug_assert!(
1155            role != accesskit::Role::GenericContainer,
1156            "GenericContainer is filtered out of the a11y tree and has no effect"
1157        );
1158        self.interactivity().override_role = Some(role);
1159        self
1160    }
1161
1162    /// Set the author-provided identifier exposed to accessibility clients.
1163    ///
1164    /// Unlike the GPUI element ID, this value is visible outside the process.
1165    /// Keep it stable and unique within its accessibility tree.
1166    fn accessibility_id(mut self, id: impl Into<SharedString>) -> Self {
1167        self.interactivity().accessibility_id = Some(id.into());
1168        self
1169    }
1170
1171    /// Set the accessible label for this element.
1172    fn aria_label(mut self, label: impl Into<SharedString>) -> Self {
1173        self.interactivity().aria_label = Some(label.into());
1174        self
1175    }
1176
1177    /// Set supplementary information announced after this element's label, role, and value.
1178    fn aria_description(mut self, description: impl Into<SharedString>) -> Self {
1179        self.interactivity().aria_description = Some(description.into());
1180        self
1181    }
1182
1183    /// Set the current textual value for this element.
1184    fn aria_value(mut self, value: impl Into<SharedString>) -> Self {
1185        self.interactivity().aria_value = Some(value.into());
1186        self
1187    }
1188
1189    /// Set the placeholder announced when this element has no value.
1190    fn aria_placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
1191        self.interactivity().aria_placeholder = Some(placeholder.into());
1192        self
1193    }
1194
1195    /// Report whether this control is disabled.
1196    fn aria_disabled(mut self, disabled: bool) -> Self {
1197        self.interactivity().aria_disabled = disabled;
1198        self
1199    }
1200
1201    /// Report whether this form control requires a value.
1202    fn aria_required(mut self, required: bool) -> Self {
1203        self.interactivity().aria_required = required;
1204        self
1205    }
1206
1207    /// Report why this form control's value is invalid.
1208    fn aria_invalid(mut self, invalid: accesskit::Invalid) -> Self {
1209        self.interactivity().aria_invalid = Some(invalid);
1210        self
1211    }
1212
1213    /// Report whether this dialog is modal.
1214    fn aria_modal(mut self, modal: bool) -> Self {
1215        self.interactivity().aria_modal = modal;
1216        self
1217    }
1218
1219    /// Report the keyboard shortcut that activates this element to assistive technology.
1220    ///
1221    /// This is metadata only and does not register a key binding.
1222    fn aria_keyshortcuts(mut self, keyshortcuts: impl Into<SharedString>) -> Self {
1223        self.interactivity().aria_keyshortcuts = Some(keyshortcuts.into());
1224        self
1225    }
1226
1227    /// Set the selected state for this element.
1228    fn aria_selected(mut self, selected: bool) -> Self {
1229        self.interactivity().aria_selected = Some(selected);
1230        self
1231    }
1232
1233    /// Set the expanded state for this element.
1234    fn aria_expanded(mut self, expanded: bool) -> Self {
1235        self.interactivity().aria_expanded = Some(expanded);
1236        self
1237    }
1238
1239    /// Set the toggled state for this element.
1240    fn aria_toggled(mut self, toggled: accesskit::Toggled) -> Self {
1241        self.interactivity().aria_toggled = Some(toggled);
1242        self
1243    }
1244
1245    /// Set the numeric value for this element.
1246    fn aria_numeric_value(mut self, value: f64) -> Self {
1247        self.interactivity().aria_numeric_value = Some(value);
1248        self
1249    }
1250
1251    /// Set the minimum numeric value for this element.
1252    fn aria_min_numeric_value(mut self, value: f64) -> Self {
1253        self.interactivity().aria_min_numeric_value = Some(value);
1254        self
1255    }
1256
1257    /// Set the maximum numeric value for this element.
1258    fn aria_max_numeric_value(mut self, value: f64) -> Self {
1259        self.interactivity().aria_max_numeric_value = Some(value);
1260        self
1261    }
1262
1263    /// Set the orientation of this element.
1264    fn aria_orientation(mut self, orientation: accesskit::Orientation) -> Self {
1265        self.interactivity().aria_orientation = Some(orientation);
1266        self
1267    }
1268
1269    /// Set the heading level of this element.
1270    fn aria_level(mut self, level: usize) -> Self {
1271        self.interactivity().aria_level = Some(level);
1272        self
1273    }
1274
1275    /// Set the position in set of this element.
1276    fn aria_position_in_set(mut self, position: usize) -> Self {
1277        self.interactivity().aria_position_in_set = Some(position);
1278        self
1279    }
1280
1281    /// Set the size of set for this element.
1282    fn aria_size_of_set(mut self, size: usize) -> Self {
1283        self.interactivity().aria_size_of_set = Some(size);
1284        self
1285    }
1286
1287    /// Set the row index for this element.
1288    fn aria_row_index(mut self, index: usize) -> Self {
1289        self.interactivity().aria_row_index = Some(index);
1290        self
1291    }
1292
1293    /// Set the column index for this element.
1294    fn aria_column_index(mut self, index: usize) -> Self {
1295        self.interactivity().aria_column_index = Some(index);
1296        self
1297    }
1298
1299    /// Set the row count for this element.
1300    fn aria_row_count(mut self, count: usize) -> Self {
1301        self.interactivity().aria_row_count = Some(count);
1302        self
1303    }
1304
1305    /// Set the column count for this element.
1306    fn aria_column_count(mut self, count: usize) -> Self {
1307        self.interactivity().aria_column_count = Some(count);
1308        self
1309    }
1310
1311    /// Register a handler for an accessibility action on this element.
1312    fn on_a11y_action(
1313        mut self,
1314        action: accesskit::Action,
1315        listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static,
1316    ) -> Self {
1317        self.interactivity()
1318            .a11y_action_listeners
1319            .push((action, Box::new(listener)));
1320        self
1321    }
1322
1323    /// Set this element to focusable.
1324    fn focusable(mut self) -> Self {
1325        self.interactivity().focusable = true;
1326        self
1327    }
1328
1329    /// Set the overflow x and y to scroll.
1330    fn overflow_scroll(mut self) -> Self {
1331        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1332        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1333        self
1334    }
1335
1336    /// Set the overflow x to scroll.
1337    fn overflow_x_scroll(mut self) -> Self {
1338        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1339        self
1340    }
1341
1342    /// Set the overflow y to scroll.
1343    fn overflow_y_scroll(mut self) -> Self {
1344        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1345        self
1346    }
1347
1348    /// Restrict scrolling of this element to the axis of the input gesture.
1349    ///
1350    /// See [`Style::restrict_scroll_to_axis`](crate::Style::restrict_scroll_to_axis) for details.
1351    fn restrict_scroll_to_axis(mut self) -> Self {
1352        self.interactivity().base_style.restrict_scroll_to_axis = Some(true);
1353        self
1354    }
1355
1356    /// Set the space to be reserved for rendering the scrollbar.
1357    ///
1358    /// This will only affect the layout of the element when overflow for this element is set to
1359    /// `Overflow::Scroll`.
1360    fn scrollbar_width(mut self, width: impl Into<AbsoluteLength>) -> Self {
1361        self.interactivity().base_style.scrollbar_width = Some(width.into());
1362        self
1363    }
1364
1365    /// Track the scroll state of this element with the given handle.
1366    fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
1367        self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone());
1368        self
1369    }
1370
1371    /// Track the scroll state of this element with the given handle.
1372    fn anchor_scroll(mut self, scroll_anchor: Option<ScrollAnchor>) -> Self {
1373        self.interactivity().scroll_anchor = scroll_anchor;
1374        self
1375    }
1376
1377    /// Set the given styles to be applied when this element is active.
1378    fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1379    where
1380        Self: Sized,
1381    {
1382        self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
1383        self
1384    }
1385
1386    /// Set the given styles to be applied when this element's group is active.
1387    fn group_active(
1388        mut self,
1389        group_name: impl Into<SharedString>,
1390        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1391    ) -> Self
1392    where
1393        Self: Sized,
1394    {
1395        self.interactivity().group_active_style = Some(GroupStyle {
1396            group: group_name.into(),
1397            style: Box::new(f(StyleRefinement::default())),
1398        });
1399        self
1400    }
1401
1402    /// Bind the given callback to click events of this element
1403    /// The fluent API equivalent to [`Interactivity::on_click`]
1404    ///
1405    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1406    fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self
1407    where
1408        Self: Sized,
1409    {
1410        self.interactivity().on_click(listener);
1411        self
1412    }
1413
1414    /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
1415    /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
1416    /// the [`InteractiveElement::on_drag_move`] API.
1417    /// The callback also has access to the offset of triggering click from the origin of parent element.
1418    /// The fluent API equivalent to [`Interactivity::on_drag`]
1419    ///
1420    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1421    fn on_drag<T, W>(
1422        mut self,
1423        value: T,
1424        constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
1425    ) -> Self
1426    where
1427        Self: Sized,
1428        T: 'static,
1429        W: 'static + Render,
1430    {
1431        self.interactivity().on_drag(value, constructor);
1432        self
1433    }
1434
1435    /// Bind the given callback on the hover start and end events of this element. Note that the boolean
1436    /// passed to the callback is true when the hover starts and false when it ends.
1437    /// Transitions caused by layout changes under a stationary mouse also invoke the callback.
1438    /// The fluent API equivalent to [`Interactivity::on_hover`]
1439    ///
1440    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1441    fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self
1442    where
1443        Self: Sized,
1444    {
1445        self.interactivity().on_hover(listener);
1446        self
1447    }
1448
1449    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
1450    /// The fluent API equivalent to [`Interactivity::tooltip`]
1451    fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self
1452    where
1453        Self: Sized,
1454    {
1455        self.interactivity().tooltip(build_tooltip);
1456        self
1457    }
1458
1459    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
1460    /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into
1461    /// the tooltip. The fluent API equivalent to [`Interactivity::hoverable_tooltip`]
1462    fn hoverable_tooltip(
1463        mut self,
1464        build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
1465    ) -> Self
1466    where
1467        Self: Sized,
1468    {
1469        self.interactivity().hoverable_tooltip(build_tooltip);
1470        self
1471    }
1472
1473    /// Set the delay before this element's tooltip is shown.
1474    /// The fluent API equivalent to [`Interactivity::tooltip_show_delay`]
1475    fn tooltip_show_delay(mut self, delay: Duration) -> Self
1476    where
1477        Self: Sized,
1478    {
1479        self.interactivity().tooltip_show_delay(delay);
1480        self
1481    }
1482
1483    /// Bind the given callback to be called when this element's bounds change size.
1484    /// The fluent API equivalent to [`Interactivity::on_resize`]
1485    ///
1486    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
1487    fn on_resize(mut self, listener: impl Fn(&ResizeEvent, &mut Window, &mut App) + 'static) -> Self
1488    where
1489        Self: Sized,
1490    {
1491        self.interactivity().on_resize(listener);
1492        self
1493    }
1494}
1495
1496pub(crate) type MouseDownListener =
1497    Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1498pub(crate) type MouseUpListener =
1499    Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1500
1501pub(crate) type MouseMoveListener =
1502    Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1503pub(crate) type MouseExitListener =
1504    Box<dyn Fn(&MouseExitEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1505
1506pub(crate) type ScrollWheelListener =
1507    Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1508
1509pub(crate) type PinchListener =
1510    Box<dyn Fn(&PinchEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1511
1512pub(crate) type ClickListener = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
1513
1514pub(crate) type DragListener =
1515    Box<dyn Fn(&dyn Any, Point<Pixels>, &mut Window, &mut App) -> AnyView + 'static>;
1516
1517type DropListener = Box<dyn Fn(&dyn Any, &mut Window, &mut App) + 'static>;
1518
1519type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>;
1520
1521pub(crate) struct TooltipBuilder {
1522    build: Rc<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>,
1523    hoverable: bool,
1524}
1525
1526pub(crate) type KeyDownListener =
1527    Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1528
1529pub(crate) type KeyUpListener =
1530    Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1531
1532pub(crate) type ModifiersChangedListener =
1533    Box<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static>;
1534
1535pub(crate) type ActionListener =
1536    Box<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
1537
1538pub(crate) type ResizeListener = Box<dyn Fn(&ResizeEvent, &mut Window, &mut App) + 'static>;
1539
1540/// Construct a new [`Div`] element
1541#[track_caller]
1542pub fn div() -> Div {
1543    Div {
1544        interactivity: Interactivity::new(),
1545        children: SmallVec::default(),
1546        prepaint_listener: None,
1547        image_cache: None,
1548    }
1549}
1550
1551/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI
1552pub struct Div {
1553    interactivity: Interactivity,
1554    children: SmallVec<[StackSafe<AnyElement>; 2]>,
1555    prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static>>,
1556    image_cache: Option<Box<dyn ImageCacheProvider>>,
1557}
1558
1559impl Div {
1560    /// Add a listener to be called when the children of this `Div` are prepainted.
1561    /// This allows you to store the [`Bounds`] of the children for later use.
1562    pub fn on_children_prepainted(
1563        mut self,
1564        listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
1565    ) -> Self {
1566        self.prepaint_listener = Some(Box::new(listener));
1567        self
1568    }
1569
1570    /// Add an image cache at the location of this div in the element tree.
1571    pub fn image_cache(mut self, cache: impl ImageCacheProvider) -> Self {
1572        self.image_cache = Some(Box::new(cache));
1573        self
1574    }
1575}
1576
1577/// A frame state for a `Div` element, which contains layout IDs for its children.
1578///
1579/// This struct is used internally by the `Div` element to manage the layout state of its children
1580/// during the UI update cycle. It holds a small vector of `LayoutId` values, each corresponding to
1581/// a child element of the `Div`. These IDs are used to query the layout engine for the computed
1582/// bounds of the children after the layout phase is complete.
1583pub struct DivFrameState {
1584    child_layout_ids: SmallVec<[LayoutId; 2]>,
1585}
1586
1587/// Interactivity state displayed an manipulated in the inspector.
1588#[derive(Clone)]
1589pub struct DivInspectorState {
1590    /// The inspected element's base style. This is used for both inspecting and modifying the
1591    /// state. In the future it will make sense to separate the read and write, possibly tracking
1592    /// the modifications.
1593    #[cfg(any(feature = "inspector", debug_assertions))]
1594    pub base_style: Box<StyleRefinement>,
1595    /// Inspects the bounds of the element.
1596    pub bounds: Bounds<Pixels>,
1597    /// Size of the children of the element, or `bounds.size` if it has no children.
1598    pub content_size: Size<Pixels>,
1599}
1600
1601impl Styled for Div {
1602    fn style(&mut self) -> &mut StyleRefinement {
1603        &mut self.interactivity.base_style
1604    }
1605}
1606
1607impl InteractiveElement for Div {
1608    fn interactivity(&mut self) -> &mut Interactivity {
1609        &mut self.interactivity
1610    }
1611}
1612
1613impl ParentElement for Div {
1614    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1615        self.children
1616            .extend(elements.into_iter().map(StackSafe::new))
1617    }
1618}
1619
1620impl Element for Div {
1621    type RequestLayoutState = DivFrameState;
1622    type PrepaintState = Option<Hitbox>;
1623
1624    fn id(&self) -> Option<ElementId> {
1625        self.interactivity.element_id.clone()
1626    }
1627
1628    fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
1629        self.interactivity.source_location()
1630    }
1631
1632    fn a11y_role(&self) -> Option<accesskit::Role> {
1633        self.interactivity
1634            .override_role
1635            .filter(|role| *role != accesskit::Role::GenericContainer)
1636    }
1637
1638    fn write_a11y_info(&self, node: &mut accesskit::Node) {
1639        self.interactivity.write_a11y_info(node);
1640    }
1641
1642    #[stacksafe]
1643    fn request_layout(
1644        &mut self,
1645        global_id: Option<&GlobalElementId>,
1646        inspector_id: Option<&InspectorElementId>,
1647        window: &mut Window,
1648        cx: &mut App,
1649    ) -> (LayoutId, Self::RequestLayoutState) {
1650        let mut child_layout_ids = SmallVec::new();
1651        let image_cache = self
1652            .image_cache
1653            .as_mut()
1654            .map(|provider| provider.provide(window, cx));
1655
1656        let layout_id = window.with_image_cache(image_cache, |window| {
1657            self.interactivity.request_layout(
1658                global_id,
1659                inspector_id,
1660                window,
1661                cx,
1662                |style, window, cx| {
1663                    window.with_text_style(style.text_style().cloned(), |window| {
1664                        child_layout_ids = self
1665                            .children
1666                            .iter_mut()
1667                            .map(|child| child.request_layout(window, cx))
1668                            .collect::<SmallVec<_>>();
1669                        window.request_layout(style, child_layout_ids.iter().copied(), cx)
1670                    })
1671                },
1672            )
1673        });
1674
1675        (layout_id, DivFrameState { child_layout_ids })
1676    }
1677
1678    #[stacksafe]
1679    fn prepaint(
1680        &mut self,
1681        global_id: Option<&GlobalElementId>,
1682        inspector_id: Option<&InspectorElementId>,
1683        bounds: Bounds<Pixels>,
1684        request_layout: &mut Self::RequestLayoutState,
1685        window: &mut Window,
1686        cx: &mut App,
1687    ) -> Option<Hitbox> {
1688        let has_prepaint_listener = self.prepaint_listener.is_some();
1689        let mut children_bounds = Vec::with_capacity(if has_prepaint_listener {
1690            request_layout.child_layout_ids.len()
1691        } else {
1692            0
1693        });
1694
1695        let mut child_min = point(Pixels::MAX, Pixels::MAX);
1696        let mut child_max = Point::default();
1697        if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1698            *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
1699        }
1700        let content_size = if request_layout.child_layout_ids.is_empty() {
1701            bounds.size
1702        } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1703            let mut state = scroll_handle.0.borrow_mut();
1704            state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1705            for child_layout_id in &request_layout.child_layout_ids {
1706                let child_bounds = window.layout_bounds(*child_layout_id);
1707                child_min = child_min.min(&child_bounds.origin);
1708                child_max = child_max.max(&child_bounds.bottom_right());
1709                state.child_bounds.push(child_bounds);
1710            }
1711            (child_max - child_min).into()
1712        } else {
1713            for child_layout_id in &request_layout.child_layout_ids {
1714                let child_bounds = window.layout_bounds(*child_layout_id);
1715                child_min = child_min.min(&child_bounds.origin);
1716                child_max = child_max.max(&child_bounds.bottom_right());
1717
1718                if has_prepaint_listener {
1719                    children_bounds.push(child_bounds);
1720                }
1721            }
1722            (child_max - child_min).into()
1723        };
1724
1725        if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1726            scroll_handle.scroll_to_active_item();
1727        }
1728
1729        self.interactivity.prepaint(
1730            global_id,
1731            inspector_id,
1732            bounds,
1733            content_size,
1734            window,
1735            cx,
1736            |_style, scroll_offset, hitbox, window, cx| {
1737                window.with_element_offset(scroll_offset, |window| {
1738                    for child in &mut self.children {
1739                        child.prepaint(window, cx);
1740                    }
1741                });
1742
1743                if let Some(listener) = self.prepaint_listener.as_ref() {
1744                    listener(children_bounds, window, cx);
1745                }
1746
1747                hitbox
1748            },
1749        )
1750    }
1751
1752    #[stacksafe]
1753    fn paint(
1754        &mut self,
1755        global_id: Option<&GlobalElementId>,
1756        inspector_id: Option<&InspectorElementId>,
1757        bounds: Bounds<Pixels>,
1758        _request_layout: &mut Self::RequestLayoutState,
1759        hitbox: &mut Option<Hitbox>,
1760        window: &mut Window,
1761        cx: &mut App,
1762    ) {
1763        let image_cache = self
1764            .image_cache
1765            .as_mut()
1766            .map(|provider| provider.provide(window, cx));
1767
1768        window.with_image_cache(image_cache, |window| {
1769            self.interactivity.paint(
1770                global_id,
1771                inspector_id,
1772                bounds,
1773                hitbox.as_ref(),
1774                window,
1775                cx,
1776                |_style, window, cx| {
1777                    for child in &mut self.children {
1778                        child.paint(window, cx);
1779                    }
1780                },
1781            )
1782        });
1783    }
1784}
1785
1786impl IntoElement for Div {
1787    type Element = Self;
1788
1789    fn into_element(self) -> Self::Element {
1790        self
1791    }
1792}
1793
1794/// The interactivity struct. Powers all of the general-purpose
1795/// interactivity in the `Div` element.
1796#[derive(Default)]
1797pub struct Interactivity {
1798    /// The element ID of the element. In id is required to support a stateful subset of the interactivity such as on_click.
1799    pub element_id: Option<ElementId>,
1800    /// Whether the element was clicked. This will only be present after layout.
1801    pub active: Option<bool>,
1802    /// Whether the element was hovered. This will only be present after paint if an hitbox
1803    /// was created for the interactive element.
1804    pub hovered: Option<bool>,
1805    pub(crate) tooltip_id: Option<TooltipId>,
1806    pub(crate) content_size: Size<Pixels>,
1807    pub(crate) key_context: Option<KeyContext>,
1808    pub(crate) focusable: bool,
1809    pub(crate) tracked_focus_handle: Option<FocusHandle>,
1810    pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
1811    pub(crate) scroll_anchor: Option<ScrollAnchor>,
1812    pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1813    pub(crate) ongoing_scroll: Option<Rc<RefCell<OngoingScroll>>>,
1814    pub(crate) group: Option<SharedString>,
1815    /// The base style of the element, before any modifications are applied
1816    /// by focus, active, etc.
1817    pub base_style: Box<StyleRefinement>,
1818    pub(crate) focus_style: Option<Box<StyleRefinement>>,
1819    pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1820    pub(crate) hover_style: Option<Box<StyleRefinement>>,
1821    pub(crate) group_hover_style: Option<GroupStyle>,
1822    pub(crate) active_style: Option<Box<StyleRefinement>>,
1823    pub(crate) group_active_style: Option<GroupStyle>,
1824    pub(crate) drag_over_styles: Vec<(
1825        TypeId,
1826        Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> StyleRefinement>,
1827    )>,
1828    pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1829    pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1830    pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1831    pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1832    pub(crate) mouse_exit_listeners: Vec<MouseExitListener>,
1833    pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1834    pub(crate) pinch_listeners: Vec<PinchListener>,
1835    pub(crate) key_down_listeners: Vec<KeyDownListener>,
1836    pub(crate) key_up_listeners: Vec<KeyUpListener>,
1837    pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
1838    pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1839    pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1840    pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1841    pub(crate) click_listeners: Vec<ClickListener>,
1842    pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
1843    pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
1844    pub(crate) resize_listeners: Vec<ResizeListener>,
1845    pub(crate) tooltip_builder: Option<TooltipBuilder>,
1846    pub(crate) tooltip_show_delay: Option<Duration>,
1847    pub(crate) window_control: Option<WindowControlArea>,
1848    pub(crate) hitbox_behavior: HitboxBehavior,
1849    pub(crate) tab_index: Option<isize>,
1850    pub(crate) tab_group: bool,
1851    pub(crate) tab_stop: bool,
1852
1853    pub(crate) a11y_action_listeners:
1854        Vec<(accesskit::Action, crate::window::a11y::A11yActionListener)>,
1855    pub(crate) override_role: Option<accesskit::Role>,
1856    pub(crate) accessibility_id: Option<SharedString>,
1857    pub(crate) aria_label: Option<SharedString>,
1858    pub(crate) aria_description: Option<SharedString>,
1859    pub(crate) aria_value: Option<SharedString>,
1860    pub(crate) aria_placeholder: Option<SharedString>,
1861    pub(crate) aria_disabled: bool,
1862    pub(crate) aria_required: bool,
1863    pub(crate) aria_invalid: Option<accesskit::Invalid>,
1864    pub(crate) aria_modal: bool,
1865    pub(crate) aria_keyshortcuts: Option<SharedString>,
1866    pub(crate) aria_selected: Option<bool>,
1867    pub(crate) aria_expanded: Option<bool>,
1868    pub(crate) aria_toggled: Option<accesskit::Toggled>,
1869    pub(crate) aria_numeric_value: Option<f64>,
1870    pub(crate) aria_min_numeric_value: Option<f64>,
1871    pub(crate) aria_max_numeric_value: Option<f64>,
1872    pub(crate) aria_orientation: Option<accesskit::Orientation>,
1873    pub(crate) aria_level: Option<usize>,
1874    pub(crate) aria_position_in_set: Option<usize>,
1875    pub(crate) aria_size_of_set: Option<usize>,
1876    pub(crate) aria_row_index: Option<usize>,
1877    pub(crate) aria_column_index: Option<usize>,
1878    pub(crate) aria_row_count: Option<usize>,
1879    pub(crate) aria_column_count: Option<usize>,
1880
1881    #[cfg(any(feature = "inspector", debug_assertions))]
1882    pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
1883
1884    #[cfg(any(test, feature = "test-support"))]
1885    pub(crate) debug_selector: Option<String>,
1886}
1887
1888impl Interactivity {
1889    /// Layout this element according to this interactivity state's configured styles
1890    pub fn request_layout(
1891        &mut self,
1892        global_id: Option<&GlobalElementId>,
1893        _inspector_id: Option<&InspectorElementId>,
1894        window: &mut Window,
1895        cx: &mut App,
1896        f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
1897    ) -> LayoutId {
1898        #[cfg(any(feature = "inspector", debug_assertions))]
1899        window.with_inspector_state(
1900            _inspector_id,
1901            cx,
1902            |inspector_state: &mut Option<DivInspectorState>, _window| {
1903                if let Some(inspector_state) = inspector_state {
1904                    self.base_style = inspector_state.base_style.clone();
1905                } else {
1906                    *inspector_state = Some(DivInspectorState {
1907                        base_style: self.base_style.clone(),
1908                        bounds: Default::default(),
1909                        content_size: Default::default(),
1910                    })
1911                }
1912            },
1913        );
1914
1915        window.with_optional_element_state::<InteractiveElementState, _>(
1916            global_id,
1917            |element_state, window| {
1918                let mut element_state =
1919                    element_state.map(|element_state| element_state.unwrap_or_default());
1920
1921                if let Some(element_state) = element_state.as_ref()
1922                    && cx.has_active_drag()
1923                {
1924                    if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
1925                        *pending_mouse_down.borrow_mut() = None;
1926                    }
1927                    if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1928                        *clicked_state.borrow_mut() = ElementClickedState::default();
1929                    }
1930                }
1931
1932                // Ensure we store a focus handle in our element state if we're focusable.
1933                // If there's an explicit focus handle we're tracking, use that. Otherwise
1934                // create a new handle and store it in the element state, which lives for as
1935                // as frames contain an element with this id.
1936                if self.focusable
1937                    && self.tracked_focus_handle.is_none()
1938                    && let Some(element_state) = element_state.as_mut()
1939                {
1940                    let mut handle = element_state
1941                        .focus_handle
1942                        .get_or_insert_with(|| cx.focus_handle())
1943                        .clone()
1944                        .tab_stop(self.tab_stop);
1945
1946                    if let Some(index) = self.tab_index {
1947                        handle = handle.tab_index(index);
1948                    }
1949
1950                    self.tracked_focus_handle = Some(handle);
1951                }
1952
1953                if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
1954                    let scroll_handle_state = scroll_handle.0.borrow();
1955                    self.scroll_offset = Some(scroll_handle_state.offset.clone());
1956                    self.ongoing_scroll = Some(scroll_handle_state.ongoing_scroll.clone());
1957                } else if (self.base_style.overflow.x == Some(Overflow::Scroll)
1958                    || self.base_style.overflow.y == Some(Overflow::Scroll))
1959                    && let Some(element_state) = element_state.as_mut()
1960                {
1961                    self.scroll_offset = Some(
1962                        element_state
1963                            .scroll_offset
1964                            .get_or_insert_with(Rc::default)
1965                            .clone(),
1966                    );
1967                    self.ongoing_scroll = Some(
1968                        element_state
1969                            .ongoing_scroll
1970                            .get_or_insert_with(|| Rc::new(RefCell::new(OngoingScroll::default())))
1971                            .clone(),
1972                    );
1973                }
1974
1975                let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
1976                let layout_id = f(style, window, cx);
1977                (layout_id, element_state)
1978            },
1979        )
1980    }
1981
1982    /// Commit the bounds of this element according to this interactivity state's configured styles.
1983    pub fn prepaint<R>(
1984        &mut self,
1985        global_id: Option<&GlobalElementId>,
1986        _inspector_id: Option<&InspectorElementId>,
1987        bounds: Bounds<Pixels>,
1988        content_size: Size<Pixels>,
1989        window: &mut Window,
1990        cx: &mut App,
1991        f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
1992    ) -> R {
1993        self.content_size = content_size;
1994
1995        #[cfg(any(feature = "inspector", debug_assertions))]
1996        window.with_inspector_state(
1997            _inspector_id,
1998            cx,
1999            |inspector_state: &mut Option<DivInspectorState>, _window| {
2000                if let Some(inspector_state) = inspector_state {
2001                    inspector_state.bounds = bounds;
2002                    inspector_state.content_size = content_size;
2003                }
2004            },
2005        );
2006
2007        if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2008            window.set_focus_handle(focus_handle, cx);
2009
2010            if window.a11y.is_active()
2011                && let Some(global_id) = global_id
2012            {
2013                let node_id = global_id.accesskit_node_id();
2014                window.a11y.focus_ids.insert(node_id, focus_handle.id);
2015                if focus_handle.is_focused(window) && window.a11y.nodes.has_node(node_id) {
2016                    window.a11y.nodes.set_focus(node_id);
2017                }
2018            }
2019        }
2020        window.with_optional_element_state::<InteractiveElementState, _>(
2021            global_id,
2022            |element_state, window| {
2023                let mut element_state =
2024                    element_state.map(|element_state| element_state.unwrap_or_default());
2025                let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
2026
2027                if let Some(element_state) = element_state.as_mut() {
2028                    if let Some(clicked_state) = element_state.clicked_state.as_ref() {
2029                        let clicked_state = clicked_state.borrow();
2030                        self.active = Some(clicked_state.element);
2031                    }
2032                    if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
2033                        if self.tooltip_builder.is_some() {
2034                            self.tooltip_id = set_tooltip_on_window(active_tooltip, window);
2035                        } else {
2036                            // If there is no longer a tooltip builder, remove the active tooltip.
2037                            element_state.active_tooltip.take();
2038                        }
2039                    }
2040                }
2041
2042                window.with_text_style(style.text_style().cloned(), |window| {
2043                    window.with_content_mask(
2044                        style.overflow_mask(bounds, window.rem_size()),
2045                        |window| {
2046                            let hitbox = if self.should_insert_hitbox(&style, window, cx) {
2047                                Some(window.insert_hitbox(bounds, self.hitbox_behavior))
2048                            } else {
2049                                None
2050                            };
2051
2052                            let scroll_offset =
2053                                self.clamp_scroll_position(bounds, &style, window, cx);
2054                            let result = f(&style, scroll_offset, hitbox, window, cx);
2055                            (result, element_state)
2056                        },
2057                    )
2058                })
2059            },
2060        )
2061    }
2062
2063    fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
2064        self.hitbox_behavior != HitboxBehavior::Normal
2065            || self.window_control.is_some()
2066            || style.mouse_cursor.is_some()
2067            || self.group.is_some()
2068            || self.scroll_offset.is_some()
2069            || self.tracked_focus_handle.is_some()
2070            || self.hover_style.is_some()
2071            || self.group_hover_style.is_some()
2072            || self.hover_listener.is_some()
2073            || !self.mouse_up_listeners.is_empty()
2074            || !self.mouse_down_listeners.is_empty()
2075            || !self.mouse_move_listeners.is_empty()
2076            || !self.mouse_exit_listeners.is_empty()
2077            || !self.click_listeners.is_empty()
2078            || !self.scroll_wheel_listeners.is_empty()
2079            || !self.pinch_listeners.is_empty()
2080            || self.drag_listener.is_some()
2081            || !self.drop_listeners.is_empty()
2082            || self.tooltip_builder.is_some()
2083            || window.is_inspector_picking(cx)
2084    }
2085
2086    fn clamp_scroll_position(
2087        &self,
2088        bounds: Bounds<Pixels>,
2089        style: &Style,
2090        window: &mut Window,
2091        _cx: &mut App,
2092    ) -> Point<Pixels> {
2093        fn round_to_two_decimals(pixels: Pixels) -> Pixels {
2094            const ROUNDING_FACTOR: f32 = 100.0;
2095            (pixels * ROUNDING_FACTOR).round() / ROUNDING_FACTOR
2096        }
2097
2098        if let Some(scroll_offset) = self.scroll_offset.as_ref() {
2099            let mut scroll_to_bottom = false;
2100            let mut tracked_scroll_handle = self
2101                .tracked_scroll_handle
2102                .as_ref()
2103                .map(|handle| handle.0.borrow_mut());
2104            if let Some(mut scroll_handle_state) = tracked_scroll_handle.as_deref_mut() {
2105                scroll_handle_state.overflow = style.overflow;
2106                scroll_to_bottom = mem::take(&mut scroll_handle_state.scroll_to_bottom);
2107            }
2108
2109            let rem_size = window.rem_size();
2110            let padding = style.padding.to_pixels(bounds.size.into(), rem_size);
2111            let padding_size = size(padding.left + padding.right, padding.top + padding.bottom);
2112            // The floating point values produced by Taffy and ours often vary
2113            // slightly after ~5 decimal places. This can lead to cases where after
2114            // subtracting these, the container becomes scrollable for less than
2115            // 0.00000x pixels. As we generally don't benefit from a precision that
2116            // high for the maximum scroll, we round the scroll max to 2 decimal
2117            // places here.
2118            let padded_content_size = self.content_size + padding_size;
2119            let scroll_max = (padded_content_size - bounds.size)
2120                .map(round_to_two_decimals)
2121                .max(&Default::default());
2122            // Clamp scroll offset in case scroll max is smaller now (e.g., if children
2123            // were removed or the bounds became larger).
2124            let mut scroll_offset = scroll_offset.borrow_mut();
2125
2126            scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
2127            if scroll_to_bottom {
2128                scroll_offset.y = -scroll_max.height;
2129            } else {
2130                scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
2131            }
2132
2133            if let Some(mut scroll_handle_state) = tracked_scroll_handle {
2134                scroll_handle_state.max_offset = scroll_max;
2135                scroll_handle_state.bounds = bounds;
2136            }
2137
2138            *scroll_offset
2139        } else {
2140            Point::default()
2141        }
2142    }
2143
2144    /// Paint this element according to this interactivity state's configured styles
2145    /// and bind the element's mouse and keyboard events.
2146    ///
2147    /// content_size is the size of the content of the element, which may be larger than the
2148    /// element's bounds if the element is scrollable.
2149    ///
2150    /// the final computed style will be passed to the provided function, along
2151    /// with the current scroll offset
2152    pub fn paint(
2153        &mut self,
2154        global_id: Option<&GlobalElementId>,
2155        _inspector_id: Option<&InspectorElementId>,
2156        bounds: Bounds<Pixels>,
2157        hitbox: Option<&Hitbox>,
2158        window: &mut Window,
2159        cx: &mut App,
2160        f: impl FnOnce(&Style, &mut Window, &mut App),
2161    ) {
2162        self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window));
2163        window.with_optional_element_state::<InteractiveElementState, _>(
2164            global_id,
2165            |element_state, window| {
2166                let mut element_state =
2167                    element_state.map(|element_state| element_state.unwrap_or_default());
2168
2169                let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2170
2171                #[cfg(any(feature = "test-support", test))]
2172                if let Some(debug_selector) = &self.debug_selector {
2173                    window
2174                        .next_frame
2175                        .debug_bounds
2176                        .insert(debug_selector.clone(), bounds);
2177                }
2178
2179                self.paint_hover_group_handler(window, cx);
2180
2181                if !self.resize_listeners.is_empty() {
2182                    if let Some(element_state) = element_state.as_mut() {
2183                        let size_changed = element_state
2184                            .prev_bounds
2185                            .map(|prev| prev.size != bounds.size)
2186                            .unwrap_or(true);
2187
2188                        if size_changed {
2189                            element_state.prev_bounds = Some(bounds);
2190                            let resize_event = ResizeEvent {
2191                                size: bounds.size,
2192                                bounds,
2193                            };
2194                            for listener in &self.resize_listeners {
2195                                listener(&resize_event, window, cx);
2196                            }
2197                        }
2198                    }
2199                }
2200
2201                if style.visibility == Visibility::Hidden {
2202                    return ((), element_state);
2203                }
2204
2205                let mut tab_group = None;
2206                if self.tab_group {
2207                    tab_group = self.tab_index;
2208                }
2209                if let Some(focus_handle) = &self.tracked_focus_handle {
2210                    window.next_frame.tab_stops.insert(focus_handle);
2211                }
2212
2213                window.with_element_opacity(style.opacity, |window| {
2214                    style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
2215                        window.with_text_style(style.text_style().cloned(), |window| {
2216                            window.with_content_mask(
2217                                style.overflow_mask(bounds, window.rem_size()),
2218                                |window| {
2219                                    window.with_tab_group(tab_group, |window| {
2220                                        if let Some(hitbox) = hitbox {
2221                                            #[cfg(debug_assertions)]
2222                                            self.paint_debug_info(
2223                                                global_id, hitbox, &style, window, cx,
2224                                            );
2225
2226                                            if let Some(drag) = cx.active_drag.as_ref() {
2227                                                if let Some(mouse_cursor) = drag.cursor_style {
2228                                                    window.set_window_cursor_style(mouse_cursor);
2229                                                }
2230                                            } else {
2231                                                if let Some(mouse_cursor) = style.mouse_cursor {
2232                                                    window.set_cursor_style(mouse_cursor, hitbox);
2233                                                }
2234                                            }
2235
2236                                            if let Some(group) = self.group.clone() {
2237                                                GroupHitboxes::push(group, hitbox.id, cx);
2238                                            }
2239
2240                                            if let Some(area) = self.window_control {
2241                                                window.insert_window_control_hitbox(
2242                                                    area,
2243                                                    hitbox.clone(),
2244                                                );
2245                                            }
2246
2247                                            self.paint_mouse_listeners(
2248                                                hitbox,
2249                                                element_state.as_mut(),
2250                                                window,
2251                                                cx,
2252                                            );
2253                                            self.paint_scroll_listener(hitbox, &style, window, cx);
2254                                        }
2255
2256                                        self.paint_keyboard_listeners(window, cx);
2257
2258                                        if window.a11y.is_active()
2259                                            && let Some(global_id) = global_id
2260                                            && !self.a11y_action_listeners.is_empty()
2261                                        {
2262                                            let node_id = global_id.accesskit_node_id();
2263                                            for (action, listener) in
2264                                                self.a11y_action_listeners.drain(..)
2265                                            {
2266                                                window.on_a11y_action(node_id, action, listener);
2267                                            }
2268                                        }
2269
2270                                        f(&style, window, cx);
2271
2272                                        if let Some(_hitbox) = hitbox {
2273                                            #[cfg(any(feature = "inspector", debug_assertions))]
2274                                            window.insert_inspector_hitbox(
2275                                                _hitbox.id,
2276                                                _inspector_id,
2277                                                cx,
2278                                            );
2279
2280                                            if let Some(group) = self.group.as_ref() {
2281                                                GroupHitboxes::pop(group, cx);
2282                                            }
2283                                        }
2284                                    })
2285                                },
2286                            );
2287                        });
2288                    });
2289                });
2290
2291                ((), element_state)
2292            },
2293        );
2294    }
2295
2296    #[cfg(debug_assertions)]
2297    fn paint_debug_info(
2298        &self,
2299        global_id: Option<&GlobalElementId>,
2300        hitbox: &Hitbox,
2301        style: &Style,
2302        window: &mut Window,
2303        cx: &mut App,
2304    ) {
2305        use crate::{BorderStyle, TextAlign};
2306
2307        if let Some(global_id) = global_id.as_ref().filter(|_| {
2308            (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
2309                && hitbox.is_hovered(window)
2310        }) {
2311            const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
2312            let element_id = format!("{:?}", global_id);
2313            let str_len = element_id.len();
2314
2315            let render_debug_text = |window: &mut Window| {
2316                if let Some(text) = window
2317                    .text_system()
2318                    .shape_text(
2319                        element_id.into(),
2320                        FONT_SIZE,
2321                        &[window.text_style().to_run(str_len)],
2322                        None,
2323                        None,
2324                    )
2325                    .ok()
2326                    .and_then(|mut text| text.pop())
2327                {
2328                    text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
2329                        .ok();
2330
2331                    let text_bounds = crate::Bounds {
2332                        origin: hitbox.origin,
2333                        size: text.size(FONT_SIZE),
2334                    };
2335                    if let Some(source_location) = self.source_location.filter(|_| {
2336                        text_bounds.contains(&window.mouse_position())
2337                            && window.modifiers().secondary()
2338                    }) {
2339                        let secondary_held = window.modifiers().secondary();
2340                        window.on_key_event({
2341                            move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| {
2342                                if e.modifiers.secondary() != secondary_held
2343                                    && text_bounds.contains(&window.mouse_position())
2344                                {
2345                                    window.refresh();
2346                                }
2347                            }
2348                        });
2349
2350                        let was_hovered = hitbox.is_hovered(window);
2351                        let current_view = window.current_view();
2352                        window.on_mouse_event({
2353                            let hitbox = hitbox.clone();
2354                            move |_: &MouseMoveEvent, phase, window, cx| {
2355                                if phase == DispatchPhase::Capture {
2356                                    let hovered = hitbox.is_hovered(window);
2357                                    if hovered != was_hovered {
2358                                        cx.notify(current_view)
2359                                    }
2360                                }
2361                            }
2362                        });
2363
2364                        window.on_mouse_event({
2365                            let hitbox = hitbox.clone();
2366                            let location = source_location;
2367                            move |e: &crate::MouseDownEvent, phase, window, cx| {
2368                                if text_bounds.contains(&e.position)
2369                                    && phase.capture()
2370                                    && hitbox.is_hovered(window)
2371                                {
2372                                    cx.stop_propagation();
2373                                    let Ok(dir) = std::env::current_dir() else {
2374                                        return;
2375                                    };
2376
2377                                    eprintln!(
2378                                        "This element was created at:\n{}:{}:{}",
2379                                        dir.join(location.file()).to_string_lossy(),
2380                                        location.line(),
2381                                        location.column()
2382                                    );
2383                                }
2384                            }
2385                        });
2386                        window.paint_quad(crate::outline(
2387                            crate::Bounds {
2388                                origin: hitbox.origin
2389                                    + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
2390                                size: crate::Size {
2391                                    width: text_bounds.size.width,
2392                                    height: crate::px(1.),
2393                                },
2394                            },
2395                            crate::red(),
2396                            BorderStyle::default(),
2397                        ))
2398                    }
2399                }
2400            };
2401
2402            window.with_text_style(
2403                Some(crate::TextStyleRefinement {
2404                    color: Some(crate::red()),
2405                    line_height: Some(FONT_SIZE.into()),
2406                    background_color: Some(crate::white()),
2407                    ..Default::default()
2408                }),
2409                render_debug_text,
2410            )
2411        }
2412    }
2413
2414    fn paint_mouse_listeners(
2415        &mut self,
2416        hitbox: &Hitbox,
2417        element_state: Option<&mut InteractiveElementState>,
2418        window: &mut Window,
2419        cx: &mut App,
2420    ) {
2421        let is_focused = self
2422            .tracked_focus_handle
2423            .as_ref()
2424            .map(|handle| handle.is_focused(window))
2425            .unwrap_or(false);
2426
2427        // If this element can be focused, register a mouse down listener
2428        // that will automatically transfer focus when hitting the element.
2429        // This behavior can be suppressed by using `cx.prevent_default()`.
2430        if let Some(focus_handle) = self.tracked_focus_handle.clone() {
2431            let hitbox = hitbox.clone();
2432            window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _| {
2433                if phase == DispatchPhase::Bubble
2434                    && hitbox.is_hovered(window)
2435                    && !window.default_prevented()
2436                {
2437                    window.focus(&focus_handle);
2438                    // If there is a parent that is also focusable, prevent it
2439                    // from transferring focus because we already did so.
2440                    window.prevent_default();
2441                }
2442            });
2443        }
2444
2445        for listener in self.mouse_down_listeners.drain(..) {
2446            let hitbox = hitbox.clone();
2447            window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
2448                listener(event, phase, &hitbox, window, cx);
2449            })
2450        }
2451
2452        for listener in self.mouse_up_listeners.drain(..) {
2453            let hitbox = hitbox.clone();
2454            window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
2455                listener(event, phase, &hitbox, window, cx);
2456            })
2457        }
2458
2459        for listener in self.mouse_move_listeners.drain(..) {
2460            let hitbox = hitbox.clone();
2461            window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
2462                listener(event, phase, &hitbox, window, cx);
2463            })
2464        }
2465
2466        for listener in self.mouse_exit_listeners.drain(..) {
2467            let hitbox = hitbox.clone();
2468            window.on_mouse_event(move |event: &MouseExitEvent, phase, window, cx| {
2469                listener(event, phase, &hitbox, window, cx);
2470            })
2471        }
2472
2473        for listener in self.scroll_wheel_listeners.drain(..) {
2474            let hitbox = hitbox.clone();
2475            window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2476                listener(event, phase, &hitbox, window, cx);
2477            })
2478        }
2479
2480        for listener in self.pinch_listeners.drain(..) {
2481            let hitbox = hitbox.clone();
2482            window.on_mouse_event(move |event: &PinchEvent, phase, window, cx| {
2483                listener(event, phase, &hitbox, window, cx);
2484            })
2485        }
2486
2487        if self.hover_style.is_some()
2488            || self.base_style.mouse_cursor.is_some()
2489            || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
2490        {
2491            let hitbox = hitbox.clone();
2492            let was_hovered = hitbox.is_hovered(window);
2493            let current_view = window.current_view();
2494            window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2495                let hovered = hitbox.is_hovered(window);
2496                if phase == DispatchPhase::Capture && hovered != was_hovered {
2497                    cx.notify(current_view);
2498                }
2499            });
2500        }
2501        let drag_cursor_style = self.base_style.as_ref().mouse_cursor;
2502
2503        let mut drag_listener = mem::take(&mut self.drag_listener);
2504        let drop_listeners = mem::take(&mut self.drop_listeners);
2505        let click_listeners = mem::take(&mut self.click_listeners);
2506        let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
2507
2508        if !drop_listeners.is_empty() {
2509            let hitbox = hitbox.clone();
2510            window.on_mouse_event({
2511                move |_: &MouseUpEvent, phase, window, cx| {
2512                    if let Some(drag) = &cx.active_drag
2513                        && phase == DispatchPhase::Bubble
2514                        && hitbox.is_hovered(window)
2515                    {
2516                        let drag_state_type = drag.value.as_ref().type_id();
2517                        for (drop_state_type, listener) in &drop_listeners {
2518                            if *drop_state_type == drag_state_type {
2519                                let drag = cx
2520                                    .active_drag
2521                                    .take()
2522                                    .expect("checked for type drag state type above");
2523
2524                                let mut can_drop = true;
2525                                if let Some(predicate) = &can_drop_predicate {
2526                                    can_drop = predicate(drag.value.as_ref(), window, cx);
2527                                }
2528
2529                                if can_drop {
2530                                    listener(drag.value.as_ref(), window, cx);
2531                                    window.refresh();
2532                                    cx.stop_propagation();
2533                                }
2534                            }
2535                        }
2536                    }
2537                }
2538            });
2539        }
2540
2541        if let Some(element_state) = element_state {
2542            if !click_listeners.is_empty() || drag_listener.is_some() {
2543                let pending_mouse_down = element_state
2544                    .pending_mouse_down
2545                    .get_or_insert_with(Default::default)
2546                    .clone();
2547
2548                let clicked_state = element_state
2549                    .clicked_state
2550                    .get_or_insert_with(Default::default)
2551                    .clone();
2552
2553                window.on_mouse_event({
2554                    let pending_mouse_down = pending_mouse_down.clone();
2555                    let hitbox = hitbox.clone();
2556                    move |event: &MouseDownEvent, phase, window, _cx| {
2557                        if phase == DispatchPhase::Bubble
2558                            && event.button == MouseButton::Left
2559                            && hitbox.is_hovered(window)
2560                        {
2561                            *pending_mouse_down.borrow_mut() = Some(event.clone());
2562                            window.refresh();
2563                        }
2564                    }
2565                });
2566
2567                window.on_mouse_event({
2568                    let pending_mouse_down = pending_mouse_down.clone();
2569                    let hitbox = hitbox.clone();
2570                    move |event: &MouseMoveEvent, phase, window, cx| {
2571                        if phase == DispatchPhase::Capture {
2572                            return;
2573                        }
2574
2575                        let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2576                        if let Some(mouse_down) = pending_mouse_down.clone()
2577                            && !cx.has_active_drag()
2578                            && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
2579                            && let Some((drag_value, drag_listener)) = drag_listener.take()
2580                        {
2581                            *clicked_state.borrow_mut() = ElementClickedState::default();
2582                            let cursor_offset = event.position - hitbox.origin;
2583                            let drag =
2584                                (drag_listener)(drag_value.as_ref(), cursor_offset, window, cx);
2585                            cx.active_drag = Some(AnyDrag {
2586                                view: drag,
2587                                value: drag_value,
2588                                cursor_offset,
2589                                cursor_style: drag_cursor_style,
2590                            });
2591                            pending_mouse_down.take();
2592                            window.refresh();
2593                            cx.stop_propagation();
2594                        }
2595                    }
2596                });
2597
2598                if is_focused {
2599                    // Press enter, space to trigger click, when the element is focused.
2600                    window.on_key_event({
2601                        let click_listeners = click_listeners.clone();
2602                        let hitbox = hitbox.clone();
2603                        move |event: &KeyUpEvent, phase, window, cx| {
2604                            if phase.bubble() && !window.default_prevented() {
2605                                let stroke = &event.keystroke;
2606                                let keyboard_button = if stroke.key.eq("enter") {
2607                                    Some(KeyboardButton::Enter)
2608                                } else if stroke.key.eq("space") {
2609                                    Some(KeyboardButton::Space)
2610                                } else {
2611                                    None
2612                                };
2613
2614                                if let Some(button) = keyboard_button
2615                                    && !stroke.modifiers.modified()
2616                                {
2617                                    let click_event = ClickEvent::Keyboard(KeyboardClickEvent {
2618                                        button,
2619                                        bounds: hitbox.bounds,
2620                                    });
2621
2622                                    for listener in &click_listeners {
2623                                        listener(&click_event, window, cx);
2624                                    }
2625                                }
2626                            }
2627                        }
2628                    });
2629                }
2630
2631                window.on_mouse_event({
2632                    let mut captured_mouse_down = None;
2633                    let hitbox = hitbox.clone();
2634                    move |event: &MouseUpEvent, phase, window, cx| match phase {
2635                        // Clear the pending mouse down during the capture phase,
2636                        // so that it happens even if another event handler stops
2637                        // propagation.
2638                        DispatchPhase::Capture => {
2639                            let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2640                            if pending_mouse_down.is_some() && hitbox.is_hovered(window) {
2641                                captured_mouse_down = pending_mouse_down.take();
2642                                window.refresh();
2643                            } else if pending_mouse_down.is_some() {
2644                                // Clear the pending mouse down event (without firing click handlers)
2645                                // if the hitbox is not being hovered.
2646                                // This avoids dragging elements that changed their position
2647                                // immediately after being clicked.
2648                                // See https://github.com/zed-industries/zed/issues/24600 for more details
2649                                pending_mouse_down.take();
2650                                window.refresh();
2651                            }
2652                        }
2653                        // Fire click handlers during the bubble phase.
2654                        DispatchPhase::Bubble => {
2655                            if let Some(mouse_down) = captured_mouse_down.take() {
2656                                let mouse_click = ClickEvent::Mouse(MouseClickEvent {
2657                                    down: mouse_down,
2658                                    up: event.clone(),
2659                                });
2660                                for listener in &click_listeners {
2661                                    listener(&mouse_click, window, cx);
2662                                }
2663                            }
2664                        }
2665                    }
2666                });
2667            }
2668
2669            if let Some(hover_listener) = self.hover_listener.take() {
2670                let was_hovered = element_state
2671                    .hover_state
2672                    .get_or_insert_with(Default::default)
2673                    .clone();
2674                let has_mouse_down = element_state
2675                    .pending_mouse_down
2676                    .get_or_insert_with(Default::default)
2677                    .clone();
2678                let hover_listener = Rc::new(hover_listener);
2679                let hover_listener_state = was_hovered.clone();
2680                let update_hover = move |is_hovered: bool, window: &mut Window, cx: &mut App| {
2681                    let mut was_hovered = hover_listener_state.borrow_mut();
2682                    if is_hovered != *was_hovered {
2683                        *was_hovered = is_hovered;
2684                        drop(was_hovered);
2685                        hover_listener(&is_hovered, window, cx);
2686                    }
2687                };
2688
2689                if has_mouse_down.borrow().is_none() {
2690                    let is_hovered = !cx.has_active_drag() && hitbox.is_hovered(window);
2691                    if is_hovered != *was_hovered.borrow() {
2692                        let update_hover = update_hover.clone();
2693                        window.defer(cx, move |window, cx| {
2694                            update_hover(is_hovered, window, cx);
2695                        });
2696                    }
2697                }
2698
2699                window.on_mouse_event({
2700                    let update_hover = update_hover.clone();
2701                    let hitbox = hitbox.clone();
2702                    move |_: &MouseMoveEvent, phase, window, cx| {
2703                        if phase == DispatchPhase::Bubble {
2704                            let is_hovered = has_mouse_down.borrow().is_none()
2705                                && !cx.has_active_drag()
2706                                && hitbox.is_hovered(window);
2707                            update_hover(is_hovered, window, cx);
2708                        }
2709                    }
2710                });
2711
2712                // The pointer can leave the window without a final MouseMove, so also
2713                // clear hover on MouseExited.
2714                window.on_mouse_event(move |_: &MouseExitEvent, phase, window, cx| {
2715                    if phase == DispatchPhase::Bubble {
2716                        update_hover(false, window, cx);
2717                    }
2718                });
2719            }
2720
2721            if let Some(tooltip_builder) = self.tooltip_builder.take() {
2722                let active_tooltip = element_state
2723                    .active_tooltip
2724                    .get_or_insert_with(Default::default)
2725                    .clone();
2726                let pending_mouse_down = element_state
2727                    .pending_mouse_down
2728                    .get_or_insert_with(Default::default)
2729                    .clone();
2730
2731                let tooltip_is_hoverable = tooltip_builder.hoverable;
2732                let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| {
2733                    Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable))
2734                });
2735                // Use bounds instead of testing hitbox since this is called during prepaint.
2736                let check_is_hovered_during_prepaint = Rc::new({
2737                    let pending_mouse_down = pending_mouse_down.clone();
2738                    let source_bounds = hitbox.bounds;
2739                    move |window: &Window| {
2740                        pending_mouse_down.borrow().is_none()
2741                            && source_bounds.contains(&window.mouse_position())
2742                    }
2743                });
2744                let check_is_hovered = Rc::new({
2745                    let hitbox = hitbox.clone();
2746                    move |window: &Window| {
2747                        pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window)
2748                    }
2749                });
2750                register_tooltip_mouse_handlers(
2751                    &active_tooltip,
2752                    self.tooltip_id,
2753                    build_tooltip,
2754                    check_is_hovered,
2755                    check_is_hovered_during_prepaint,
2756                    self.tooltip_show_delay,
2757                    window,
2758                );
2759            }
2760
2761            let active_state = element_state
2762                .clicked_state
2763                .get_or_insert_with(Default::default)
2764                .clone();
2765            if active_state.borrow().is_clicked() {
2766                window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| {
2767                    if phase == DispatchPhase::Capture {
2768                        *active_state.borrow_mut() = ElementClickedState::default();
2769                        window.refresh();
2770                    }
2771                });
2772            } else {
2773                let active_group_hitbox = self
2774                    .group_active_style
2775                    .as_ref()
2776                    .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
2777                let hitbox = hitbox.clone();
2778                window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| {
2779                    if phase == DispatchPhase::Bubble && !window.default_prevented() {
2780                        let group_hovered = active_group_hitbox
2781                            .is_some_and(|group_hitbox_id| group_hitbox_id.is_hovered(window));
2782                        let element_hovered = hitbox.is_hovered(window);
2783                        if group_hovered || element_hovered {
2784                            *active_state.borrow_mut() = ElementClickedState {
2785                                group: group_hovered,
2786                                element: element_hovered,
2787                            };
2788                            window.refresh();
2789                        }
2790                    }
2791                });
2792            }
2793        }
2794    }
2795
2796    fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) {
2797        let key_down_listeners = mem::take(&mut self.key_down_listeners);
2798        let key_up_listeners = mem::take(&mut self.key_up_listeners);
2799        let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
2800        let action_listeners = mem::take(&mut self.action_listeners);
2801        if let Some(context) = self.key_context.clone() {
2802            window.set_key_context(context);
2803        }
2804
2805        for listener in key_down_listeners {
2806            window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| {
2807                listener(event, phase, window, cx);
2808            })
2809        }
2810
2811        for listener in key_up_listeners {
2812            window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| {
2813                listener(event, phase, window, cx);
2814            })
2815        }
2816
2817        for listener in modifiers_changed_listeners {
2818            window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| {
2819                listener(event, window, cx);
2820            })
2821        }
2822
2823        for (action_type, listener) in action_listeners {
2824            window.on_action(action_type, listener)
2825        }
2826    }
2827
2828    fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) {
2829        let group_hitbox = self
2830            .group_hover_style
2831            .as_ref()
2832            .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
2833
2834        if let Some(group_hitbox) = group_hitbox {
2835            let was_hovered = group_hitbox.is_hovered(window);
2836            let current_view = window.current_view();
2837            window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2838                let hovered = group_hitbox.is_hovered(window);
2839                if phase == DispatchPhase::Capture && hovered != was_hovered {
2840                    cx.notify(current_view);
2841                }
2842            });
2843        }
2844    }
2845
2846    fn paint_scroll_listener(
2847        &self,
2848        hitbox: &Hitbox,
2849        style: &Style,
2850        window: &mut Window,
2851        _cx: &mut App,
2852    ) {
2853        if let Some(scroll_offset) = self.scroll_offset.clone() {
2854            let ongoing_scroll = self.ongoing_scroll.clone();
2855            let overflow = style.overflow;
2856            let allow_concurrent_scroll = style.allow_concurrent_scroll;
2857            let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
2858            let line_height = window.line_height();
2859            let hitbox = hitbox.clone();
2860            let current_view = window.current_view();
2861            window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2862                if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
2863                    let mut scroll_offset = scroll_offset.borrow_mut();
2864                    let old_scroll_offset = *scroll_offset;
2865                    let mut delta = event.delta.pixel_delta(line_height);
2866
2867                    if restrict_scroll_to_axis
2868                        && event.delta.precise()
2869                        && let Some(ongoing_scroll) = &ongoing_scroll
2870                    {
2871                        ongoing_scroll
2872                            .borrow_mut()
2873                            .filter(&mut delta, event.touch_phase);
2874                    }
2875
2876                    let mut delta_x = match overflow.x {
2877                        Overflow::Scroll if !delta.x.is_zero() => delta.x,
2878                        Overflow::Scroll
2879                            if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll =>
2880                        {
2881                            delta.y
2882                        }
2883                        _ => Pixels::ZERO,
2884                    };
2885                    let mut delta_y = match overflow.y {
2886                        Overflow::Scroll if !delta.y.is_zero() => delta.y,
2887                        Overflow::Scroll
2888                            if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll =>
2889                        {
2890                            delta.x
2891                        }
2892                        _ => Pixels::ZERO,
2893                    };
2894                    if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
2895                        if delta_x.abs() > delta_y.abs() {
2896                            delta_y = Pixels::ZERO;
2897                        } else {
2898                            delta_x = Pixels::ZERO;
2899                        }
2900                    }
2901                    scroll_offset.y += delta_y;
2902                    scroll_offset.x += delta_x;
2903                    if *scroll_offset != old_scroll_offset {
2904                        cx.notify(current_view);
2905                    }
2906                }
2907            });
2908        }
2909    }
2910
2911    /// Compute the visual style for this element, based on the current bounds and the element's state.
2912    pub fn compute_style(
2913        &self,
2914        global_id: Option<&GlobalElementId>,
2915        hitbox: Option<&Hitbox>,
2916        window: &mut Window,
2917        cx: &mut App,
2918    ) -> Style {
2919        window.with_optional_element_state(global_id, |element_state, window| {
2920            let mut element_state =
2921                element_state.map(|element_state| element_state.unwrap_or_default());
2922            let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2923            (style, element_state)
2924        })
2925    }
2926
2927    /// Called from internal methods that have already called with_element_state.
2928    fn compute_style_internal(
2929        &self,
2930        hitbox: Option<&Hitbox>,
2931        element_state: Option<&mut InteractiveElementState>,
2932        window: &mut Window,
2933        cx: &mut App,
2934    ) -> Style {
2935        let mut style = Style::default();
2936        style.refine(&self.base_style);
2937
2938        if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2939            if let Some(in_focus_style) = self.in_focus_style.as_ref()
2940                && focus_handle.within_focused(window, cx)
2941            {
2942                style.refine(in_focus_style);
2943            }
2944
2945            if let Some(focus_style) = self.focus_style.as_ref()
2946                && focus_handle.is_focused(window)
2947            {
2948                style.refine(focus_style);
2949            }
2950        }
2951
2952        if let Some(hitbox) = hitbox {
2953            if !cx.has_active_drag() {
2954                if let Some(group_hover) = self.group_hover_style.as_ref()
2955                    && let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx)
2956                    && group_hitbox_id.is_hovered(window)
2957                {
2958                    style.refine(&group_hover.style);
2959                }
2960
2961                if let Some(hover_style) = self.hover_style.as_ref()
2962                    && hitbox.is_hovered(window)
2963                {
2964                    style.refine(hover_style);
2965                }
2966            }
2967
2968            if let Some(drag) = cx.active_drag.take() {
2969                let mut can_drop = true;
2970                if let Some(can_drop_predicate) = &self.can_drop_predicate {
2971                    can_drop = can_drop_predicate(drag.value.as_ref(), window, cx);
2972                }
2973
2974                if can_drop {
2975                    for (state_type, group_drag_style) in &self.group_drag_over_styles {
2976                        if let Some(group_hitbox_id) =
2977                            GroupHitboxes::get(&group_drag_style.group, cx)
2978                            && *state_type == drag.value.as_ref().type_id()
2979                            && group_hitbox_id.is_hovered(window)
2980                        {
2981                            style.refine(&group_drag_style.style);
2982                        }
2983                    }
2984
2985                    for (state_type, build_drag_over_style) in &self.drag_over_styles {
2986                        if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window)
2987                        {
2988                            style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx));
2989                        }
2990                    }
2991                }
2992
2993                style.mouse_cursor = drag.cursor_style;
2994                cx.active_drag = Some(drag);
2995            }
2996        }
2997
2998        if let Some(element_state) = element_state {
2999            let clicked_state = element_state
3000                .clicked_state
3001                .get_or_insert_with(Default::default)
3002                .borrow();
3003            if clicked_state.group
3004                && let Some(group) = self.group_active_style.as_ref()
3005            {
3006                style.refine(&group.style)
3007            }
3008
3009            if let Some(active_style) = self.active_style.as_ref()
3010                && clicked_state.element
3011            {
3012                style.refine(active_style)
3013            }
3014        }
3015
3016        style
3017    }
3018
3019    pub(crate) fn write_a11y_info(&self, node: &mut accesskit::Node) {
3020        if let Some(id) = &self.accessibility_id {
3021            node.set_author_id(id.to_string());
3022        }
3023        if let Some(label) = &self.aria_label {
3024            node.set_label(label.to_string());
3025        }
3026        if let Some(description) = &self.aria_description {
3027            node.set_description(description.to_string());
3028        }
3029        if let Some(value) = &self.aria_value {
3030            node.set_value(value.to_string());
3031        }
3032        if let Some(placeholder) = &self.aria_placeholder {
3033            node.set_placeholder(placeholder.to_string());
3034        }
3035        if self.aria_disabled {
3036            node.set_disabled();
3037        }
3038        if self.aria_required {
3039            node.set_required();
3040        }
3041        if let Some(invalid) = self.aria_invalid {
3042            node.set_invalid(invalid);
3043        }
3044        if self.aria_modal {
3045            node.set_modal();
3046        }
3047        if let Some(keyshortcuts) = &self.aria_keyshortcuts {
3048            node.set_keyboard_shortcut(keyshortcuts.to_string());
3049        }
3050        if let Some(selected) = self.aria_selected {
3051            node.set_selected(selected);
3052        }
3053        if let Some(expanded) = self.aria_expanded {
3054            node.set_expanded(expanded);
3055        }
3056        if let Some(toggled) = self.aria_toggled {
3057            node.set_toggled(toggled);
3058        }
3059        if let Some(value) = self.aria_numeric_value {
3060            node.set_numeric_value(value);
3061        }
3062        if let Some(value) = self.aria_min_numeric_value {
3063            node.set_min_numeric_value(value);
3064        }
3065        if let Some(value) = self.aria_max_numeric_value {
3066            node.set_max_numeric_value(value);
3067        }
3068        if let Some(orientation) = self.aria_orientation {
3069            node.set_orientation(orientation);
3070        }
3071        if let Some(level) = self.aria_level {
3072            node.set_level(level);
3073        }
3074        if let Some(position) = self.aria_position_in_set {
3075            node.set_position_in_set(position);
3076        }
3077        if let Some(size) = self.aria_size_of_set {
3078            node.set_size_of_set(size);
3079        }
3080        if let Some(index) = self.aria_row_index {
3081            node.set_row_index(index);
3082        }
3083        if let Some(index) = self.aria_column_index {
3084            node.set_column_index(index);
3085        }
3086        if let Some(count) = self.aria_row_count {
3087            node.set_row_count(count);
3088        }
3089        if let Some(count) = self.aria_column_count {
3090            node.set_column_count(count);
3091        }
3092        if !self.click_listeners.is_empty() {
3093            node.add_action(accesskit::Action::Click);
3094        }
3095        if self.tracked_focus_handle.is_some() || self.focusable {
3096            node.add_action(accesskit::Action::Focus);
3097        }
3098        for (action, _) in &self.a11y_action_listeners {
3099            node.add_action(*action);
3100        }
3101    }
3102}
3103
3104/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks
3105/// and scroll offsets.
3106#[derive(Default)]
3107pub struct InteractiveElementState {
3108    pub(crate) focus_handle: Option<FocusHandle>,
3109    pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
3110    pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
3111    pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
3112    pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
3113    ongoing_scroll: Option<Rc<RefCell<OngoingScroll>>>,
3114    pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
3115    pub(crate) prev_bounds: Option<Bounds<Pixels>>,
3116}
3117
3118/// Whether or not the element or a group that contains it is clicked by the mouse.
3119#[derive(Copy, Clone, Default, Eq, PartialEq)]
3120pub struct ElementClickedState {
3121    /// True if this element's group has been clicked, false otherwise
3122    pub group: bool,
3123
3124    /// True if this element has been clicked, false otherwise
3125    pub element: bool,
3126}
3127
3128impl ElementClickedState {
3129    fn is_clicked(&self) -> bool {
3130        self.group || self.element
3131    }
3132}
3133
3134pub(crate) enum ActiveTooltip {
3135    /// Currently delaying before showing the tooltip.
3136    WaitingForShow { _task: Task<()> },
3137    /// Tooltip is visible, element was hovered or for hoverable tooltips, the tooltip was hovered.
3138    Visible {
3139        tooltip: AnyTooltip,
3140        is_hoverable: bool,
3141    },
3142    /// Tooltip is visible and hoverable, but the mouse is no longer hovering. Currently delaying
3143    /// before hiding it.
3144    WaitingForHide {
3145        tooltip: AnyTooltip,
3146        _task: Task<()>,
3147    },
3148}
3149
3150pub(crate) fn clear_active_tooltip(
3151    active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3152    window: &mut Window,
3153) {
3154    match active_tooltip.borrow_mut().take() {
3155        None => {}
3156        Some(ActiveTooltip::WaitingForShow { .. }) => {}
3157        Some(ActiveTooltip::Visible { .. }) => window.refresh(),
3158        Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(),
3159    }
3160}
3161
3162pub(crate) fn clear_active_tooltip_if_not_hoverable(
3163    active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3164    window: &mut Window,
3165) {
3166    let should_clear = match active_tooltip.borrow().as_ref() {
3167        None => false,
3168        Some(ActiveTooltip::WaitingForShow { .. }) => false,
3169        Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable,
3170        Some(ActiveTooltip::WaitingForHide { .. }) => false,
3171    };
3172    if should_clear {
3173        active_tooltip.borrow_mut().take();
3174        window.refresh();
3175    }
3176}
3177
3178pub(crate) fn set_tooltip_on_window(
3179    active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3180    window: &mut Window,
3181) -> Option<TooltipId> {
3182    let tooltip = match active_tooltip.borrow().as_ref() {
3183        None => return None,
3184        Some(ActiveTooltip::WaitingForShow { .. }) => return None,
3185        Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(),
3186        Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(),
3187    };
3188    Some(window.set_tooltip(tooltip))
3189}
3190
3191pub(crate) fn register_tooltip_mouse_handlers(
3192    active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3193    tooltip_id: Option<TooltipId>,
3194    build_tooltip: Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3195    check_is_hovered: Rc<dyn Fn(&Window) -> bool>,
3196    check_is_hovered_during_prepaint: Rc<dyn Fn(&Window) -> bool>,
3197    show_delay: Option<Duration>,
3198    window: &mut Window,
3199) {
3200    let current_view = window.current_view();
3201    let show_delay = show_delay.unwrap_or(DEFAULT_TOOLTIP_SHOW_DELAY);
3202
3203    window.on_mouse_event({
3204        let active_tooltip = active_tooltip.clone();
3205        let build_tooltip = build_tooltip.clone();
3206        let check_is_hovered = check_is_hovered.clone();
3207        move |_: &MouseMoveEvent, phase, window, cx| {
3208            handle_tooltip_mouse_move(
3209                &active_tooltip,
3210                &build_tooltip,
3211                &check_is_hovered,
3212                &check_is_hovered_during_prepaint,
3213                tooltip_id,
3214                current_view,
3215                phase,
3216                show_delay,
3217                window,
3218                cx,
3219            )
3220        }
3221    });
3222
3223    window.on_mouse_event({
3224        let active_tooltip = active_tooltip.clone();
3225        move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| {
3226            if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3227                clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3228            }
3229        }
3230    });
3231
3232    window.on_mouse_event({
3233        let active_tooltip = active_tooltip.clone();
3234        move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| {
3235            if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
3236                clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
3237            }
3238        }
3239    });
3240}
3241
3242/// Handles displaying tooltips when an element is hovered.
3243///
3244/// The mouse hovering logic also relies on being called from window prepaint in order to handle the
3245/// case where the element the tooltip is on is not rendered - in that case its mouse listeners are
3246/// also not registered. During window prepaint, the hitbox information is not available, so
3247/// `check_is_hovered_during_prepaint` is used which bases the check off of the absolute bounds of
3248/// the element.
3249///
3250/// TODO: There's a minor bug due to the use of absolute bounds while checking during prepaint - it
3251/// does not know if the hitbox is occluded. In the case where a tooltip gets displayed and then
3252/// gets occluded after display, it will stick around until the mouse exits the hover bounds.
3253fn handle_tooltip_mouse_move(
3254    active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3255    build_tooltip: &Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
3256    check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3257    check_is_hovered_during_prepaint: &Rc<dyn Fn(&Window) -> bool>,
3258    tooltip_id: Option<TooltipId>,
3259    current_view: EntityId,
3260    phase: DispatchPhase,
3261    show_delay: Duration,
3262    window: &mut Window,
3263    cx: &mut App,
3264) {
3265    // Separates logic for what mutation should occur from applying it, to avoid overlapping
3266    // RefCell borrows.
3267    enum Action {
3268        None,
3269        CancelShow,
3270        ScheduleShow,
3271        CheckVisible,
3272    }
3273
3274    let action = match active_tooltip.borrow().as_ref() {
3275        None => {
3276            let is_hovered = check_is_hovered(window);
3277            if is_hovered && phase.bubble() {
3278                Action::ScheduleShow
3279            } else {
3280                Action::None
3281            }
3282        }
3283        Some(ActiveTooltip::WaitingForShow { .. }) => {
3284            let is_hovered = check_is_hovered(window);
3285            if is_hovered {
3286                Action::None
3287            } else {
3288                Action::CancelShow
3289            }
3290        }
3291        Some(ActiveTooltip::Visible { is_hoverable, .. }) => {
3292            if phase.capture()
3293                && !check_is_hovered(window)
3294                && (!*is_hoverable
3295                    || !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)))
3296            {
3297                Action::CheckVisible
3298            } else {
3299                Action::None
3300            }
3301        }
3302        Some(ActiveTooltip::WaitingForHide { .. }) => {
3303            if phase.capture()
3304                && (check_is_hovered(window)
3305                    || tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)))
3306            {
3307                Action::CheckVisible
3308            } else {
3309                Action::None
3310            }
3311        }
3312    };
3313
3314    match action {
3315        Action::None => {}
3316        Action::CancelShow => {
3317            // Cancel waiting to show tooltip when it is no longer hovered.
3318            active_tooltip.borrow_mut().take();
3319        }
3320        Action::ScheduleShow => {
3321            let delayed_show_task = window.spawn(cx, {
3322                let weak_active_tooltip = Rc::downgrade(active_tooltip);
3323                let build_tooltip = build_tooltip.clone();
3324                let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone();
3325                async move |cx| {
3326                    cx.background_executor().timer(show_delay).await;
3327                    let Some(active_tooltip) = weak_active_tooltip.upgrade() else {
3328                        return;
3329                    };
3330                    cx.update(|window, cx| {
3331                        let new_tooltip =
3332                            build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| {
3333                                let weak_active_tooltip = Rc::downgrade(&active_tooltip);
3334                                ActiveTooltip::Visible {
3335                                    tooltip: AnyTooltip {
3336                                        view,
3337                                        mouse_position: window.mouse_position(),
3338                                        check_visible_and_update: Rc::new(
3339                                            move |tooltip_bounds, window, cx| {
3340                                                let Some(active_tooltip) =
3341                                                    weak_active_tooltip.upgrade()
3342                                                else {
3343                                                    return false;
3344                                                };
3345                                                handle_tooltip_check_visible_and_update(
3346                                                    &active_tooltip,
3347                                                    tooltip_is_hoverable,
3348                                                    &check_is_hovered_during_prepaint,
3349                                                    tooltip_bounds,
3350                                                    window,
3351                                                    cx,
3352                                                )
3353                                            },
3354                                        ),
3355                                    },
3356                                    is_hoverable: tooltip_is_hoverable,
3357                                }
3358                            });
3359                        *active_tooltip.borrow_mut() = new_tooltip;
3360                        window.refresh();
3361                    })
3362                    .ok();
3363                }
3364            });
3365            active_tooltip
3366                .borrow_mut()
3367                .replace(ActiveTooltip::WaitingForShow {
3368                    _task: delayed_show_task,
3369                });
3370        }
3371        Action::CheckVisible => cx.notify(current_view),
3372    }
3373}
3374
3375/// Returns a callback which will be called by window prepaint to update tooltip visibility. The
3376/// purpose of doing this logic here instead of the mouse move handler is that the mouse move
3377/// handler won't get called when the element is not painted (e.g. via use of `visible_on_hover`).
3378fn handle_tooltip_check_visible_and_update(
3379    active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
3380    tooltip_is_hoverable: bool,
3381    check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
3382    tooltip_bounds: Bounds<Pixels>,
3383    window: &mut Window,
3384    cx: &mut App,
3385) -> bool {
3386    // Separates logic for what mutation should occur from applying it, to avoid overlapping RefCell
3387    // borrows.
3388    enum Action {
3389        None,
3390        Hide,
3391        ScheduleHide(AnyTooltip),
3392        CancelHide(AnyTooltip),
3393    }
3394
3395    let is_hovered = check_is_hovered(window)
3396        || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position()));
3397    let action = match active_tooltip.borrow().as_ref() {
3398        Some(ActiveTooltip::Visible { tooltip, .. }) => {
3399            if is_hovered {
3400                Action::None
3401            } else {
3402                if tooltip_is_hoverable {
3403                    Action::ScheduleHide(tooltip.clone())
3404                } else {
3405                    Action::Hide
3406                }
3407            }
3408        }
3409        Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => {
3410            if is_hovered {
3411                Action::CancelHide(tooltip.clone())
3412            } else {
3413                Action::None
3414            }
3415        }
3416        None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None,
3417    };
3418
3419    match action {
3420        Action::None => {}
3421        Action::Hide => clear_active_tooltip(active_tooltip, window),
3422        Action::ScheduleHide(tooltip) => {
3423            let delayed_hide_task = window.spawn(cx, {
3424                let active_tooltip = active_tooltip.clone();
3425                async move |cx| {
3426                    cx.background_executor()
3427                        .timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
3428                        .await;
3429                    if active_tooltip.borrow_mut().take().is_some() {
3430                        cx.update(|window, _cx| window.refresh()).ok();
3431                    }
3432                }
3433            });
3434            active_tooltip
3435                .borrow_mut()
3436                .replace(ActiveTooltip::WaitingForHide {
3437                    tooltip,
3438                    _task: delayed_hide_task,
3439                });
3440        }
3441        Action::CancelHide(tooltip) => {
3442            // Cancel waiting to hide tooltip when it becomes hovered.
3443            active_tooltip.borrow_mut().replace(ActiveTooltip::Visible {
3444                tooltip,
3445                is_hoverable: true,
3446            });
3447        }
3448    }
3449
3450    active_tooltip.borrow().is_some()
3451}
3452
3453#[derive(Default)]
3454pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
3455
3456impl Global for GroupHitboxes {}
3457
3458impl GroupHitboxes {
3459    pub fn get(name: &SharedString, cx: &mut App) -> Option<HitboxId> {
3460        cx.default_global::<Self>()
3461            .0
3462            .get(name)
3463            .and_then(|bounds_stack| bounds_stack.last())
3464            .cloned()
3465    }
3466
3467    pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) {
3468        cx.default_global::<Self>()
3469            .0
3470            .entry(name)
3471            .or_default()
3472            .push(hitbox_id);
3473    }
3474
3475    pub fn pop(name: &SharedString, cx: &mut App) {
3476        cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
3477    }
3478}
3479
3480/// A wrapper around an element that can store state, produced after assigning an ElementId.
3481pub struct Stateful<E> {
3482    pub(crate) element: E,
3483}
3484
3485impl<E> Styled for Stateful<E>
3486where
3487    E: Styled,
3488{
3489    fn style(&mut self) -> &mut StyleRefinement {
3490        self.element.style()
3491    }
3492}
3493
3494impl<E> StatefulInteractiveElement for Stateful<E>
3495where
3496    E: Element,
3497    Self: InteractiveElement,
3498{
3499}
3500
3501impl<E> InteractiveElement for Stateful<E>
3502where
3503    E: InteractiveElement,
3504{
3505    fn interactivity(&mut self) -> &mut Interactivity {
3506        self.element.interactivity()
3507    }
3508}
3509
3510impl<E> Element for Stateful<E>
3511where
3512    E: Element,
3513{
3514    type RequestLayoutState = E::RequestLayoutState;
3515    type PrepaintState = E::PrepaintState;
3516
3517    fn id(&self) -> Option<ElementId> {
3518        self.element.id()
3519    }
3520
3521    fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
3522        self.element.source_location()
3523    }
3524
3525    fn a11y_role(&self) -> Option<accesskit::Role> {
3526        self.element.a11y_role()
3527    }
3528
3529    fn write_a11y_info(&self, node: &mut accesskit::Node) {
3530        self.element.write_a11y_info(node);
3531    }
3532
3533    fn request_layout(
3534        &mut self,
3535        id: Option<&GlobalElementId>,
3536        inspector_id: Option<&InspectorElementId>,
3537        window: &mut Window,
3538        cx: &mut App,
3539    ) -> (LayoutId, Self::RequestLayoutState) {
3540        self.element.request_layout(id, inspector_id, window, cx)
3541    }
3542
3543    fn prepaint(
3544        &mut self,
3545        id: Option<&GlobalElementId>,
3546        inspector_id: Option<&InspectorElementId>,
3547        bounds: Bounds<Pixels>,
3548        state: &mut Self::RequestLayoutState,
3549        window: &mut Window,
3550        cx: &mut App,
3551    ) -> E::PrepaintState {
3552        self.element
3553            .prepaint(id, inspector_id, bounds, state, window, cx)
3554    }
3555
3556    fn paint(
3557        &mut self,
3558        id: Option<&GlobalElementId>,
3559        inspector_id: Option<&InspectorElementId>,
3560        bounds: Bounds<Pixels>,
3561        request_layout: &mut Self::RequestLayoutState,
3562        prepaint: &mut Self::PrepaintState,
3563        window: &mut Window,
3564        cx: &mut App,
3565    ) {
3566        self.element.paint(
3567            id,
3568            inspector_id,
3569            bounds,
3570            request_layout,
3571            prepaint,
3572            window,
3573            cx,
3574        );
3575    }
3576}
3577
3578impl<E> IntoElement for Stateful<E>
3579where
3580    E: Element,
3581{
3582    type Element = Self;
3583
3584    fn into_element(self) -> Self::Element {
3585        self
3586    }
3587}
3588
3589impl<E> ParentElement for Stateful<E>
3590where
3591    E: ParentElement,
3592{
3593    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
3594        self.element.extend(elements)
3595    }
3596}
3597
3598/// Represents an element that can be scrolled *to* in its parent element.
3599/// Contrary to `ScrollHandle::scroll_to_active_item`, an anchored element does not have to be an immediate child of the parent.
3600#[derive(Clone)]
3601pub struct ScrollAnchor {
3602    handle: ScrollHandle,
3603    last_origin: Rc<RefCell<Point<Pixels>>>,
3604}
3605
3606impl ScrollAnchor {
3607    /// Creates a [ScrollAnchor] associated with a given [ScrollHandle].
3608    pub fn for_handle(handle: ScrollHandle) -> Self {
3609        Self {
3610            handle,
3611            last_origin: Default::default(),
3612        }
3613    }
3614    /// Request scroll to this item on the next frame.
3615    pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) {
3616        let this = self.clone();
3617
3618        window.on_next_frame(move |_, _| {
3619            let viewport_bounds = this.handle.bounds();
3620            let self_bounds = *this.last_origin.borrow();
3621            this.handle.set_offset(viewport_bounds.origin - self_bounds);
3622        });
3623    }
3624}
3625
3626#[derive(Default, Debug)]
3627struct ScrollHandleState {
3628    offset: Rc<RefCell<Point<Pixels>>>,
3629    ongoing_scroll: Rc<RefCell<OngoingScroll>>,
3630    bounds: Bounds<Pixels>,
3631    max_offset: Size<Pixels>,
3632    child_bounds: Vec<Bounds<Pixels>>,
3633    scroll_to_bottom: bool,
3634    overflow: Point<Overflow>,
3635    active_item: Option<ScrollActiveItem>,
3636}
3637
3638#[derive(Default, Debug, Clone, Copy)]
3639struct ScrollActiveItem {
3640    index: usize,
3641    strategy: ScrollStrategy,
3642}
3643
3644#[derive(Default, Debug, Clone, Copy)]
3645enum ScrollStrategy {
3646    #[default]
3647    FirstVisible,
3648    Top,
3649}
3650
3651/// A handle to the scrollable aspects of an element.
3652/// Used for accessing scroll state, like the current scroll offset,
3653/// and for mutating the scroll state, like scrolling to a specific child.
3654#[derive(Clone, Debug)]
3655pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
3656
3657impl Default for ScrollHandle {
3658    fn default() -> Self {
3659        Self::new()
3660    }
3661}
3662
3663impl ScrollHandle {
3664    /// Construct a new scroll handle.
3665    pub fn new() -> Self {
3666        Self(Rc::default())
3667    }
3668
3669    /// Get the current scroll offset.
3670    pub fn offset(&self) -> Point<Pixels> {
3671        *self.0.borrow().offset.borrow()
3672    }
3673
3674    /// Get the maximum scroll offset.
3675    pub fn max_offset(&self) -> Size<Pixels> {
3676        self.0.borrow().max_offset
3677    }
3678
3679    /// Get the top child that's scrolled into view.
3680    pub fn top_item(&self) -> usize {
3681        let state = self.0.borrow();
3682        let top = state.bounds.top() - state.offset.borrow().y;
3683
3684        match state.child_bounds.binary_search_by(|bounds| {
3685            if top < bounds.top() {
3686                Ordering::Greater
3687            } else if top > bounds.bottom() {
3688                Ordering::Less
3689            } else {
3690                Ordering::Equal
3691            }
3692        }) {
3693            Ok(ix) => ix,
3694            Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
3695        }
3696    }
3697
3698    /// Get the bottom child that's scrolled into view.
3699    pub fn bottom_item(&self) -> usize {
3700        let state = self.0.borrow();
3701        let bottom = state.bounds.bottom() - state.offset.borrow().y;
3702
3703        match state.child_bounds.binary_search_by(|bounds| {
3704            if bottom < bounds.top() {
3705                Ordering::Greater
3706            } else if bottom > bounds.bottom() {
3707                Ordering::Less
3708            } else {
3709                Ordering::Equal
3710            }
3711        }) {
3712            Ok(ix) => ix,
3713            Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
3714        }
3715    }
3716
3717    /// Return the bounds into which this child is painted
3718    pub fn bounds(&self) -> Bounds<Pixels> {
3719        self.0.borrow().bounds
3720    }
3721
3722    /// Get the bounds for a specific child.
3723    pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
3724        self.0.borrow().child_bounds.get(ix).cloned()
3725    }
3726
3727    /// Update `ScrollHandleState`'s active item for scrolling to in prepaint
3728    pub fn scroll_to_item(&self, ix: usize) {
3729        let mut state = self.0.borrow_mut();
3730        state.active_item = Some(ScrollActiveItem {
3731            index: ix,
3732            strategy: ScrollStrategy::default(),
3733        });
3734    }
3735
3736    /// Update `ScrollHandleState`'s active item for scrolling to in prepaint
3737    /// This scrolls the minimal amount to ensure that the child is the first visible element
3738    pub fn scroll_to_top_of_item(&self, ix: usize) {
3739        let mut state = self.0.borrow_mut();
3740        state.active_item = Some(ScrollActiveItem {
3741            index: ix,
3742            strategy: ScrollStrategy::Top,
3743        });
3744    }
3745
3746    /// Scrolls the minimal amount to either ensure that the child is
3747    /// fully visible or the top element of the view depends on the
3748    /// scroll strategy
3749    fn scroll_to_active_item(&self) {
3750        let mut state = self.0.borrow_mut();
3751
3752        let Some(active_item) = state.active_item else {
3753            return;
3754        };
3755
3756        let active_item = match state.child_bounds.get(active_item.index) {
3757            Some(bounds) => {
3758                let mut scroll_offset = state.offset.borrow_mut();
3759
3760                match active_item.strategy {
3761                    ScrollStrategy::FirstVisible => {
3762                        if state.overflow.y == Overflow::Scroll {
3763                            if bounds.top() + scroll_offset.y < state.bounds.top() {
3764                                scroll_offset.y = state.bounds.top() - bounds.top();
3765                            } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
3766                                scroll_offset.y = state.bounds.bottom() - bounds.bottom();
3767                            }
3768                        }
3769                    }
3770                    ScrollStrategy::Top => {
3771                        scroll_offset.y = state.bounds.top() - bounds.top();
3772                    }
3773                }
3774
3775                if state.overflow.x == Overflow::Scroll {
3776                    if bounds.left() + scroll_offset.x < state.bounds.left() {
3777                        scroll_offset.x = state.bounds.left() - bounds.left();
3778                    } else if bounds.right() + scroll_offset.x > state.bounds.right() {
3779                        scroll_offset.x = state.bounds.right() - bounds.right();
3780                    }
3781                }
3782                None
3783            }
3784            None => Some(active_item),
3785        };
3786        state.active_item = active_item;
3787    }
3788
3789    /// Scrolls to the bottom.
3790    pub fn scroll_to_bottom(&self) {
3791        let mut state = self.0.borrow_mut();
3792        state.scroll_to_bottom = true;
3793    }
3794
3795    /// Set the offset explicitly. The offset is the distance from the top left of the
3796    /// parent container to the top left of the first child.
3797    /// As you scroll further down the offset becomes more negative.
3798    pub fn set_offset(&self, mut position: Point<Pixels>) {
3799        let state = self.0.borrow();
3800        *state.offset.borrow_mut() = position;
3801    }
3802
3803    /// Get the logical scroll top, based on a child index and a pixel offset.
3804    pub fn logical_scroll_top(&self) -> (usize, Pixels) {
3805        let ix = self.top_item();
3806        let state = self.0.borrow();
3807
3808        if let Some(child_bounds) = state.child_bounds.get(ix) {
3809            (
3810                ix,
3811                child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
3812            )
3813        } else {
3814            (ix, px(0.))
3815        }
3816    }
3817
3818    /// Get the logical scroll bottom, based on a child index and a pixel offset.
3819    pub fn logical_scroll_bottom(&self) -> (usize, Pixels) {
3820        let ix = self.bottom_item();
3821        let state = self.0.borrow();
3822
3823        if let Some(child_bounds) = state.child_bounds.get(ix) {
3824            (
3825                ix,
3826                child_bounds.bottom() + state.offset.borrow().y - state.bounds.bottom(),
3827            )
3828        } else {
3829            (ix, px(0.))
3830        }
3831    }
3832
3833    /// Get the count of children for scrollable item.
3834    pub fn children_count(&self) -> usize {
3835        self.0.borrow().child_bounds.len()
3836    }
3837}
3838
3839#[cfg(test)]
3840mod tests {
3841    use super::*;
3842    use crate::{
3843        AnyWindowHandle, AppContext as _, Context, InputEvent as _, MouseButton, Render,
3844        ScrollDelta, ScrollWheelEvent, TestAppContext, fallback_prompt_renderer, hsla, point, size,
3845    };
3846    use std::{
3847        cell::{Cell, RefCell},
3848        rc::Rc,
3849    };
3850
3851    struct MouseDownOutOwner {
3852        mouse_down_out_count: Rc<Cell<usize>>,
3853    }
3854
3855    impl Render for MouseDownOutOwner {
3856        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
3857            let mouse_down_out_count = self.mouse_down_out_count.clone();
3858            div()
3859                .size_full()
3860                .child(
3861                    div()
3862                        .id("target")
3863                        .size(px(50.))
3864                        .on_mouse_down_out(move |_, _, _| {
3865                            mouse_down_out_count.set(mouse_down_out_count.get() + 1);
3866                        }),
3867                )
3868        }
3869    }
3870
3871    #[test]
3872    fn mouse_down_out_is_suppressed_while_window_prompt_is_active() {
3873        let mut test_app = TestAppContext::single();
3874        let mouse_down_out_count = Rc::new(Cell::new(0));
3875        let (_, cx) = test_app.add_window_view({
3876            let mouse_down_out_count = mouse_down_out_count.clone();
3877            move |_, _| MouseDownOutOwner {
3878                mouse_down_out_count,
3879            }
3880        });
3881
3882        cx.simulate_mouse_down(
3883            point(px(75.), px(75.)),
3884            MouseButton::Left,
3885            Default::default(),
3886        );
3887        assert_eq!(mouse_down_out_count.get(), 1);
3888
3889        cx.update(|window, cx| {
3890            cx.set_prompt_builder(fallback_prompt_renderer);
3891            let _receiver =
3892                window.prompt(crate::PromptLevel::Warning, "message", None, &["Ok"], cx);
3893        });
3894        cx.run_until_parked();
3895
3896        cx.simulate_mouse_down(
3897            point(px(75.), px(75.)),
3898            MouseButton::Left,
3899            Default::default(),
3900        );
3901        assert_eq!(
3902            mouse_down_out_count.get(),
3903            1,
3904            "mouse down over an active prompt must not reach underlying listeners"
3905        );
3906    }
3907
3908    struct HoverListenerLayoutTestView {
3909        target_left: Pixels,
3910        hover_transitions: Rc<RefCell<Vec<bool>>>,
3911    }
3912
3913    impl Render for HoverListenerLayoutTestView {
3914        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
3915            let hover_transitions = self.hover_transitions.clone();
3916            div().relative().size_full().child(
3917                div()
3918                    .id("hover-target")
3919                    .absolute()
3920                    .left(self.target_left)
3921                    .top_0()
3922                    .size(px(20.))
3923                    .on_click(|_, _, _| {})
3924                    .on_hover(move |is_hovered, _, _| {
3925                        hover_transitions.borrow_mut().push(*is_hovered);
3926                    }),
3927            )
3928        }
3929    }
3930
3931    #[gpui::test]
3932    fn hover_listeners_update_when_layout_changes_under_stationary_mouse(cx: &mut TestAppContext) {
3933        let hover_transitions = Rc::new(RefCell::new(Vec::new()));
3934        let window = cx.add_window({
3935            let hover_transitions = hover_transitions.clone();
3936            move |_, _| HoverListenerLayoutTestView {
3937                target_left: px(40.),
3938                hover_transitions,
3939            }
3940        });
3941        let any_window = AnyWindowHandle::from(window);
3942
3943        cx.update_window(any_window, |_, window, cx| {
3944            window.draw(cx).clear();
3945            window.dispatch_event(
3946                MouseMoveEvent {
3947                    position: point(px(10.), px(10.)),
3948                    modifiers: Default::default(),
3949                    pressed_button: None,
3950                }
3951                .to_platform_input(),
3952                cx,
3953            );
3954        })
3955        .unwrap();
3956        assert!(hover_transitions.borrow().is_empty());
3957
3958        window
3959            .update(cx, |view, _, cx| {
3960                view.target_left = px(0.);
3961                cx.notify();
3962            })
3963            .unwrap();
3964        cx.update_window(any_window, |_, window, cx| window.draw(cx).clear())
3965            .unwrap();
3966        assert_eq!(*hover_transitions.borrow(), [true]);
3967
3968        window
3969            .update(cx, |view, _, cx| {
3970                view.target_left = px(40.);
3971                cx.notify();
3972            })
3973            .unwrap();
3974        cx.update_window(any_window, |_, window, cx| window.draw(cx).clear())
3975            .unwrap();
3976        assert_eq!(*hover_transitions.borrow(), [true, false]);
3977    }
3978
3979    #[gpui::test]
3980    fn hover_listeners_remain_hovered_during_stationary_mouse_press(cx: &mut TestAppContext) {
3981        let hover_transitions = Rc::new(RefCell::new(Vec::new()));
3982        let window = cx.add_window({
3983            let hover_transitions = hover_transitions.clone();
3984            move |_, _| HoverListenerLayoutTestView {
3985                target_left: px(0.),
3986                hover_transitions,
3987            }
3988        });
3989        let any_window = AnyWindowHandle::from(window);
3990        let mouse_position = point(px(10.), px(10.));
3991
3992        cx.update_window(any_window, |_, window, cx| {
3993            window.draw(cx).clear();
3994            window.dispatch_event(
3995                MouseMoveEvent {
3996                    position: mouse_position,
3997                    modifiers: Default::default(),
3998                    pressed_button: None,
3999                }
4000                .to_platform_input(),
4001                cx,
4002            );
4003        })
4004        .unwrap();
4005        assert_eq!(*hover_transitions.borrow(), [true]);
4006
4007        cx.update_window(any_window, |_, window, cx| {
4008            window.dispatch_event(
4009                MouseDownEvent {
4010                    position: mouse_position,
4011                    button: MouseButton::Left,
4012                    modifiers: Default::default(),
4013                    click_count: 1,
4014                    first_mouse: false,
4015                }
4016                .to_platform_input(),
4017                cx,
4018            );
4019            window.draw(cx).clear();
4020        })
4021        .unwrap();
4022        assert_eq!(*hover_transitions.borrow(), [true]);
4023
4024        cx.update_window(any_window, |_, window, cx| {
4025            window.dispatch_event(
4026                MouseUpEvent {
4027                    position: mouse_position,
4028                    button: MouseButton::Left,
4029                    modifiers: Default::default(),
4030                    click_count: 1,
4031                }
4032                .to_platform_input(),
4033                cx,
4034            );
4035            window.draw(cx).clear();
4036        })
4037        .unwrap();
4038        assert_eq!(*hover_transitions.borrow(), [true]);
4039    }
4040
4041    #[test]
4042    fn test_tooltip_show_delay_sets_interactivity_state() {
4043        let delay = Duration::from_millis(125);
4044        let element = div().id("tooltip-target").tooltip_show_delay(delay);
4045
4046        assert_eq!(
4047            element.element.interactivity.tooltip_show_delay,
4048            Some(delay)
4049        );
4050    }
4051
4052    #[test]
4053    fn aria_description_and_keyshortcuts_are_written_to_accesskit() {
4054        let element = div()
4055            .id("save-button")
4056            .role(accesskit::Role::Button)
4057            .aria_description("Save the active document")
4058            .aria_keyshortcuts("Ctrl+S");
4059        let mut node = accesskit::Node::new(accesskit::Role::Button);
4060
4061        element.element.interactivity.write_a11y_info(&mut node);
4062
4063        assert_eq!(node.description(), Some("Save the active document"));
4064        assert_eq!(node.keyboard_shortcut(), Some("Ctrl+S"));
4065    }
4066
4067    #[test]
4068    fn accessibility_id_is_written_to_accesskit() {
4069        let element = div()
4070            .id("buffer-font-size")
4071            .accessibility_id("settings.buffer-font-size");
4072        let mut node = accesskit::Node::new(accesskit::Role::SpinButton);
4073
4074        element.element.interactivity.write_a11y_info(&mut node);
4075
4076        assert_eq!(node.author_id(), Some("settings.buffer-font-size"));
4077    }
4078
4079    #[test]
4080    fn form_control_properties_are_written_to_accesskit() {
4081        let element = div()
4082            .id("email")
4083            .role(accesskit::Role::TextInput)
4084            .aria_value("alice@example.com")
4085            .aria_placeholder("name@example.com")
4086            .aria_disabled(true)
4087            .aria_required(true)
4088            .aria_invalid(accesskit::Invalid::True)
4089            .aria_modal(true);
4090        let mut node = accesskit::Node::new(accesskit::Role::TextInput);
4091
4092        element.element.interactivity.write_a11y_info(&mut node);
4093
4094        assert_eq!(node.value(), Some("alice@example.com"));
4095        assert_eq!(node.placeholder(), Some("name@example.com"));
4096        assert!(node.is_disabled());
4097        assert!(node.is_required());
4098        assert_eq!(node.invalid(), Some(accesskit::Invalid::True));
4099        assert!(node.is_modal());
4100    }
4101
4102    #[test]
4103    fn test_shadow_sm_preset_preserves_shadow_fields() {
4104        let mut element = div().shadow_sm();
4105        let shadows = element
4106            .style()
4107            .box_shadow
4108            .as_ref()
4109            .expect("shadow_sm should set box shadows");
4110
4111        assert_eq!(shadows.len(), 2);
4112        assert_eq!(shadows[0].color, hsla(0., 0., 0., 0.1));
4113        assert_eq!(shadows[0].offset, point(px(0.), px(1.)));
4114        assert_eq!(shadows[0].blur_radius, px(3.));
4115        assert_eq!(shadows[0].spread_radius, px(0.));
4116        assert!(!shadows[0].inset);
4117
4118        assert_eq!(shadows[1].color, hsla(0., 0., 0., 0.1));
4119        assert_eq!(shadows[1].offset, point(px(0.), px(1.)));
4120        assert_eq!(shadows[1].blur_radius, px(2.));
4121        assert_eq!(shadows[1].spread_radius, px(-1.));
4122        assert!(!shadows[1].inset);
4123    }
4124
4125    struct ContentSizedGrid;
4126
4127    impl Render for ContentSizedGrid {
4128        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4129            let widths = [px(100.), px(200.), px(50.)];
4130            div().size_full().child(
4131                div()
4132                    .w_full()
4133                    .grid()
4134                    .grid_cols_max_content(widths.len() as u16)
4135                    .children(widths.into_iter().enumerate().map(|(index, width)| {
4136                        div()
4137                            .debug_selector(move || format!("cell-{index}"))
4138                            .w(width)
4139                            .h(px(10.))
4140                    })),
4141            )
4142        }
4143    }
4144
4145    #[gpui::test]
4146    fn grid_cols_max_content_sizes_columns_to_their_content(cx: &mut TestAppContext) {
4147        let window = cx.add_window(|_, _| ContentSizedGrid);
4148        cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear())
4149            .unwrap();
4150
4151        let mut bounds = |selector: &'static str| {
4152            cx.update_window(window.into(), |_, window, _| {
4153                window.rendered_frame.debug_bounds.get(selector).copied()
4154            })
4155            .unwrap()
4156            .unwrap_or_else(|| panic!("{selector} was not rendered"))
4157        };
4158
4159        assert_eq!(bounds("cell-0").origin.x, px(0.));
4160        assert_eq!(bounds("cell-0").size.width, px(100.));
4161        assert_eq!(bounds("cell-1").origin.x, px(100.));
4162        assert_eq!(bounds("cell-1").size.width, px(200.));
4163        assert_eq!(bounds("cell-2").origin.x, px(300.));
4164        assert_eq!(bounds("cell-2").size.width, px(50.));
4165    }
4166
4167    #[test]
4168    fn restrict_scroll_to_axis_sets_style_flag() {
4169        let mut element = div().id("axis-lock").restrict_scroll_to_axis();
4170        assert_eq!(
4171            element
4172                .element
4173                .interactivity
4174                .base_style
4175                .restrict_scroll_to_axis,
4176            Some(true)
4177        );
4178    }
4179
4180    struct AxisLockedScrollView {
4181        handle: ScrollHandle,
4182    }
4183
4184    impl Render for AxisLockedScrollView {
4185        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
4186            div()
4187                .id("axis-lock-scroller")
4188                .size_full()
4189                .overflow_scroll()
4190                .restrict_scroll_to_axis()
4191                .track_scroll(&self.handle)
4192                .child(div().size(px(2000.)))
4193        }
4194    }
4195
4196    #[gpui::test]
4197    fn restrict_scroll_to_axis_locks_precise_gestures_to_start_axis(cx: &mut TestAppContext) {
4198        let handle = ScrollHandle::new();
4199        let (_, cx) = cx.add_window_view({
4200            let handle = handle.clone();
4201            move |_, _| AxisLockedScrollView { handle }
4202        });
4203        cx.simulate_resize(size(px(200.), px(200.)));
4204
4205        cx.update(|window, cx| {
4206            window.dispatch_event(
4207                ScrollWheelEvent {
4208                    position: point(px(50.), px(50.)),
4209                    delta: ScrollDelta::Pixels(point(px(-20.), px(-4.))),
4210                    touch_phase: crate::TouchPhase::Started,
4211                    ..Default::default()
4212                }
4213                .to_platform_input(),
4214                cx,
4215            );
4216        });
4217        cx.run_until_parked();
4218        let after_start = handle.offset();
4219        assert!(after_start.x < px(0.), "horizontal start should scroll x");
4220        assert_eq!(
4221            after_start.y,
4222            px(0.),
4223            "sticky start axis should drop the weaker vertical component"
4224        );
4225
4226        cx.update(|window, cx| {
4227            window.dispatch_event(
4228                ScrollWheelEvent {
4229                    position: point(px(50.), px(50.)),
4230                    delta: ScrollDelta::Pixels(point(px(-3.), px(-2.))),
4231                    touch_phase: crate::TouchPhase::Moved,
4232                    ..Default::default()
4233                }
4234                .to_platform_input(),
4235                cx,
4236            );
4237        });
4238        cx.run_until_parked();
4239        let after_continue = handle.offset();
4240        assert!(after_continue.x < after_start.x);
4241        assert_eq!(after_continue.y, px(0.));
4242
4243        cx.update(|window, cx| {
4244            window.dispatch_event(
4245                ScrollWheelEvent {
4246                    position: point(px(50.), px(50.)),
4247                    delta: ScrollDelta::Pixels(point(px(-2.), px(-20.))),
4248                    touch_phase: crate::TouchPhase::Moved,
4249                    ..Default::default()
4250                }
4251                .to_platform_input(),
4252                cx,
4253            );
4254        });
4255        cx.run_until_parked();
4256        let after_unlock = handle.offset();
4257        assert!(
4258            after_unlock.y < px(0.),
4259            "a strong perpendicular delta should break the axis lock"
4260        );
4261    }
4262
4263    #[gpui::test]
4264    fn restrict_scroll_to_axis_style_flag_still_blocks_axis_remap(cx: &mut TestAppContext) {
4265        let handle = ScrollHandle::new();
4266        struct VerticalOnly {
4267            handle: ScrollHandle,
4268        }
4269        impl Render for VerticalOnly {
4270            fn render(
4271                &mut self,
4272                _window: &mut Window,
4273                _cx: &mut Context<Self>,
4274            ) -> impl IntoElement {
4275                let mut scroller = div()
4276                    .id("vertical-only")
4277                    .size_full()
4278                    .overflow_y_scroll()
4279                    .track_scroll(&self.handle)
4280                    .child(div().h(px(2000.)).w_full());
4281                scroller.style().restrict_scroll_to_axis = Some(true);
4282                scroller
4283            }
4284        }
4285
4286        let (_, cx) = cx.add_window_view({
4287            let handle = handle.clone();
4288            move |_, _| VerticalOnly { handle }
4289        });
4290        cx.simulate_resize(size(px(200.), px(200.)));
4291
4292        cx.update(|window, cx| {
4293            window.dispatch_event(
4294                ScrollWheelEvent {
4295                    position: point(px(50.), px(50.)),
4296                    delta: ScrollDelta::Pixels(point(px(-20.), px(0.))),
4297                    ..Default::default()
4298                }
4299                .to_platform_input(),
4300                cx,
4301            );
4302        });
4303        cx.run_until_parked();
4304        assert_eq!(
4305            handle.offset(),
4306            point(px(0.), px(0.)),
4307            "existing restrict_scroll_to_axis callers must not have horizontal input remapped onto a vertical-only scroller"
4308        );
4309    }
4310}