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    point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
  20    ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, Global, GlobalElementId, Hitbox,
  21    HitboxId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
  22    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
  23    ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
  24    StyleRefinement, Styled, Task, TooltipId, View, Visibility, WindowContext,
  25};
  26use collections::HashMap;
  27use refineable::Refineable;
  28use smallvec::SmallVec;
  29use std::{
  30    any::{Any, TypeId},
  31    cell::RefCell,
  32    cmp::Ordering,
  33    fmt::Debug,
  34    marker::PhantomData,
  35    mem,
  36    ops::DerefMut,
  37    rc::Rc,
  38    sync::Arc,
  39    time::Duration,
  40};
  41use taffy::style::Overflow;
  42use util::ResultExt;
  43
  44const DRAG_THRESHOLD: f64 = 2.;
  45pub(crate) const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
  46
  47/// The styling information for a given group.
  48pub struct GroupStyle {
  49    /// The identifier for this group.
  50    pub group: SharedString,
  51
  52    /// The specific style refinement that this group would apply
  53    /// to its children.
  54    pub style: Box<StyleRefinement>,
  55}
  56
  57/// An event for when a drag is moving over this element, with the given state type.
  58pub struct DragMoveEvent<T> {
  59    /// The mouse move event that triggered this drag move event.
  60    pub event: MouseMoveEvent,
  61
  62    /// The bounds of this element.
  63    pub bounds: Bounds<Pixels>,
  64    drag: PhantomData<T>,
  65    dragged_item: Arc<dyn Any>,
  66}
  67
  68impl<T: 'static> DragMoveEvent<T> {
  69    /// Returns the drag state for this event.
  70    pub fn drag<'b>(&self, cx: &'b AppContext) -> &'b T {
  71        cx.active_drag
  72            .as_ref()
  73            .and_then(|drag| drag.value.downcast_ref::<T>())
  74            .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
  75    }
  76
  77    /// An item that is about to be dropped.
  78    pub fn dragged_item(&self) -> &dyn Any {
  79        self.dragged_item.as_ref()
  80    }
  81}
  82
  83impl Interactivity {
  84    /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase
  85    /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]
  86    ///
  87    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to the view state from this callback.
  88    pub fn on_mouse_down(
  89        &mut self,
  90        button: MouseButton,
  91        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
  92    ) {
  93        self.mouse_down_listeners
  94            .push(Box::new(move |event, phase, hitbox, cx| {
  95                if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(cx)
  96                {
  97                    (listener)(event, cx)
  98                }
  99            }));
 100    }
 101
 102    /// Bind the given callback to the mouse down event for any button, during the capture phase
 103    /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]
 104    ///
 105    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 106    pub fn capture_any_mouse_down(
 107        &mut self,
 108        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 109    ) {
 110        self.mouse_down_listeners
 111            .push(Box::new(move |event, phase, hitbox, cx| {
 112                if phase == DispatchPhase::Capture && hitbox.is_hovered(cx) {
 113                    (listener)(event, cx)
 114                }
 115            }));
 116    }
 117
 118    /// Bind the given callback to the mouse down event for any button, during the bubble phase
 119    /// the imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]
 120    ///
 121    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 122    pub fn on_any_mouse_down(
 123        &mut self,
 124        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 125    ) {
 126        self.mouse_down_listeners
 127            .push(Box::new(move |event, phase, hitbox, cx| {
 128                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
 129                    (listener)(event, cx)
 130                }
 131            }));
 132    }
 133
 134    /// Bind the given callback to the mouse up event for the given button, during the bubble phase
 135    /// the imperative API equivalent to [`InteractiveElement::on_mouse_up`]
 136    ///
 137    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 138    pub fn on_mouse_up(
 139        &mut self,
 140        button: MouseButton,
 141        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 142    ) {
 143        self.mouse_up_listeners
 144            .push(Box::new(move |event, phase, hitbox, cx| {
 145                if phase == DispatchPhase::Bubble && event.button == button && hitbox.is_hovered(cx)
 146                {
 147                    (listener)(event, cx)
 148                }
 149            }));
 150    }
 151
 152    /// Bind the given callback to the mouse up event for any button, during the capture phase
 153    /// the imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]
 154    ///
 155    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 156    pub fn capture_any_mouse_up(
 157        &mut self,
 158        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 159    ) {
 160        self.mouse_up_listeners
 161            .push(Box::new(move |event, phase, hitbox, cx| {
 162                if phase == DispatchPhase::Capture && hitbox.is_hovered(cx) {
 163                    (listener)(event, cx)
 164                }
 165            }));
 166    }
 167
 168    /// Bind the given callback to the mouse up event for any button, during the bubble phase
 169    /// the imperative API equivalent to [`Interactivity::on_any_mouse_up`]
 170    ///
 171    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 172    pub fn on_any_mouse_up(
 173        &mut self,
 174        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 175    ) {
 176        self.mouse_up_listeners
 177            .push(Box::new(move |event, phase, hitbox, cx| {
 178                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
 179                    (listener)(event, cx)
 180                }
 181            }));
 182    }
 183
 184    /// Bind the given callback to the mouse down event, on any button, during the capture phase,
 185    /// when the mouse is outside of the bounds of this element.
 186    /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]
 187    ///
 188    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 189    pub fn on_mouse_down_out(
 190        &mut self,
 191        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 192    ) {
 193        self.mouse_down_listeners
 194            .push(Box::new(move |event, phase, hitbox, cx| {
 195                if phase == DispatchPhase::Capture && !hitbox.contains(&cx.mouse_position()) {
 196                    (listener)(event, cx)
 197                }
 198            }));
 199    }
 200
 201    /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
 202    /// when the mouse is outside of the bounds of this element.
 203    /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]
 204    ///
 205    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 206    pub fn on_mouse_up_out(
 207        &mut self,
 208        button: MouseButton,
 209        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 210    ) {
 211        self.mouse_up_listeners
 212            .push(Box::new(move |event, phase, hitbox, cx| {
 213                if phase == DispatchPhase::Capture
 214                    && event.button == button
 215                    && !hitbox.is_hovered(cx)
 216                {
 217                    (listener)(event, cx);
 218                }
 219            }));
 220    }
 221
 222    /// Bind the given callback to the mouse move event, during the bubble phase
 223    /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]
 224    ///
 225    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 226    pub fn on_mouse_move(
 227        &mut self,
 228        listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
 229    ) {
 230        self.mouse_move_listeners
 231            .push(Box::new(move |event, phase, hitbox, cx| {
 232                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
 233                    (listener)(event, cx);
 234                }
 235            }));
 236    }
 237
 238    /// Bind the given callback to the mouse drag event of the given type. Note that this
 239    /// will be called for all move events, inside or outside of this element, as long as the
 240    /// drag was started with this element under the mouse. Useful for implementing draggable
 241    /// UIs that don't conform to a drag and drop style interaction, like resizing.
 242    /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]
 243    ///
 244    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 245    pub fn on_drag_move<T>(
 246        &mut self,
 247        listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
 248    ) where
 249        T: 'static,
 250    {
 251        self.mouse_move_listeners
 252            .push(Box::new(move |event, phase, hitbox, cx| {
 253                if phase == DispatchPhase::Capture {
 254                    if let Some(drag) = &cx.active_drag {
 255                        if drag.value.as_ref().type_id() == TypeId::of::<T>() {
 256                            (listener)(
 257                                &DragMoveEvent {
 258                                    event: event.clone(),
 259                                    bounds: hitbox.bounds,
 260                                    drag: PhantomData,
 261                                    dragged_item: Arc::clone(&drag.value),
 262                                },
 263                                cx,
 264                            );
 265                        }
 266                    }
 267                }
 268            }));
 269    }
 270
 271    /// Bind the given callback to scroll wheel events during the bubble phase
 272    /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]
 273    ///
 274    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 275    pub fn on_scroll_wheel(
 276        &mut self,
 277        listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
 278    ) {
 279        self.scroll_wheel_listeners
 280            .push(Box::new(move |event, phase, hitbox, cx| {
 281                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
 282                    (listener)(event, cx);
 283                }
 284            }));
 285    }
 286
 287    /// Bind the given callback to an action dispatch during the capture phase
 288    /// The imperative API equivalent to [`InteractiveElement::capture_action`]
 289    ///
 290    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 291    pub fn capture_action<A: Action>(
 292        &mut self,
 293        listener: impl Fn(&A, &mut WindowContext) + 'static,
 294    ) {
 295        self.action_listeners.push((
 296            TypeId::of::<A>(),
 297            Box::new(move |action, phase, cx| {
 298                let action = action.downcast_ref().unwrap();
 299                if phase == DispatchPhase::Capture {
 300                    (listener)(action, cx)
 301                } else {
 302                    cx.propagate();
 303                }
 304            }),
 305        ));
 306    }
 307
 308    /// Bind the given callback to an action dispatch during the bubble phase
 309    /// The imperative API equivalent to [`InteractiveElement::on_action`]
 310    ///
 311    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 312    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) {
 313        self.action_listeners.push((
 314            TypeId::of::<A>(),
 315            Box::new(move |action, phase, cx| {
 316                let action = action.downcast_ref().unwrap();
 317                if phase == DispatchPhase::Bubble {
 318                    (listener)(action, cx)
 319                }
 320            }),
 321        ));
 322    }
 323
 324    /// Bind the given callback to an action dispatch, based on a dynamic action parameter
 325    /// instead of a type parameter. Useful for component libraries that want to expose
 326    /// action bindings to their users.
 327    /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]
 328    ///
 329    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 330    pub fn on_boxed_action(
 331        &mut self,
 332        action: &dyn Action,
 333        listener: impl Fn(&dyn Action, &mut WindowContext) + 'static,
 334    ) {
 335        let action = action.boxed_clone();
 336        self.action_listeners.push((
 337            (*action).type_id(),
 338            Box::new(move |_, phase, cx| {
 339                if phase == DispatchPhase::Bubble {
 340                    (listener)(&*action, cx)
 341                }
 342            }),
 343        ));
 344    }
 345
 346    /// Bind the given callback to key down events during the bubble phase
 347    /// The imperative API equivalent to [`InteractiveElement::on_key_down`]
 348    ///
 349    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 350    pub fn on_key_down(&mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static) {
 351        self.key_down_listeners
 352            .push(Box::new(move |event, phase, cx| {
 353                if phase == DispatchPhase::Bubble {
 354                    (listener)(event, cx)
 355                }
 356            }));
 357    }
 358
 359    /// Bind the given callback to key down events during the capture phase
 360    /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]
 361    ///
 362    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 363    pub fn capture_key_down(
 364        &mut self,
 365        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
 366    ) {
 367        self.key_down_listeners
 368            .push(Box::new(move |event, phase, cx| {
 369                if phase == DispatchPhase::Capture {
 370                    listener(event, cx)
 371                }
 372            }));
 373    }
 374
 375    /// Bind the given callback to key up events during the bubble phase
 376    /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
 377    ///
 378    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 379    pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
 380        self.key_up_listeners
 381            .push(Box::new(move |event, phase, cx| {
 382                if phase == DispatchPhase::Bubble {
 383                    listener(event, cx)
 384                }
 385            }));
 386    }
 387
 388    /// Bind the given callback to key up events during the capture phase
 389    /// The imperative API equivalent to [`InteractiveElement::on_key_up`]
 390    ///
 391    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 392    pub fn capture_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
 393        self.key_up_listeners
 394            .push(Box::new(move |event, phase, cx| {
 395                if phase == DispatchPhase::Capture {
 396                    listener(event, cx)
 397                }
 398            }));
 399    }
 400
 401    /// Bind the given callback to modifiers changing events.
 402    /// The imperative API equivalent to [`InteractiveElement::on_modifiers_changed`]
 403    ///
 404    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 405    pub fn on_modifiers_changed(
 406        &mut self,
 407        listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
 408    ) {
 409        self.modifiers_changed_listeners
 410            .push(Box::new(move |event, cx| listener(event, cx)));
 411    }
 412
 413    /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
 414    /// The imperative API equivalent to [`InteractiveElement::on_drop`]
 415    ///
 416    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 417    pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) {
 418        self.drop_listeners.push((
 419            TypeId::of::<T>(),
 420            Box::new(move |dragged_value, cx| {
 421                listener(dragged_value.downcast_ref().unwrap(), cx);
 422            }),
 423        ));
 424    }
 425
 426    /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
 427    /// The imperative API equivalent to [`InteractiveElement::can_drop`]
 428    pub fn can_drop(&mut self, predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static) {
 429        self.can_drop_predicate = Some(Box::new(predicate));
 430    }
 431
 432    /// Bind the given callback to click events of this element
 433    /// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]
 434    ///
 435    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 436    pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static)
 437    where
 438        Self: Sized,
 439    {
 440        self.click_listeners
 441            .push(Box::new(move |event, cx| listener(event, cx)));
 442    }
 443
 444    /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
 445    /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
 446    /// the [`Self::on_drag_move`] API
 447    /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
 448    ///
 449    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 450    pub fn on_drag<T, W>(
 451        &mut self,
 452        value: T,
 453        constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
 454    ) where
 455        Self: Sized,
 456        T: 'static,
 457        W: 'static + Render,
 458    {
 459        debug_assert!(
 460            self.drag_listener.is_none(),
 461            "calling on_drag more than once on the same element is not supported"
 462        );
 463        self.drag_listener = Some((
 464            Arc::new(value),
 465            Box::new(move |value, offset, cx| {
 466                constructor(value.downcast_ref().unwrap(), offset, cx).into()
 467            }),
 468        ));
 469    }
 470
 471    /// Bind the given callback on the hover start and end events of this element. Note that the boolean
 472    /// passed to the callback is true when the hover starts and false when it ends.
 473    /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]
 474    ///
 475    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 476    pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static)
 477    where
 478        Self: Sized,
 479    {
 480        debug_assert!(
 481            self.hover_listener.is_none(),
 482            "calling on_hover more than once on the same element is not supported"
 483        );
 484        self.hover_listener = Some(Box::new(listener));
 485    }
 486
 487    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
 488    /// The imperative API equivalent to [`InteractiveElement::tooltip`]
 489    pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static)
 490    where
 491        Self: Sized,
 492    {
 493        debug_assert!(
 494            self.tooltip_builder.is_none(),
 495            "calling tooltip more than once on the same element is not supported"
 496        );
 497        self.tooltip_builder = Some(TooltipBuilder {
 498            build: Rc::new(build_tooltip),
 499            hoverable: false,
 500        });
 501    }
 502
 503    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
 504    /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into
 505    /// the tooltip. The imperative API equivalent to [`InteractiveElement::hoverable_tooltip`]
 506    pub fn hoverable_tooltip(
 507        &mut self,
 508        build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static,
 509    ) where
 510        Self: Sized,
 511    {
 512        debug_assert!(
 513            self.tooltip_builder.is_none(),
 514            "calling tooltip more than once on the same element is not supported"
 515        );
 516        self.tooltip_builder = Some(TooltipBuilder {
 517            build: Rc::new(build_tooltip),
 518            hoverable: true,
 519        });
 520    }
 521
 522    /// Block the mouse from interacting with this element or any of its children
 523    /// The imperative API equivalent to [`InteractiveElement::occlude`]
 524    pub fn occlude_mouse(&mut self) {
 525        self.occlude_mouse = true;
 526    }
 527}
 528
 529/// A trait for elements that want to use the standard GPUI event handlers that don't
 530/// require any state.
 531pub trait InteractiveElement: Sized {
 532    /// Retrieve the interactivity state associated with this element
 533    fn interactivity(&mut self) -> &mut Interactivity;
 534
 535    /// Assign this element to a group of elements that can be styled together
 536    fn group(mut self, group: impl Into<SharedString>) -> Self {
 537        self.interactivity().group = Some(group.into());
 538        self
 539    }
 540
 541    /// Assign this element an ID, so that it can be used with interactivity
 542    fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
 543        self.interactivity().element_id = Some(id.into());
 544
 545        Stateful { element: self }
 546    }
 547
 548    /// Track the focus state of the given focus handle on this element.
 549    /// If the focus handle is focused by the application, this element will
 550    /// apply its focused styles.
 551    fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<Self> {
 552        self.interactivity().focusable = true;
 553        self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
 554        Focusable { element: self }
 555    }
 556
 557    /// Set the keymap context for this element. This will be used to determine
 558    /// which action to dispatch from the keymap.
 559    fn key_context<C, E>(mut self, key_context: C) -> Self
 560    where
 561        C: TryInto<KeyContext, Error = E>,
 562        E: Debug,
 563    {
 564        if let Some(key_context) = key_context.try_into().log_err() {
 565            self.interactivity().key_context = Some(key_context);
 566        }
 567        self
 568    }
 569
 570    /// Apply the given style to this element when the mouse hovers over it
 571    fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
 572        debug_assert!(
 573            self.interactivity().hover_style.is_none(),
 574            "hover style already set"
 575        );
 576        self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
 577        self
 578    }
 579
 580    /// Apply the given style to this element when the mouse hovers over a group member
 581    fn group_hover(
 582        mut self,
 583        group_name: impl Into<SharedString>,
 584        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 585    ) -> Self {
 586        self.interactivity().group_hover_style = Some(GroupStyle {
 587            group: group_name.into(),
 588            style: Box::new(f(StyleRefinement::default())),
 589        });
 590        self
 591    }
 592
 593    /// Bind the given callback to the mouse down event for the given mouse button,
 594    /// the fluent API equivalent to [`Interactivity::on_mouse_down`]
 595    ///
 596    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to the view state from this callback.
 597    fn on_mouse_down(
 598        mut self,
 599        button: MouseButton,
 600        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 601    ) -> Self {
 602        self.interactivity().on_mouse_down(button, listener);
 603        self
 604    }
 605
 606    #[cfg(any(test, feature = "test-support"))]
 607    /// Set a key that can be used to look up this element's bounds
 608    /// in the [`VisualTestContext::debug_bounds`] map
 609    /// This is a noop in release builds
 610    fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
 611        self.interactivity().debug_selector = Some(f());
 612        self
 613    }
 614
 615    #[cfg(not(any(test, feature = "test-support")))]
 616    /// Set a key that can be used to look up this element's bounds
 617    /// in the [`VisualTestContext::debug_bounds`] map
 618    /// This is a noop in release builds
 619    #[inline]
 620    fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
 621        self
 622    }
 623
 624    /// Bind the given callback to the mouse down event for any button, during the capture phase
 625    /// the fluent API equivalent to [`Interactivity::capture_any_mouse_down`]
 626    ///
 627    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 628    fn capture_any_mouse_down(
 629        mut self,
 630        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 631    ) -> Self {
 632        self.interactivity().capture_any_mouse_down(listener);
 633        self
 634    }
 635
 636    /// Bind the given callback to the mouse down event for any button, during the capture phase
 637    /// the fluent API equivalent to [`Interactivity::on_any_mouse_down`]
 638    ///
 639    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 640    fn on_any_mouse_down(
 641        mut self,
 642        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 643    ) -> Self {
 644        self.interactivity().on_any_mouse_down(listener);
 645        self
 646    }
 647
 648    /// Bind the given callback to the mouse up event for the given button, during the bubble phase
 649    /// the fluent API equivalent to [`Interactivity::on_mouse_up`]
 650    ///
 651    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 652    fn on_mouse_up(
 653        mut self,
 654        button: MouseButton,
 655        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 656    ) -> Self {
 657        self.interactivity().on_mouse_up(button, listener);
 658        self
 659    }
 660
 661    /// Bind the given callback to the mouse up event for any button, during the capture phase
 662    /// the fluent API equivalent to [`Interactivity::capture_any_mouse_up`]
 663    ///
 664    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 665    fn capture_any_mouse_up(
 666        mut self,
 667        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 668    ) -> Self {
 669        self.interactivity().capture_any_mouse_up(listener);
 670        self
 671    }
 672
 673    /// Bind the given callback to the mouse down event, on any button, during the capture phase,
 674    /// when the mouse is outside of the bounds of this element.
 675    /// The fluent API equivalent to [`Interactivity::on_mouse_down_out`]
 676    ///
 677    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 678    fn on_mouse_down_out(
 679        mut self,
 680        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 681    ) -> Self {
 682        self.interactivity().on_mouse_down_out(listener);
 683        self
 684    }
 685
 686    /// Bind the given callback to the mouse up event, for the given button, during the capture phase,
 687    /// when the mouse is outside of the bounds of this element.
 688    /// The fluent API equivalent to [`Interactivity::on_mouse_up_out`]
 689    ///
 690    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 691    fn on_mouse_up_out(
 692        mut self,
 693        button: MouseButton,
 694        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 695    ) -> Self {
 696        self.interactivity().on_mouse_up_out(button, listener);
 697        self
 698    }
 699
 700    /// Bind the given callback to the mouse move event, during the bubble phase
 701    /// The fluent API equivalent to [`Interactivity::on_mouse_move`]
 702    ///
 703    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 704    fn on_mouse_move(
 705        mut self,
 706        listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
 707    ) -> Self {
 708        self.interactivity().on_mouse_move(listener);
 709        self
 710    }
 711
 712    /// Bind the given callback to the mouse drag event of the given type. Note that this
 713    /// will be called for all move events, inside or outside of this element, as long as the
 714    /// drag was started with this element under the mouse. Useful for implementing draggable
 715    /// UIs that don't conform to a drag and drop style interaction, like resizing.
 716    /// The fluent API equivalent to [`Interactivity::on_drag_move`]
 717    ///
 718    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 719    fn on_drag_move<T: 'static>(
 720        mut self,
 721        listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
 722    ) -> Self {
 723        self.interactivity().on_drag_move(listener);
 724        self
 725    }
 726
 727    /// Bind the given callback to scroll wheel events during the bubble phase
 728    /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
 729    ///
 730    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 731    fn on_scroll_wheel(
 732        mut self,
 733        listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
 734    ) -> Self {
 735        self.interactivity().on_scroll_wheel(listener);
 736        self
 737    }
 738
 739    /// Capture the given action, before normal action dispatch can fire
 740    /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]
 741    ///
 742    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 743    fn capture_action<A: Action>(
 744        mut self,
 745        listener: impl Fn(&A, &mut WindowContext) + 'static,
 746    ) -> Self {
 747        self.interactivity().capture_action(listener);
 748        self
 749    }
 750
 751    /// Bind the given callback to an action dispatch during the bubble phase
 752    /// The fluent API equivalent to [`Interactivity::on_action`]
 753    ///
 754    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 755    fn on_action<A: Action>(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
 756        self.interactivity().on_action(listener);
 757        self
 758    }
 759
 760    /// Bind the given callback to an action dispatch, based on a dynamic action parameter
 761    /// instead of a type parameter. Useful for component libraries that want to expose
 762    /// action bindings to their users.
 763    /// The fluent API equivalent to [`Interactivity::on_boxed_action`]
 764    ///
 765    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 766    fn on_boxed_action(
 767        mut self,
 768        action: &dyn Action,
 769        listener: impl Fn(&dyn Action, &mut WindowContext) + 'static,
 770    ) -> Self {
 771        self.interactivity().on_boxed_action(action, listener);
 772        self
 773    }
 774
 775    /// Bind the given callback to key down events during the bubble phase
 776    /// The fluent API equivalent to [`Interactivity::on_key_down`]
 777    ///
 778    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 779    fn on_key_down(
 780        mut self,
 781        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
 782    ) -> Self {
 783        self.interactivity().on_key_down(listener);
 784        self
 785    }
 786
 787    /// Bind the given callback to key down events during the capture phase
 788    /// The fluent API equivalent to [`Interactivity::capture_key_down`]
 789    ///
 790    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 791    fn capture_key_down(
 792        mut self,
 793        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
 794    ) -> Self {
 795        self.interactivity().capture_key_down(listener);
 796        self
 797    }
 798
 799    /// Bind the given callback to key up events during the bubble phase
 800    /// The fluent API equivalent to [`Interactivity::on_key_up`]
 801    ///
 802    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 803    fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
 804        self.interactivity().on_key_up(listener);
 805        self
 806    }
 807
 808    /// Bind the given callback to key up events during the capture phase
 809    /// The fluent API equivalent to [`Interactivity::capture_key_up`]
 810    ///
 811    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 812    fn capture_key_up(
 813        mut self,
 814        listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
 815    ) -> Self {
 816        self.interactivity().capture_key_up(listener);
 817        self
 818    }
 819
 820    /// Bind the given callback to modifiers changing events.
 821    /// The fluent API equivalent to [`Interactivity::on_modifiers_changed`]
 822    ///
 823    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 824    fn on_modifiers_changed(
 825        mut self,
 826        listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
 827    ) -> Self {
 828        self.interactivity().on_modifiers_changed(listener);
 829        self
 830    }
 831
 832    /// Apply the given style when the given data type is dragged over this element
 833    fn drag_over<S: 'static>(
 834        mut self,
 835        f: impl 'static + Fn(StyleRefinement, &S, &WindowContext) -> StyleRefinement,
 836    ) -> Self {
 837        self.interactivity().drag_over_styles.push((
 838            TypeId::of::<S>(),
 839            Box::new(move |currently_dragged: &dyn Any, cx| {
 840                f(
 841                    StyleRefinement::default(),
 842                    currently_dragged.downcast_ref::<S>().unwrap(),
 843                    cx,
 844                )
 845            }),
 846        ));
 847        self
 848    }
 849
 850    /// Apply the given style when the given data type is dragged over this element's group
 851    fn group_drag_over<S: 'static>(
 852        mut self,
 853        group_name: impl Into<SharedString>,
 854        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 855    ) -> Self {
 856        self.interactivity().group_drag_over_styles.push((
 857            TypeId::of::<S>(),
 858            GroupStyle {
 859                group: group_name.into(),
 860                style: Box::new(f(StyleRefinement::default())),
 861            },
 862        ));
 863        self
 864    }
 865
 866    /// Bind the given callback to drop events of the given type, whether or not the drag started on this element
 867    /// The fluent API equivalent to [`Interactivity::on_drop`]
 868    ///
 869    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 870    fn on_drop<T: 'static>(mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) -> Self {
 871        self.interactivity().on_drop(listener);
 872        self
 873    }
 874
 875    /// Use the given predicate to determine whether or not a drop event should be dispatched to this element
 876    /// The fluent API equivalent to [`Interactivity::can_drop`]
 877    fn can_drop(
 878        mut self,
 879        predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
 880    ) -> Self {
 881        self.interactivity().can_drop(predicate);
 882        self
 883    }
 884
 885    /// Block the mouse from interacting with this element or any of its children
 886    /// The fluent API equivalent to [`Interactivity::occlude_mouse`]
 887    fn occlude(mut self) -> Self {
 888        self.interactivity().occlude_mouse();
 889        self
 890    }
 891
 892    /// Block the mouse from interacting with this element or any of its children
 893    /// The fluent API equivalent to [`Interactivity::occlude_mouse`]
 894    fn block_mouse_down(mut self) -> Self {
 895        self.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
 896    }
 897}
 898
 899/// A trait for elements that want to use the standard GPUI interactivity features
 900/// that require state.
 901pub trait StatefulInteractiveElement: InteractiveElement {
 902    /// Set this element to focusable.
 903    fn focusable(mut self) -> Focusable<Self> {
 904        self.interactivity().focusable = true;
 905        Focusable { element: self }
 906    }
 907
 908    /// Set the overflow x and y to scroll.
 909    fn overflow_scroll(mut self) -> Self {
 910        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
 911        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
 912        self
 913    }
 914
 915    /// Set the overflow x to scroll.
 916    fn overflow_x_scroll(mut self) -> Self {
 917        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
 918        self
 919    }
 920
 921    /// Set the overflow y to scroll.
 922    fn overflow_y_scroll(mut self) -> Self {
 923        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
 924        self
 925    }
 926
 927    /// Track the scroll state of this element with the given handle.
 928    fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
 929        self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone());
 930        self
 931    }
 932
 933    /// Track the scroll state of this element with the given handle.
 934    fn anchor_scroll(mut self, scroll_anchor: Option<ScrollAnchor>) -> Self {
 935        self.interactivity().scroll_anchor = scroll_anchor;
 936        self
 937    }
 938
 939    /// Set the given styles to be applied when this element is active.
 940    fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 941    where
 942        Self: Sized,
 943    {
 944        self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
 945        self
 946    }
 947
 948    /// Set the given styles to be applied when this element's group is active.
 949    fn group_active(
 950        mut self,
 951        group_name: impl Into<SharedString>,
 952        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 953    ) -> Self
 954    where
 955        Self: Sized,
 956    {
 957        self.interactivity().group_active_style = Some(GroupStyle {
 958            group: group_name.into(),
 959            style: Box::new(f(StyleRefinement::default())),
 960        });
 961        self
 962    }
 963
 964    /// Bind the given callback to click events of this element
 965    /// The fluent API equivalent to [`Interactivity::on_click`]
 966    ///
 967    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 968    fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
 969    where
 970        Self: Sized,
 971    {
 972        self.interactivity().on_click(listener);
 973        self
 974    }
 975
 976    /// On drag initiation, this callback will be used to create a new view to render the dragged value for a
 977    /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
 978    /// the [`Self::on_drag_move`] API.
 979    /// The callback also has access to the offset of triggering click from the origin of parent element.
 980    /// The fluent API equivalent to [`Interactivity::on_drag`]
 981    ///
 982    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
 983    fn on_drag<T, W>(
 984        mut self,
 985        value: T,
 986        constructor: impl Fn(&T, Point<Pixels>, &mut WindowContext) -> View<W> + 'static,
 987    ) -> Self
 988    where
 989        Self: Sized,
 990        T: 'static,
 991        W: 'static + Render,
 992    {
 993        self.interactivity().on_drag(value, constructor);
 994        self
 995    }
 996
 997    /// Bind the given callback on the hover start and end events of this element. Note that the boolean
 998    /// passed to the callback is true when the hover starts and false when it ends.
 999    /// The fluent API equivalent to [`Interactivity::on_hover`]
1000    ///
1001    /// See [`ViewContext::listener`](crate::ViewContext::listener) to get access to a view's state from this callback.
1002    fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
1003    where
1004        Self: Sized,
1005    {
1006        self.interactivity().on_hover(listener);
1007        self
1008    }
1009
1010    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
1011    /// The fluent API equivalent to [`Interactivity::tooltip`]
1012    fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
1013    where
1014        Self: Sized,
1015    {
1016        self.interactivity().tooltip(build_tooltip);
1017        self
1018    }
1019
1020    /// Use the given callback to construct a new tooltip view when the mouse hovers over this element.
1021    /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into
1022    /// the tooltip. The fluent API equivalent to [`Interactivity::hoverable_tooltip`]
1023    fn hoverable_tooltip(
1024        mut self,
1025        build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static,
1026    ) -> Self
1027    where
1028        Self: Sized,
1029    {
1030        self.interactivity().hoverable_tooltip(build_tooltip);
1031        self
1032    }
1033}
1034
1035/// A trait for providing focus related APIs to interactive elements
1036pub trait FocusableElement: InteractiveElement {
1037    /// Set the given styles to be applied when this element, specifically, is focused.
1038    fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1039    where
1040        Self: Sized,
1041    {
1042        self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
1043        self
1044    }
1045
1046    /// Set the given styles to be applied when this element is inside another element that is focused.
1047    fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1048    where
1049        Self: Sized,
1050    {
1051        self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
1052        self
1053    }
1054}
1055
1056pub(crate) type MouseDownListener =
1057    Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
1058pub(crate) type MouseUpListener =
1059    Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
1060
1061pub(crate) type MouseMoveListener =
1062    Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
1063
1064pub(crate) type ScrollWheelListener =
1065    Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut WindowContext) + 'static>;
1066
1067pub(crate) type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
1068
1069pub(crate) type DragListener =
1070    Box<dyn Fn(&dyn Any, Point<Pixels>, &mut WindowContext) -> AnyView + 'static>;
1071
1072type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
1073
1074type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
1075
1076pub(crate) struct TooltipBuilder {
1077    build: Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
1078    hoverable: bool,
1079}
1080
1081pub(crate) type KeyDownListener =
1082    Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
1083
1084pub(crate) type KeyUpListener =
1085    Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
1086
1087pub(crate) type ModifiersChangedListener =
1088    Box<dyn Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static>;
1089
1090pub(crate) type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
1091
1092/// Construct a new [`Div`] element
1093#[track_caller]
1094pub fn div() -> Div {
1095    #[cfg(debug_assertions)]
1096    let interactivity = Interactivity {
1097        location: Some(*core::panic::Location::caller()),
1098        ..Default::default()
1099    };
1100
1101    #[cfg(not(debug_assertions))]
1102    let interactivity = Interactivity::default();
1103
1104    Div {
1105        interactivity,
1106        children: SmallVec::default(),
1107    }
1108}
1109
1110/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI
1111pub struct Div {
1112    interactivity: Interactivity,
1113    children: SmallVec<[AnyElement; 2]>,
1114}
1115
1116/// A frame state for a `Div` element, which contains layout IDs for its children.
1117///
1118/// This struct is used internally by the `Div` element to manage the layout state of its children
1119/// during the UI update cycle. It holds a small vector of `LayoutId` values, each corresponding to
1120/// a child element of the `Div`. These IDs are used to query the layout engine for the computed
1121/// bounds of the children after the layout phase is complete.
1122pub struct DivFrameState {
1123    child_layout_ids: SmallVec<[LayoutId; 2]>,
1124}
1125
1126impl Styled for Div {
1127    fn style(&mut self) -> &mut StyleRefinement {
1128        &mut self.interactivity.base_style
1129    }
1130}
1131
1132impl InteractiveElement for Div {
1133    fn interactivity(&mut self) -> &mut Interactivity {
1134        &mut self.interactivity
1135    }
1136}
1137
1138impl ParentElement for Div {
1139    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1140        self.children.extend(elements)
1141    }
1142}
1143
1144impl Element for Div {
1145    type RequestLayoutState = DivFrameState;
1146    type PrepaintState = Option<Hitbox>;
1147
1148    fn id(&self) -> Option<ElementId> {
1149        self.interactivity.element_id.clone()
1150    }
1151
1152    fn request_layout(
1153        &mut self,
1154        global_id: Option<&GlobalElementId>,
1155        cx: &mut WindowContext,
1156    ) -> (LayoutId, Self::RequestLayoutState) {
1157        let mut child_layout_ids = SmallVec::new();
1158        let layout_id = self
1159            .interactivity
1160            .request_layout(global_id, cx, |style, cx| {
1161                cx.with_text_style(style.text_style().cloned(), |cx| {
1162                    child_layout_ids = self
1163                        .children
1164                        .iter_mut()
1165                        .map(|child| child.request_layout(cx))
1166                        .collect::<SmallVec<_>>();
1167                    cx.request_layout(style, child_layout_ids.iter().copied())
1168                })
1169            });
1170        (layout_id, DivFrameState { child_layout_ids })
1171    }
1172
1173    fn prepaint(
1174        &mut self,
1175        global_id: Option<&GlobalElementId>,
1176        bounds: Bounds<Pixels>,
1177        request_layout: &mut Self::RequestLayoutState,
1178        cx: &mut WindowContext,
1179    ) -> Option<Hitbox> {
1180        let mut child_min = point(Pixels::MAX, Pixels::MAX);
1181        let mut child_max = Point::default();
1182        if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1183            *handle.last_origin.borrow_mut() = bounds.origin - cx.element_offset();
1184        }
1185        let content_size = if request_layout.child_layout_ids.is_empty() {
1186            bounds.size
1187        } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1188            let mut state = scroll_handle.0.borrow_mut();
1189            state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1190            state.bounds = bounds;
1191            let requested = state.requested_scroll_top.take();
1192
1193            for (ix, child_layout_id) in request_layout.child_layout_ids.iter().enumerate() {
1194                let child_bounds = cx.layout_bounds(*child_layout_id);
1195                child_min = child_min.min(&child_bounds.origin);
1196                child_max = child_max.max(&child_bounds.bottom_right());
1197                state.child_bounds.push(child_bounds);
1198
1199                if let Some(requested) = requested.as_ref() {
1200                    if requested.0 == ix {
1201                        *state.offset.borrow_mut() =
1202                            bounds.origin - (child_bounds.origin - point(px(0.), requested.1));
1203                    }
1204                }
1205            }
1206            (child_max - child_min).into()
1207        } else {
1208            for child_layout_id in &request_layout.child_layout_ids {
1209                let child_bounds = cx.layout_bounds(*child_layout_id);
1210                child_min = child_min.min(&child_bounds.origin);
1211                child_max = child_max.max(&child_bounds.bottom_right());
1212            }
1213            (child_max - child_min).into()
1214        };
1215
1216        self.interactivity.prepaint(
1217            global_id,
1218            bounds,
1219            content_size,
1220            cx,
1221            |_style, scroll_offset, hitbox, cx| {
1222                cx.with_element_offset(scroll_offset, |cx| {
1223                    for child in &mut self.children {
1224                        child.prepaint(cx);
1225                    }
1226                });
1227                hitbox
1228            },
1229        )
1230    }
1231
1232    fn paint(
1233        &mut self,
1234        global_id: Option<&GlobalElementId>,
1235        bounds: Bounds<Pixels>,
1236        _request_layout: &mut Self::RequestLayoutState,
1237        hitbox: &mut Option<Hitbox>,
1238        cx: &mut WindowContext,
1239    ) {
1240        self.interactivity
1241            .paint(global_id, bounds, hitbox.as_ref(), cx, |_style, cx| {
1242                for child in &mut self.children {
1243                    child.paint(cx);
1244                }
1245            });
1246    }
1247}
1248
1249impl IntoElement for Div {
1250    type Element = Self;
1251
1252    fn into_element(self) -> Self::Element {
1253        self
1254    }
1255}
1256
1257/// The interactivity struct. Powers all of the general-purpose
1258/// interactivity in the `Div` element.
1259#[derive(Default)]
1260pub struct Interactivity {
1261    /// The element ID of the element. In id is required to support a stateful subset of the interactivity such as on_click.
1262    pub element_id: Option<ElementId>,
1263    /// Whether the element was clicked. This will only be present after layout.
1264    pub active: Option<bool>,
1265    /// Whether the element was hovered. This will only be present after paint if an hitbox
1266    /// was created for the interactive element.
1267    pub hovered: Option<bool>,
1268    pub(crate) tooltip_id: Option<TooltipId>,
1269    pub(crate) content_size: Size<Pixels>,
1270    pub(crate) key_context: Option<KeyContext>,
1271    pub(crate) focusable: bool,
1272    pub(crate) tracked_focus_handle: Option<FocusHandle>,
1273    pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
1274    pub(crate) scroll_anchor: Option<ScrollAnchor>,
1275    pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1276    pub(crate) group: Option<SharedString>,
1277    /// The base style of the element, before any modifications are applied
1278    /// by focus, active, etc.
1279    pub base_style: Box<StyleRefinement>,
1280    pub(crate) focus_style: Option<Box<StyleRefinement>>,
1281    pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1282    pub(crate) hover_style: Option<Box<StyleRefinement>>,
1283    pub(crate) group_hover_style: Option<GroupStyle>,
1284    pub(crate) active_style: Option<Box<StyleRefinement>>,
1285    pub(crate) group_active_style: Option<GroupStyle>,
1286    pub(crate) drag_over_styles: Vec<(
1287        TypeId,
1288        Box<dyn Fn(&dyn Any, &mut WindowContext) -> StyleRefinement>,
1289    )>,
1290    pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1291    pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1292    pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1293    pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1294    pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1295    pub(crate) key_down_listeners: Vec<KeyDownListener>,
1296    pub(crate) key_up_listeners: Vec<KeyUpListener>,
1297    pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
1298    pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1299    pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1300    pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1301    pub(crate) click_listeners: Vec<ClickListener>,
1302    pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
1303    pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
1304    pub(crate) tooltip_builder: Option<TooltipBuilder>,
1305    pub(crate) occlude_mouse: bool,
1306
1307    #[cfg(debug_assertions)]
1308    pub(crate) location: Option<core::panic::Location<'static>>,
1309
1310    #[cfg(any(test, feature = "test-support"))]
1311    pub(crate) debug_selector: Option<String>,
1312}
1313
1314impl Interactivity {
1315    /// Layout this element according to this interactivity state's configured styles
1316    pub fn request_layout(
1317        &mut self,
1318        global_id: Option<&GlobalElementId>,
1319        cx: &mut WindowContext,
1320        f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
1321    ) -> LayoutId {
1322        cx.with_optional_element_state::<InteractiveElementState, _>(
1323            global_id,
1324            |element_state, cx| {
1325                let mut element_state =
1326                    element_state.map(|element_state| element_state.unwrap_or_default());
1327
1328                if let Some(element_state) = element_state.as_ref() {
1329                    if cx.has_active_drag() {
1330                        if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref()
1331                        {
1332                            *pending_mouse_down.borrow_mut() = None;
1333                        }
1334                        if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1335                            *clicked_state.borrow_mut() = ElementClickedState::default();
1336                        }
1337                    }
1338                }
1339
1340                // Ensure we store a focus handle in our element state if we're focusable.
1341                // If there's an explicit focus handle we're tracking, use that. Otherwise
1342                // create a new handle and store it in the element state, which lives for as
1343                // as frames contain an element with this id.
1344                if self.focusable && self.tracked_focus_handle.is_none() {
1345                    if let Some(element_state) = element_state.as_mut() {
1346                        self.tracked_focus_handle = Some(
1347                            element_state
1348                                .focus_handle
1349                                .get_or_insert_with(|| cx.focus_handle())
1350                                .clone(),
1351                        );
1352                    }
1353                }
1354
1355                if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
1356                    self.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
1357                } else if self.base_style.overflow.x == Some(Overflow::Scroll)
1358                    || self.base_style.overflow.y == Some(Overflow::Scroll)
1359                {
1360                    if let Some(element_state) = element_state.as_mut() {
1361                        self.scroll_offset = Some(
1362                            element_state
1363                                .scroll_offset
1364                                .get_or_insert_with(Rc::default)
1365                                .clone(),
1366                        );
1367                    }
1368                }
1369
1370                let style = self.compute_style_internal(None, element_state.as_mut(), cx);
1371                let layout_id = f(style, cx);
1372                (layout_id, element_state)
1373            },
1374        )
1375    }
1376
1377    /// Commit the bounds of this element according to this interactivity state's configured styles.
1378    pub fn prepaint<R>(
1379        &mut self,
1380        global_id: Option<&GlobalElementId>,
1381        bounds: Bounds<Pixels>,
1382        content_size: Size<Pixels>,
1383        cx: &mut WindowContext,
1384        f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
1385    ) -> R {
1386        self.content_size = content_size;
1387        if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1388            cx.set_focus_handle(focus_handle);
1389        }
1390        cx.with_optional_element_state::<InteractiveElementState, _>(
1391            global_id,
1392            |element_state, cx| {
1393                let mut element_state =
1394                    element_state.map(|element_state| element_state.unwrap_or_default());
1395                let style = self.compute_style_internal(None, element_state.as_mut(), cx);
1396
1397                if let Some(element_state) = element_state.as_ref() {
1398                    if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1399                        let clicked_state = clicked_state.borrow();
1400                        self.active = Some(clicked_state.element);
1401                    }
1402
1403                    if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
1404                        if let Some(active_tooltip) = active_tooltip.borrow().as_ref() {
1405                            if let Some(tooltip) = active_tooltip.tooltip.clone() {
1406                                self.tooltip_id = Some(cx.set_tooltip(tooltip));
1407                            }
1408                        }
1409                    }
1410                }
1411
1412                cx.with_text_style(style.text_style().cloned(), |cx| {
1413                    cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
1414                        let hitbox = if self.should_insert_hitbox(&style) {
1415                            Some(cx.insert_hitbox(bounds, self.occlude_mouse))
1416                        } else {
1417                            None
1418                        };
1419
1420                        let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
1421                        let result = f(&style, scroll_offset, hitbox, cx);
1422                        (result, element_state)
1423                    })
1424                })
1425            },
1426        )
1427    }
1428
1429    fn should_insert_hitbox(&self, style: &Style) -> bool {
1430        self.occlude_mouse
1431            || style.mouse_cursor.is_some()
1432            || self.group.is_some()
1433            || self.scroll_offset.is_some()
1434            || self.tracked_focus_handle.is_some()
1435            || self.hover_style.is_some()
1436            || self.group_hover_style.is_some()
1437            || !self.mouse_up_listeners.is_empty()
1438            || !self.mouse_down_listeners.is_empty()
1439            || !self.mouse_move_listeners.is_empty()
1440            || !self.click_listeners.is_empty()
1441            || !self.scroll_wheel_listeners.is_empty()
1442            || self.drag_listener.is_some()
1443            || !self.drop_listeners.is_empty()
1444            || self.tooltip_builder.is_some()
1445    }
1446
1447    fn clamp_scroll_position(
1448        &self,
1449        bounds: Bounds<Pixels>,
1450        style: &Style,
1451        cx: &mut WindowContext,
1452    ) -> Point<Pixels> {
1453        if let Some(scroll_offset) = self.scroll_offset.as_ref() {
1454            if let Some(scroll_handle) = &self.tracked_scroll_handle {
1455                scroll_handle.0.borrow_mut().overflow = style.overflow;
1456            }
1457
1458            let rem_size = cx.rem_size();
1459            let padding_size = size(
1460                style
1461                    .padding
1462                    .left
1463                    .to_pixels(bounds.size.width.into(), rem_size)
1464                    + style
1465                        .padding
1466                        .right
1467                        .to_pixels(bounds.size.width.into(), rem_size),
1468                style
1469                    .padding
1470                    .top
1471                    .to_pixels(bounds.size.height.into(), rem_size)
1472                    + style
1473                        .padding
1474                        .bottom
1475                        .to_pixels(bounds.size.height.into(), rem_size),
1476            );
1477            let scroll_max = (self.content_size + padding_size - bounds.size).max(&Size::default());
1478            // Clamp scroll offset in case scroll max is smaller now (e.g., if children
1479            // were removed or the bounds became larger).
1480            let mut scroll_offset = scroll_offset.borrow_mut();
1481            scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1482            scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1483            *scroll_offset
1484        } else {
1485            Point::default()
1486        }
1487    }
1488
1489    /// Paint this element according to this interactivity state's configured styles
1490    /// and bind the element's mouse and keyboard events.
1491    ///
1492    /// content_size is the size of the content of the element, which may be larger than the
1493    /// element's bounds if the element is scrollable.
1494    ///
1495    /// the final computed style will be passed to the provided function, along
1496    /// with the current scroll offset
1497    pub fn paint(
1498        &mut self,
1499        global_id: Option<&GlobalElementId>,
1500        bounds: Bounds<Pixels>,
1501        hitbox: Option<&Hitbox>,
1502        cx: &mut WindowContext,
1503        f: impl FnOnce(&Style, &mut WindowContext),
1504    ) {
1505        self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(cx));
1506        cx.with_optional_element_state::<InteractiveElementState, _>(
1507            global_id,
1508            |element_state, cx| {
1509                let mut element_state =
1510                    element_state.map(|element_state| element_state.unwrap_or_default());
1511
1512                let style = self.compute_style_internal(hitbox, element_state.as_mut(), cx);
1513
1514                #[cfg(any(feature = "test-support", test))]
1515                if let Some(debug_selector) = &self.debug_selector {
1516                    cx.window
1517                        .next_frame
1518                        .debug_bounds
1519                        .insert(debug_selector.clone(), bounds);
1520                }
1521
1522                self.paint_hover_group_handler(cx);
1523
1524                if style.visibility == Visibility::Hidden {
1525                    return ((), element_state);
1526                }
1527
1528                cx.with_element_opacity(style.opacity, |cx| {
1529                    style.paint(bounds, cx, |cx: &mut WindowContext| {
1530                        cx.with_text_style(style.text_style().cloned(), |cx| {
1531                            cx.with_content_mask(
1532                                style.overflow_mask(bounds, cx.rem_size()),
1533                                |cx| {
1534                                    if let Some(hitbox) = hitbox {
1535                                        #[cfg(debug_assertions)]
1536                                        self.paint_debug_info(global_id, hitbox, &style, cx);
1537
1538                                        if !cx.has_active_drag() {
1539                                            if let Some(mouse_cursor) = style.mouse_cursor {
1540                                                cx.set_cursor_style(mouse_cursor, hitbox);
1541                                            }
1542                                        }
1543
1544                                        if let Some(group) = self.group.clone() {
1545                                            GroupHitboxes::push(group, hitbox.id, cx);
1546                                        }
1547
1548                                        self.paint_mouse_listeners(
1549                                            hitbox,
1550                                            element_state.as_mut(),
1551                                            cx,
1552                                        );
1553                                        self.paint_scroll_listener(hitbox, &style, cx);
1554                                    }
1555
1556                                    self.paint_keyboard_listeners(cx);
1557                                    f(&style, cx);
1558
1559                                    if hitbox.is_some() {
1560                                        if let Some(group) = self.group.as_ref() {
1561                                            GroupHitboxes::pop(group, cx);
1562                                        }
1563                                    }
1564                                },
1565                            );
1566                        });
1567                    });
1568                });
1569
1570                ((), element_state)
1571            },
1572        );
1573    }
1574
1575    #[cfg(debug_assertions)]
1576    fn paint_debug_info(
1577        &self,
1578        global_id: Option<&GlobalElementId>,
1579        hitbox: &Hitbox,
1580        style: &Style,
1581        cx: &mut WindowContext,
1582    ) {
1583        if global_id.is_some()
1584            && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
1585            && hitbox.is_hovered(cx)
1586        {
1587            const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
1588            let element_id = format!("{:?}", global_id.unwrap());
1589            let str_len = element_id.len();
1590
1591            let render_debug_text = |cx: &mut WindowContext| {
1592                if let Some(text) = cx
1593                    .text_system()
1594                    .shape_text(
1595                        element_id.into(),
1596                        FONT_SIZE,
1597                        &[cx.text_style().to_run(str_len)],
1598                        None,
1599                    )
1600                    .ok()
1601                    .and_then(|mut text| text.pop())
1602                {
1603                    text.paint(hitbox.origin, FONT_SIZE, cx).ok();
1604
1605                    let text_bounds = crate::Bounds {
1606                        origin: hitbox.origin,
1607                        size: text.size(FONT_SIZE),
1608                    };
1609                    if self.location.is_some()
1610                        && text_bounds.contains(&cx.mouse_position())
1611                        && cx.modifiers().secondary()
1612                    {
1613                        let secondary_held = cx.modifiers().secondary();
1614                        cx.on_key_event({
1615                            move |e: &crate::ModifiersChangedEvent, _phase, cx| {
1616                                if e.modifiers.secondary() != secondary_held
1617                                    && text_bounds.contains(&cx.mouse_position())
1618                                {
1619                                    cx.refresh();
1620                                }
1621                            }
1622                        });
1623
1624                        let was_hovered = hitbox.is_hovered(cx);
1625                        cx.on_mouse_event({
1626                            let hitbox = hitbox.clone();
1627                            move |_: &MouseMoveEvent, phase, cx| {
1628                                if phase == DispatchPhase::Capture {
1629                                    let hovered = hitbox.is_hovered(cx);
1630                                    if hovered != was_hovered {
1631                                        cx.refresh();
1632                                    }
1633                                }
1634                            }
1635                        });
1636
1637                        cx.on_mouse_event({
1638                            let hitbox = hitbox.clone();
1639                            let location = self.location.unwrap();
1640                            move |e: &crate::MouseDownEvent, phase, cx| {
1641                                if text_bounds.contains(&e.position)
1642                                    && phase.capture()
1643                                    && hitbox.is_hovered(cx)
1644                                {
1645                                    cx.stop_propagation();
1646                                    let Ok(dir) = std::env::current_dir() else {
1647                                        return;
1648                                    };
1649
1650                                    eprintln!(
1651                                        "This element was created at:\n{}:{}:{}",
1652                                        dir.join(location.file()).to_string_lossy(),
1653                                        location.line(),
1654                                        location.column()
1655                                    );
1656                                }
1657                            }
1658                        });
1659                        cx.paint_quad(crate::outline(
1660                            crate::Bounds {
1661                                origin: hitbox.origin
1662                                    + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
1663                                size: crate::Size {
1664                                    width: text_bounds.size.width,
1665                                    height: crate::px(1.),
1666                                },
1667                            },
1668                            crate::red(),
1669                        ))
1670                    }
1671                }
1672            };
1673
1674            cx.with_text_style(
1675                Some(crate::TextStyleRefinement {
1676                    color: Some(crate::red()),
1677                    line_height: Some(FONT_SIZE.into()),
1678                    background_color: Some(crate::white()),
1679                    ..Default::default()
1680                }),
1681                render_debug_text,
1682            )
1683        }
1684    }
1685
1686    fn paint_mouse_listeners(
1687        &mut self,
1688        hitbox: &Hitbox,
1689        element_state: Option<&mut InteractiveElementState>,
1690        cx: &mut WindowContext,
1691    ) {
1692        // If this element can be focused, register a mouse down listener
1693        // that will automatically transfer focus when hitting the element.
1694        // This behavior can be suppressed by using `cx.prevent_default()`.
1695        if let Some(focus_handle) = self.tracked_focus_handle.clone() {
1696            let hitbox = hitbox.clone();
1697            cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
1698                if phase == DispatchPhase::Bubble
1699                    && hitbox.is_hovered(cx)
1700                    && !cx.default_prevented()
1701                {
1702                    cx.focus(&focus_handle);
1703                    // If there is a parent that is also focusable, prevent it
1704                    // from transferring focus because we already did so.
1705                    cx.prevent_default();
1706                }
1707            });
1708        }
1709
1710        for listener in self.mouse_down_listeners.drain(..) {
1711            let hitbox = hitbox.clone();
1712            cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
1713                listener(event, phase, &hitbox, cx);
1714            })
1715        }
1716
1717        for listener in self.mouse_up_listeners.drain(..) {
1718            let hitbox = hitbox.clone();
1719            cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
1720                listener(event, phase, &hitbox, cx);
1721            })
1722        }
1723
1724        for listener in self.mouse_move_listeners.drain(..) {
1725            let hitbox = hitbox.clone();
1726            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1727                listener(event, phase, &hitbox, cx);
1728            })
1729        }
1730
1731        for listener in self.scroll_wheel_listeners.drain(..) {
1732            let hitbox = hitbox.clone();
1733            cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1734                listener(event, phase, &hitbox, cx);
1735            })
1736        }
1737
1738        if self.hover_style.is_some()
1739            || self.base_style.mouse_cursor.is_some()
1740            || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
1741        {
1742            let hitbox = hitbox.clone();
1743            let was_hovered = hitbox.is_hovered(cx);
1744            cx.on_mouse_event(move |_: &MouseMoveEvent, phase, cx| {
1745                let hovered = hitbox.is_hovered(cx);
1746                if phase == DispatchPhase::Capture && hovered != was_hovered {
1747                    cx.refresh();
1748                }
1749            });
1750        }
1751
1752        let mut drag_listener = mem::take(&mut self.drag_listener);
1753        let drop_listeners = mem::take(&mut self.drop_listeners);
1754        let click_listeners = mem::take(&mut self.click_listeners);
1755        let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
1756
1757        if !drop_listeners.is_empty() {
1758            let hitbox = hitbox.clone();
1759            cx.on_mouse_event({
1760                move |_: &MouseUpEvent, phase, cx| {
1761                    if let Some(drag) = &cx.active_drag {
1762                        if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
1763                            let drag_state_type = drag.value.as_ref().type_id();
1764                            for (drop_state_type, listener) in &drop_listeners {
1765                                if *drop_state_type == drag_state_type {
1766                                    let drag = cx
1767                                        .active_drag
1768                                        .take()
1769                                        .expect("checked for type drag state type above");
1770
1771                                    let mut can_drop = true;
1772                                    if let Some(predicate) = &can_drop_predicate {
1773                                        can_drop = predicate(drag.value.as_ref(), cx);
1774                                    }
1775
1776                                    if can_drop {
1777                                        listener(drag.value.as_ref(), cx);
1778                                        cx.refresh();
1779                                        cx.stop_propagation();
1780                                    }
1781                                }
1782                            }
1783                        }
1784                    }
1785                }
1786            });
1787        }
1788
1789        if let Some(element_state) = element_state {
1790            if !click_listeners.is_empty() || drag_listener.is_some() {
1791                let pending_mouse_down = element_state
1792                    .pending_mouse_down
1793                    .get_or_insert_with(Default::default)
1794                    .clone();
1795
1796                let clicked_state = element_state
1797                    .clicked_state
1798                    .get_or_insert_with(Default::default)
1799                    .clone();
1800
1801                cx.on_mouse_event({
1802                    let pending_mouse_down = pending_mouse_down.clone();
1803                    let hitbox = hitbox.clone();
1804                    move |event: &MouseDownEvent, phase, cx| {
1805                        if phase == DispatchPhase::Bubble
1806                            && event.button == MouseButton::Left
1807                            && hitbox.is_hovered(cx)
1808                        {
1809                            *pending_mouse_down.borrow_mut() = Some(event.clone());
1810                            cx.refresh();
1811                        }
1812                    }
1813                });
1814
1815                cx.on_mouse_event({
1816                    let pending_mouse_down = pending_mouse_down.clone();
1817                    let hitbox = hitbox.clone();
1818                    move |event: &MouseMoveEvent, phase, cx| {
1819                        if phase == DispatchPhase::Capture {
1820                            return;
1821                        }
1822
1823                        let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1824                        if let Some(mouse_down) = pending_mouse_down.clone() {
1825                            if !cx.has_active_drag()
1826                                && (event.position - mouse_down.position).magnitude()
1827                                    > DRAG_THRESHOLD
1828                            {
1829                                if let Some((drag_value, drag_listener)) = drag_listener.take() {
1830                                    *clicked_state.borrow_mut() = ElementClickedState::default();
1831                                    let cursor_offset = event.position - hitbox.origin;
1832                                    let drag =
1833                                        (drag_listener)(drag_value.as_ref(), cursor_offset, cx);
1834                                    cx.active_drag = Some(AnyDrag {
1835                                        view: drag,
1836                                        value: drag_value,
1837                                        cursor_offset,
1838                                    });
1839                                    pending_mouse_down.take();
1840                                    cx.refresh();
1841                                    cx.stop_propagation();
1842                                }
1843                            }
1844                        }
1845                    }
1846                });
1847
1848                cx.on_mouse_event({
1849                    let mut captured_mouse_down = None;
1850                    let hitbox = hitbox.clone();
1851                    move |event: &MouseUpEvent, phase, cx| match phase {
1852                        // Clear the pending mouse down during the capture phase,
1853                        // so that it happens even if another event handler stops
1854                        // propagation.
1855                        DispatchPhase::Capture => {
1856                            let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1857                            if pending_mouse_down.is_some() && hitbox.is_hovered(cx) {
1858                                captured_mouse_down = pending_mouse_down.take();
1859                                cx.refresh();
1860                            }
1861                        }
1862                        // Fire click handlers during the bubble phase.
1863                        DispatchPhase::Bubble => {
1864                            if let Some(mouse_down) = captured_mouse_down.take() {
1865                                let mouse_click = ClickEvent {
1866                                    down: mouse_down,
1867                                    up: event.clone(),
1868                                };
1869                                for listener in &click_listeners {
1870                                    listener(&mouse_click, cx);
1871                                }
1872                            }
1873                        }
1874                    }
1875                });
1876            }
1877
1878            if let Some(hover_listener) = self.hover_listener.take() {
1879                let hitbox = hitbox.clone();
1880                let was_hovered = element_state
1881                    .hover_state
1882                    .get_or_insert_with(Default::default)
1883                    .clone();
1884                let has_mouse_down = element_state
1885                    .pending_mouse_down
1886                    .get_or_insert_with(Default::default)
1887                    .clone();
1888
1889                cx.on_mouse_event(move |_: &MouseMoveEvent, phase, cx| {
1890                    if phase != DispatchPhase::Bubble {
1891                        return;
1892                    }
1893                    let is_hovered = has_mouse_down.borrow().is_none()
1894                        && !cx.has_active_drag()
1895                        && hitbox.is_hovered(cx);
1896                    let mut was_hovered = was_hovered.borrow_mut();
1897
1898                    if is_hovered != *was_hovered {
1899                        *was_hovered = is_hovered;
1900                        drop(was_hovered);
1901
1902                        hover_listener(&is_hovered, cx);
1903                    }
1904                });
1905            }
1906
1907            // Ensure to remove active tooltip if tooltip builder is none
1908            if self.tooltip_builder.is_none() {
1909                element_state.active_tooltip.take();
1910            }
1911
1912            if let Some(tooltip_builder) = self.tooltip_builder.take() {
1913                let tooltip_is_hoverable = tooltip_builder.hoverable;
1914                let active_tooltip = element_state
1915                    .active_tooltip
1916                    .get_or_insert_with(Default::default)
1917                    .clone();
1918                let pending_mouse_down = element_state
1919                    .pending_mouse_down
1920                    .get_or_insert_with(Default::default)
1921                    .clone();
1922
1923                cx.on_mouse_event({
1924                    let active_tooltip = active_tooltip.clone();
1925                    let hitbox = hitbox.clone();
1926                    let source_bounds = hitbox.bounds;
1927                    let tooltip_id = self.tooltip_id;
1928                    move |_: &MouseMoveEvent, phase, cx| {
1929                        let is_hovered =
1930                            pending_mouse_down.borrow().is_none() && hitbox.is_hovered(cx);
1931                        let tooltip_is_hovered =
1932                            tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
1933                        if !is_hovered && (!tooltip_is_hoverable || !tooltip_is_hovered) {
1934                            if active_tooltip.borrow_mut().take().is_some() {
1935                                cx.refresh();
1936                            }
1937
1938                            return;
1939                        }
1940
1941                        if phase != DispatchPhase::Bubble {
1942                            return;
1943                        }
1944
1945                        if active_tooltip.borrow().is_none() {
1946                            let task = cx.spawn({
1947                                let active_tooltip = active_tooltip.clone();
1948                                let build_tooltip = tooltip_builder.build.clone();
1949                                move |mut cx| async move {
1950                                    cx.background_executor().timer(TOOLTIP_DELAY).await;
1951                                    cx.update(|cx| {
1952                                        active_tooltip.borrow_mut().replace(ActiveTooltip {
1953                                            tooltip: Some(AnyTooltip {
1954                                                view: build_tooltip(cx),
1955                                                mouse_position: cx.mouse_position(),
1956                                                hoverable: tooltip_is_hoverable,
1957                                                origin_bounds: source_bounds,
1958                                            }),
1959                                            _task: None,
1960                                        });
1961                                        cx.refresh();
1962                                    })
1963                                    .ok();
1964                                }
1965                            });
1966                            active_tooltip.borrow_mut().replace(ActiveTooltip {
1967                                tooltip: None,
1968                                _task: Some(task),
1969                            });
1970                        }
1971                    }
1972                });
1973
1974                cx.on_mouse_event({
1975                    let active_tooltip = active_tooltip.clone();
1976                    let tooltip_id = self.tooltip_id;
1977                    move |_: &MouseDownEvent, _, cx| {
1978                        let tooltip_is_hovered =
1979                            tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
1980
1981                        if (!tooltip_is_hoverable || !tooltip_is_hovered)
1982                            && active_tooltip.borrow_mut().take().is_some()
1983                        {
1984                            cx.refresh();
1985                        }
1986                    }
1987                });
1988
1989                cx.on_mouse_event({
1990                    let active_tooltip = active_tooltip.clone();
1991                    let tooltip_id = self.tooltip_id;
1992                    move |_: &ScrollWheelEvent, _, cx| {
1993                        let tooltip_is_hovered =
1994                            tooltip_id.map_or(false, |tooltip_id| tooltip_id.is_hovered(cx));
1995                        if (!tooltip_is_hoverable || !tooltip_is_hovered)
1996                            && active_tooltip.borrow_mut().take().is_some()
1997                        {
1998                            cx.refresh();
1999                        }
2000                    }
2001                })
2002            }
2003
2004            let active_state = element_state
2005                .clicked_state
2006                .get_or_insert_with(Default::default)
2007                .clone();
2008            if active_state.borrow().is_clicked() {
2009                cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
2010                    if phase == DispatchPhase::Capture {
2011                        *active_state.borrow_mut() = ElementClickedState::default();
2012                        cx.refresh();
2013                    }
2014                });
2015            } else {
2016                let active_group_hitbox = self
2017                    .group_active_style
2018                    .as_ref()
2019                    .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
2020                let hitbox = hitbox.clone();
2021                cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
2022                    if phase == DispatchPhase::Bubble && !cx.default_prevented() {
2023                        let group_hovered = active_group_hitbox
2024                            .map_or(false, |group_hitbox_id| group_hitbox_id.is_hovered(cx));
2025                        let element_hovered = hitbox.is_hovered(cx);
2026                        if group_hovered || element_hovered {
2027                            *active_state.borrow_mut() = ElementClickedState {
2028                                group: group_hovered,
2029                                element: element_hovered,
2030                            };
2031                            cx.refresh();
2032                        }
2033                    }
2034                });
2035            }
2036        }
2037    }
2038
2039    fn paint_keyboard_listeners(&mut self, cx: &mut WindowContext) {
2040        let key_down_listeners = mem::take(&mut self.key_down_listeners);
2041        let key_up_listeners = mem::take(&mut self.key_up_listeners);
2042        let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
2043        let action_listeners = mem::take(&mut self.action_listeners);
2044        if let Some(context) = self.key_context.clone() {
2045            cx.set_key_context(context);
2046        }
2047
2048        for listener in key_down_listeners {
2049            cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
2050                listener(event, phase, cx);
2051            })
2052        }
2053
2054        for listener in key_up_listeners {
2055            cx.on_key_event(move |event: &KeyUpEvent, phase, cx| {
2056                listener(event, phase, cx);
2057            })
2058        }
2059
2060        for listener in modifiers_changed_listeners {
2061            cx.on_modifiers_changed(move |event: &ModifiersChangedEvent, cx| {
2062                listener(event, cx);
2063            })
2064        }
2065
2066        for (action_type, listener) in action_listeners {
2067            cx.on_action(action_type, listener)
2068        }
2069    }
2070
2071    fn paint_hover_group_handler(&self, cx: &mut WindowContext) {
2072        let group_hitbox = self
2073            .group_hover_style
2074            .as_ref()
2075            .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
2076
2077        if let Some(group_hitbox) = group_hitbox {
2078            let was_hovered = group_hitbox.is_hovered(cx);
2079            cx.on_mouse_event(move |_: &MouseMoveEvent, phase, cx| {
2080                let hovered = group_hitbox.is_hovered(cx);
2081                if phase == DispatchPhase::Capture && hovered != was_hovered {
2082                    cx.refresh();
2083                }
2084            });
2085        }
2086    }
2087
2088    fn paint_scroll_listener(&self, hitbox: &Hitbox, style: &Style, cx: &mut WindowContext) {
2089        if let Some(scroll_offset) = self.scroll_offset.clone() {
2090            let overflow = style.overflow;
2091            let allow_concurrent_scroll = style.allow_concurrent_scroll;
2092            let line_height = cx.line_height();
2093            let hitbox = hitbox.clone();
2094            cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
2095                if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
2096                    let mut scroll_offset = scroll_offset.borrow_mut();
2097                    let old_scroll_offset = *scroll_offset;
2098                    let delta = event.delta.pixel_delta(line_height);
2099
2100                    let mut delta_x = Pixels::ZERO;
2101                    if overflow.x == Overflow::Scroll {
2102                        if !delta.x.is_zero() {
2103                            delta_x = delta.x;
2104                        } else if overflow.y != Overflow::Scroll {
2105                            delta_x = delta.y;
2106                        }
2107                    }
2108                    let mut delta_y = Pixels::ZERO;
2109                    if overflow.y == Overflow::Scroll {
2110                        if !delta.y.is_zero() {
2111                            delta_y = delta.y;
2112                        } else if overflow.x != Overflow::Scroll {
2113                            delta_y = delta.x;
2114                        }
2115                    }
2116                    if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
2117                        if delta_x.abs() > delta_y.abs() {
2118                            delta_y = Pixels::ZERO;
2119                        } else {
2120                            delta_x = Pixels::ZERO;
2121                        }
2122                    }
2123                    scroll_offset.y += delta_y;
2124                    scroll_offset.x += delta_x;
2125                    cx.stop_propagation();
2126                    if *scroll_offset != old_scroll_offset {
2127                        cx.refresh();
2128                    }
2129                }
2130            });
2131        }
2132    }
2133
2134    /// Compute the visual style for this element, based on the current bounds and the element's state.
2135    pub fn compute_style(
2136        &self,
2137        global_id: Option<&GlobalElementId>,
2138        hitbox: Option<&Hitbox>,
2139        cx: &mut WindowContext,
2140    ) -> Style {
2141        cx.with_optional_element_state(global_id, |element_state, cx| {
2142            let mut element_state =
2143                element_state.map(|element_state| element_state.unwrap_or_default());
2144            let style = self.compute_style_internal(hitbox, element_state.as_mut(), cx);
2145            (style, element_state)
2146        })
2147    }
2148
2149    /// Called from internal methods that have already called with_element_state.
2150    fn compute_style_internal(
2151        &self,
2152        hitbox: Option<&Hitbox>,
2153        element_state: Option<&mut InteractiveElementState>,
2154        cx: &mut WindowContext,
2155    ) -> Style {
2156        let mut style = Style::default();
2157        style.refine(&self.base_style);
2158
2159        if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2160            if let Some(in_focus_style) = self.in_focus_style.as_ref() {
2161                if focus_handle.within_focused(cx) {
2162                    style.refine(in_focus_style);
2163                }
2164            }
2165
2166            if let Some(focus_style) = self.focus_style.as_ref() {
2167                if focus_handle.is_focused(cx) {
2168                    style.refine(focus_style);
2169                }
2170            }
2171        }
2172
2173        if let Some(hitbox) = hitbox {
2174            if !cx.has_active_drag() {
2175                if let Some(group_hover) = self.group_hover_style.as_ref() {
2176                    if let Some(group_hitbox_id) =
2177                        GroupHitboxes::get(&group_hover.group, cx.deref_mut())
2178                    {
2179                        if group_hitbox_id.is_hovered(cx) {
2180                            style.refine(&group_hover.style);
2181                        }
2182                    }
2183                }
2184
2185                if let Some(hover_style) = self.hover_style.as_ref() {
2186                    if hitbox.is_hovered(cx) {
2187                        style.refine(hover_style);
2188                    }
2189                }
2190            }
2191
2192            if let Some(drag) = cx.active_drag.take() {
2193                let mut can_drop = true;
2194                if let Some(can_drop_predicate) = &self.can_drop_predicate {
2195                    can_drop = can_drop_predicate(drag.value.as_ref(), cx);
2196                }
2197
2198                if can_drop {
2199                    for (state_type, group_drag_style) in &self.group_drag_over_styles {
2200                        if let Some(group_hitbox_id) =
2201                            GroupHitboxes::get(&group_drag_style.group, cx.deref_mut())
2202                        {
2203                            if *state_type == drag.value.as_ref().type_id()
2204                                && group_hitbox_id.is_hovered(cx)
2205                            {
2206                                style.refine(&group_drag_style.style);
2207                            }
2208                        }
2209                    }
2210
2211                    for (state_type, build_drag_over_style) in &self.drag_over_styles {
2212                        if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(cx) {
2213                            style.refine(&build_drag_over_style(drag.value.as_ref(), cx));
2214                        }
2215                    }
2216                }
2217
2218                cx.active_drag = Some(drag);
2219            }
2220        }
2221
2222        if let Some(element_state) = element_state {
2223            let clicked_state = element_state
2224                .clicked_state
2225                .get_or_insert_with(Default::default)
2226                .borrow();
2227            if clicked_state.group {
2228                if let Some(group) = self.group_active_style.as_ref() {
2229                    style.refine(&group.style)
2230                }
2231            }
2232
2233            if let Some(active_style) = self.active_style.as_ref() {
2234                if clicked_state.element {
2235                    style.refine(active_style)
2236                }
2237            }
2238        }
2239
2240        style
2241    }
2242}
2243
2244/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks
2245/// and scroll offsets.
2246#[derive(Default)]
2247pub struct InteractiveElementState {
2248    pub(crate) focus_handle: Option<FocusHandle>,
2249    pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2250    pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
2251    pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2252    pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2253    pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2254}
2255
2256/// The current active tooltip
2257pub struct ActiveTooltip {
2258    pub(crate) tooltip: Option<AnyTooltip>,
2259    pub(crate) _task: Option<Task<()>>,
2260}
2261
2262/// Whether or not the element or a group that contains it is clicked by the mouse.
2263#[derive(Copy, Clone, Default, Eq, PartialEq)]
2264pub struct ElementClickedState {
2265    /// True if this element's group has been clicked, false otherwise
2266    pub group: bool,
2267
2268    /// True if this element has been clicked, false otherwise
2269    pub element: bool,
2270}
2271
2272impl ElementClickedState {
2273    fn is_clicked(&self) -> bool {
2274        self.group || self.element
2275    }
2276}
2277
2278#[derive(Default)]
2279pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
2280
2281impl Global for GroupHitboxes {}
2282
2283impl GroupHitboxes {
2284    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<HitboxId> {
2285        cx.default_global::<Self>()
2286            .0
2287            .get(name)
2288            .and_then(|bounds_stack| bounds_stack.last())
2289            .cloned()
2290    }
2291
2292    pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut AppContext) {
2293        cx.default_global::<Self>()
2294            .0
2295            .entry(name)
2296            .or_default()
2297            .push(hitbox_id);
2298    }
2299
2300    pub fn pop(name: &SharedString, cx: &mut AppContext) {
2301        cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
2302    }
2303}
2304
2305/// A wrapper around an element that can be focused.
2306pub struct Focusable<E> {
2307    /// The element that is focusable
2308    pub element: E,
2309}
2310
2311impl<E: InteractiveElement> FocusableElement for Focusable<E> {}
2312
2313impl<E> InteractiveElement for Focusable<E>
2314where
2315    E: InteractiveElement,
2316{
2317    fn interactivity(&mut self) -> &mut Interactivity {
2318        self.element.interactivity()
2319    }
2320}
2321
2322impl<E: StatefulInteractiveElement> StatefulInteractiveElement for Focusable<E> {}
2323
2324impl<E> Styled for Focusable<E>
2325where
2326    E: Styled,
2327{
2328    fn style(&mut self) -> &mut StyleRefinement {
2329        self.element.style()
2330    }
2331}
2332
2333impl<E> Element for Focusable<E>
2334where
2335    E: Element,
2336{
2337    type RequestLayoutState = E::RequestLayoutState;
2338    type PrepaintState = E::PrepaintState;
2339
2340    fn id(&self) -> Option<ElementId> {
2341        self.element.id()
2342    }
2343
2344    fn request_layout(
2345        &mut self,
2346        id: Option<&GlobalElementId>,
2347        cx: &mut WindowContext,
2348    ) -> (LayoutId, Self::RequestLayoutState) {
2349        self.element.request_layout(id, cx)
2350    }
2351
2352    fn prepaint(
2353        &mut self,
2354        id: Option<&GlobalElementId>,
2355        bounds: Bounds<Pixels>,
2356        state: &mut Self::RequestLayoutState,
2357        cx: &mut WindowContext,
2358    ) -> E::PrepaintState {
2359        self.element.prepaint(id, bounds, state, cx)
2360    }
2361
2362    fn paint(
2363        &mut self,
2364        id: Option<&GlobalElementId>,
2365        bounds: Bounds<Pixels>,
2366        request_layout: &mut Self::RequestLayoutState,
2367        prepaint: &mut Self::PrepaintState,
2368        cx: &mut WindowContext,
2369    ) {
2370        self.element.paint(id, bounds, request_layout, prepaint, cx)
2371    }
2372}
2373
2374impl<E> IntoElement for Focusable<E>
2375where
2376    E: IntoElement,
2377{
2378    type Element = E::Element;
2379
2380    fn into_element(self) -> Self::Element {
2381        self.element.into_element()
2382    }
2383}
2384
2385impl<E> ParentElement for Focusable<E>
2386where
2387    E: ParentElement,
2388{
2389    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2390        self.element.extend(elements)
2391    }
2392}
2393
2394/// A wrapper around an element that can store state, produced after assigning an ElementId.
2395pub struct Stateful<E> {
2396    pub(crate) element: E,
2397}
2398
2399impl<E> Styled for Stateful<E>
2400where
2401    E: Styled,
2402{
2403    fn style(&mut self) -> &mut StyleRefinement {
2404        self.element.style()
2405    }
2406}
2407
2408impl<E> StatefulInteractiveElement for Stateful<E>
2409where
2410    E: Element,
2411    Self: InteractiveElement,
2412{
2413}
2414
2415impl<E> InteractiveElement for Stateful<E>
2416where
2417    E: InteractiveElement,
2418{
2419    fn interactivity(&mut self) -> &mut Interactivity {
2420        self.element.interactivity()
2421    }
2422}
2423
2424impl<E: FocusableElement> FocusableElement for Stateful<E> {}
2425
2426impl<E> Element for Stateful<E>
2427where
2428    E: Element,
2429{
2430    type RequestLayoutState = E::RequestLayoutState;
2431    type PrepaintState = E::PrepaintState;
2432
2433    fn id(&self) -> Option<ElementId> {
2434        self.element.id()
2435    }
2436
2437    fn request_layout(
2438        &mut self,
2439        id: Option<&GlobalElementId>,
2440        cx: &mut WindowContext,
2441    ) -> (LayoutId, Self::RequestLayoutState) {
2442        self.element.request_layout(id, cx)
2443    }
2444
2445    fn prepaint(
2446        &mut self,
2447        id: Option<&GlobalElementId>,
2448        bounds: Bounds<Pixels>,
2449        state: &mut Self::RequestLayoutState,
2450        cx: &mut WindowContext,
2451    ) -> E::PrepaintState {
2452        self.element.prepaint(id, bounds, state, cx)
2453    }
2454
2455    fn paint(
2456        &mut self,
2457        id: Option<&GlobalElementId>,
2458        bounds: Bounds<Pixels>,
2459        request_layout: &mut Self::RequestLayoutState,
2460        prepaint: &mut Self::PrepaintState,
2461        cx: &mut WindowContext,
2462    ) {
2463        self.element.paint(id, bounds, request_layout, prepaint, cx);
2464    }
2465}
2466
2467impl<E> IntoElement for Stateful<E>
2468where
2469    E: Element,
2470{
2471    type Element = Self;
2472
2473    fn into_element(self) -> Self::Element {
2474        self
2475    }
2476}
2477
2478impl<E> ParentElement for Stateful<E>
2479where
2480    E: ParentElement,
2481{
2482    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
2483        self.element.extend(elements)
2484    }
2485}
2486
2487/// Represents an element that can be scrolled *to* in its parent element.
2488///
2489/// Contrary to [ScrollHandle::scroll_to_item], an anchored element does not have to be an immediate child of the parent.
2490#[derive(Clone)]
2491pub struct ScrollAnchor {
2492    handle: ScrollHandle,
2493    last_origin: Rc<RefCell<Point<Pixels>>>,
2494}
2495
2496impl ScrollAnchor {
2497    /// Creates a [ScrollAnchor] associated with a given [ScrollHandle].
2498    pub fn for_handle(handle: ScrollHandle) -> Self {
2499        Self {
2500            handle,
2501            last_origin: Default::default(),
2502        }
2503    }
2504    /// Request scroll to this item on the next frame.
2505    pub fn scroll_to(&self, cx: &mut WindowContext) {
2506        let this = self.clone();
2507
2508        cx.on_next_frame(move |_| {
2509            let viewport_bounds = this.handle.bounds();
2510            let self_bounds = *this.last_origin.borrow();
2511            this.handle.set_offset(viewport_bounds.origin - self_bounds);
2512        });
2513    }
2514}
2515#[derive(Default, Debug)]
2516struct ScrollHandleState {
2517    offset: Rc<RefCell<Point<Pixels>>>,
2518    bounds: Bounds<Pixels>,
2519    child_bounds: Vec<Bounds<Pixels>>,
2520    requested_scroll_top: Option<(usize, Pixels)>,
2521    overflow: Point<Overflow>,
2522}
2523
2524/// A handle to the scrollable aspects of an element.
2525/// Used for accessing scroll state, like the current scroll offset,
2526/// and for mutating the scroll state, like scrolling to a specific child.
2527#[derive(Clone, Debug)]
2528pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
2529
2530impl Default for ScrollHandle {
2531    fn default() -> Self {
2532        Self::new()
2533    }
2534}
2535
2536impl ScrollHandle {
2537    /// Construct a new scroll handle.
2538    pub fn new() -> Self {
2539        Self(Rc::default())
2540    }
2541
2542    /// Get the current scroll offset.
2543    pub fn offset(&self) -> Point<Pixels> {
2544        *self.0.borrow().offset.borrow()
2545    }
2546
2547    /// Get the top child that's scrolled into view.
2548    pub fn top_item(&self) -> usize {
2549        let state = self.0.borrow();
2550        let top = state.bounds.top() - state.offset.borrow().y;
2551
2552        match state.child_bounds.binary_search_by(|bounds| {
2553            if top < bounds.top() {
2554                Ordering::Greater
2555            } else if top > bounds.bottom() {
2556                Ordering::Less
2557            } else {
2558                Ordering::Equal
2559            }
2560        }) {
2561            Ok(ix) => ix,
2562            Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
2563        }
2564    }
2565
2566    /// Return the bounds into which this child is painted
2567    pub fn bounds(&self) -> Bounds<Pixels> {
2568        self.0.borrow().bounds
2569    }
2570
2571    /// Set the bounds into which this child is painted
2572    pub(super) fn set_bounds(&self, bounds: Bounds<Pixels>) {
2573        self.0.borrow_mut().bounds = bounds;
2574    }
2575
2576    /// Get the bounds for a specific child.
2577    pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
2578        self.0.borrow().child_bounds.get(ix).cloned()
2579    }
2580
2581    /// scroll_to_item scrolls the minimal amount to ensure that the child is
2582    /// fully visible
2583    pub fn scroll_to_item(&self, ix: usize) {
2584        let state = self.0.borrow();
2585
2586        let Some(bounds) = state.child_bounds.get(ix) else {
2587            return;
2588        };
2589
2590        let mut scroll_offset = state.offset.borrow_mut();
2591
2592        if state.overflow.y == Overflow::Scroll {
2593            if bounds.top() + scroll_offset.y < state.bounds.top() {
2594                scroll_offset.y = state.bounds.top() - bounds.top();
2595            } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
2596                scroll_offset.y = state.bounds.bottom() - bounds.bottom();
2597            }
2598        }
2599
2600        if state.overflow.x == Overflow::Scroll {
2601            if bounds.left() + scroll_offset.x < state.bounds.left() {
2602                scroll_offset.x = state.bounds.left() - bounds.left();
2603            } else if bounds.right() + scroll_offset.x > state.bounds.right() {
2604                scroll_offset.x = state.bounds.right() - bounds.right();
2605            }
2606        }
2607    }
2608
2609    /// Set the offset explicitly. The offset is the distance from the top left of the
2610    /// parent container to the top left of the first child.
2611    /// As you scroll further down the offset becomes more negative.
2612    pub fn set_offset(&self, mut position: Point<Pixels>) {
2613        let state = self.0.borrow();
2614        *state.offset.borrow_mut() = position;
2615    }
2616
2617    /// Get the logical scroll top, based on a child index and a pixel offset.
2618    pub fn logical_scroll_top(&self) -> (usize, Pixels) {
2619        let ix = self.top_item();
2620        let state = self.0.borrow();
2621
2622        if let Some(child_bounds) = state.child_bounds.get(ix) {
2623            (
2624                ix,
2625                child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
2626            )
2627        } else {
2628            (ix, px(0.))
2629        }
2630    }
2631
2632    /// Set the logical scroll top, based on a child index and a pixel offset.
2633    pub fn set_logical_scroll_top(&self, ix: usize, px: Pixels) {
2634        self.0.borrow_mut().requested_scroll_top = Some((ix, px));
2635    }
2636
2637    /// Get the count of children for scrollable item.
2638    pub fn children_count(&self) -> usize {
2639        self.0.borrow().child_bounds.len()
2640    }
2641}