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