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