3545 lines · 139.4 KB · Rust
RawDownload
//! Div is the central, reusable element that most GPUI trees will be built from.//! It functions as a container for other elements, and provides a number of//! useful features for laying out and styling its children as well as binding//! mouse events and action handlers. It is meant to be similar to the HTML `<div>`//! element, but for GPUI.//!//! # Build your own div//!//! GPUI does not directly provide APIs for stateful, multi step events like `click`//! and `drag`. We want GPUI users to be able to build their own abstractions for//! their own needs. However, as a UI framework, we're also obliged to provide some//! building blocks to make the process of building your own elements easier.//! For this we have the [`Interactivity`] and the [`StyleRefinement`] structs, as well//! as several associated traits. Together, these provide the full suite of Dom-like events//! and Tailwind-like styling that you can use to build your own custom elements. Div is//! constructed by combining these two systems into an all-in-one element. use crate::{    AbsoluteLength, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent,    DispatchPhase, Display, Element, ElementId, Entity, FocusHandle, Global, GlobalElementId,    Hitbox, HitboxBehavior, HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext,    KeyDownEvent, KeyUpEvent, KeyboardButton, KeyboardClickEvent, LayoutId, ModifiersChangedEvent,    MouseButton, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent,    Overflow, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,    StyleRefinement, Styled, Task, TooltipId, Visibility, Window, WindowControlArea, point, px,    size,};use collections::HashMap;use refineable::Refineable;use smallvec::SmallVec;use stacksafe::{StackSafe, stacksafe};use std::{    any::{Any, TypeId},    cell::RefCell,    cmp::Ordering,    fmt::Debug,    marker::PhantomData,    mem,    rc::Rc,    sync::Arc,    time::Duration,};use util::ResultExt; use super::ImageCacheProvider; const DRAG_THRESHOLD: f64 = 2.;const TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500); /// The styling information for a given group.pub struct GroupStyle {    /// The identifier for this group.    pub group: SharedString,     /// The specific style refinement that this group would apply    /// to its children.    pub style: Box<StyleRefinement>,} /// An event for when a drag is moving over this element, with the given state type.pub struct DragMoveEvent<T> {    /// The mouse move event that triggered this drag move event.    pub event: MouseMoveEvent,     /// The bounds of this element.    pub bounds: Bounds<Pixels>,    drag: PhantomData<T>,    dragged_item: Arc<dyn Any>,} impl<T: 'static> DragMoveEvent<T> {    /// Returns the drag state for this event.    pub fn drag<'b>(&self, cx: &'b App) -> &'b T {        cx.active_drag            .as_ref()            .and_then(|drag| drag.value.downcast_ref::<T>())            .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")    }     /// An item that is about to be dropped.    pub fn dragged_item(&self) -> &dyn Any {        self.dragged_item.as_ref()    }} impl Interactivity {    /// Create an `Interactivity`, capturing the caller location in debug mode.    #[cfg(any(feature = "inspector", debug_assertions))]    #[track_caller]    pub fn new() -> Interactivity {        Interactivity {            source_location: Some(core::panic::Location::caller()),            ..Default::default()        }    }     /// Create an `Interactivity`, capturing the caller location in debug mode.    #[cfg(not(any(feature = "inspector", debug_assertions)))]    pub fn new() -> Interactivity {        Interactivity::default()    }     /// Gets the source location of construction. Returns `None` when not in debug mode.    pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {        #[cfg(any(feature = "inspector", debug_assertions))]        {            self.source_location        }         #[cfg(not(any(feature = "inspector", debug_assertions)))]        {            None        }    }     /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase.    /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback.    pub fn on_mouse_down(        &mut self,        button: MouseButton,        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_down_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Bubble                    && event.button == button                    && hitbox.is_hovered(window)                {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse down event for any button, during the capture phase.    /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn capture_any_mouse_down(        &mut self,        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_down_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse down event for any button, during the bubble phase.    /// The imperative API equivalent to [`InteractiveElement::on_any_mouse_down`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_any_mouse_down(        &mut self,        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_down_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse pressure event, during the bubble phase    /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_mouse_pressure(        &mut self,        listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_pressure_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse pressure event, during the capture phase    /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn capture_mouse_pressure(        &mut self,        listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_pressure_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse up event for the given button, during the bubble phase.    /// The imperative API equivalent to [`InteractiveElement::on_mouse_up`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_mouse_up(        &mut self,        button: MouseButton,        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_up_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Bubble                    && event.button == button                    && hitbox.is_hovered(window)                {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse up event for any button, during the capture phase.    /// The imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn capture_any_mouse_up(        &mut self,        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_up_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse up event for any button, during the bubble phase.    /// The imperative API equivalent to [`Interactivity::on_any_mouse_up`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_any_mouse_up(        &mut self,        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_up_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse down event, on any button, during the capture phase,    /// when the mouse is outside of the bounds of this element.    /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_mouse_down_out(        &mut self,        listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_down_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Capture && !hitbox.contains(&window.mouse_position()) {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to the mouse up event, for the given button, during the capture phase,    /// when the mouse is outside of the bounds of this element.    /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_mouse_up_out(        &mut self,        button: MouseButton,        listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_up_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Capture                    && event.button == button                    && !hitbox.is_hovered(window)                {                    (listener)(event, window, cx);                }            }));    }     /// Bind the given callback to the mouse move event, during the bubble phase.    /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_mouse_move(        &mut self,        listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,    ) {        self.mouse_move_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {                    (listener)(event, window, cx);                }            }));    }     /// Bind the given callback to the mouse drag event of the given type. Note that this    /// will be called for all move events, inside or outside of this element, as long as the    /// drag was started with this element under the mouse. Useful for implementing draggable    /// UIs that don't conform to a drag and drop style interaction, like resizing.    /// The imperative API equivalent to [`InteractiveElement::on_drag_move`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_drag_move<T>(        &mut self,        listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,    ) where        T: 'static,    {        self.mouse_move_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Capture                    && let Some(drag) = &cx.active_drag                    && drag.value.as_ref().type_id() == TypeId::of::<T>()                {                    (listener)(                        &DragMoveEvent {                            event: event.clone(),                            bounds: hitbox.bounds,                            drag: PhantomData,                            dragged_item: Arc::clone(&drag.value),                        },                        window,                        cx,                    );                }            }));    }     /// Bind the given callback to scroll wheel events during the bubble phase.    /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_scroll_wheel(        &mut self,        listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,    ) {        self.scroll_wheel_listeners            .push(Box::new(move |event, phase, hitbox, window, cx| {                if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {                    (listener)(event, window, cx);                }            }));    }     /// Bind the given callback to an action dispatch during the capture phase.    /// The imperative API equivalent to [`InteractiveElement::capture_action`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn capture_action<A: Action>(        &mut self,        listener: impl Fn(&A, &mut Window, &mut App) + 'static,    ) {        self.action_listeners.push((            TypeId::of::<A>(),            Box::new(move |action, phase, window, cx| {                let action = action.downcast_ref().unwrap();                if phase == DispatchPhase::Capture {                    (listener)(action, window, cx)                } else {                    cx.propagate();                }            }),        ));    }     /// Bind the given callback to an action dispatch during the bubble phase.    /// The imperative API equivalent to [`InteractiveElement::on_action`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {        self.action_listeners.push((            TypeId::of::<A>(),            Box::new(move |action, phase, window, cx| {                let action = action.downcast_ref().unwrap();                if phase == DispatchPhase::Bubble {                    (listener)(action, window, cx)                }            }),        ));    }     /// Bind the given callback to an action dispatch, based on a dynamic action parameter    /// instead of a type parameter. Useful for component libraries that want to expose    /// action bindings to their users.    /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_boxed_action(        &mut self,        action: &dyn Action,        listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,    ) {        let action = action.boxed_clone();        self.action_listeners.push((            (*action).type_id(),            Box::new(move |_, phase, window, cx| {                if phase == DispatchPhase::Bubble {                    (listener)(&*action, window, cx)                }            }),        ));    }     /// Bind the given callback to key down events during the bubble phase.    /// The imperative API equivalent to [`InteractiveElement::on_key_down`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_key_down(        &mut self,        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,    ) {        self.key_down_listeners            .push(Box::new(move |event, phase, window, cx| {                if phase == DispatchPhase::Bubble {                    (listener)(event, window, cx)                }            }));    }     /// Bind the given callback to key down events during the capture phase.    /// The imperative API equivalent to [`InteractiveElement::capture_key_down`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn capture_key_down(        &mut self,        listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,    ) {        self.key_down_listeners            .push(Box::new(move |event, phase, window, cx| {                if phase == DispatchPhase::Capture {                    listener(event, window, cx)                }            }));    }     /// Bind the given callback to key up events during the bubble phase.    /// The imperative API equivalent to [`InteractiveElement::on_key_up`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {        self.key_up_listeners            .push(Box::new(move |event, phase, window, cx| {                if phase == DispatchPhase::Bubble {                    listener(event, window, cx)                }            }));    }     /// Bind the given callback to key up events during the capture phase.    /// The imperative API equivalent to [`InteractiveElement::on_key_up`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn capture_key_up(        &mut self,        listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,    ) {        self.key_up_listeners            .push(Box::new(move |event, phase, window, cx| {                if phase == DispatchPhase::Capture {                    listener(event, window, cx)                }            }));    }     /// Bind the given callback to modifiers changing events.    /// The imperative API equivalent to [`InteractiveElement::on_modifiers_changed`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_modifiers_changed(        &mut self,        listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,    ) {        self.modifiers_changed_listeners            .push(Box::new(move |event, window, cx| {                listener(event, window, cx)            }));    }     /// Bind the given callback to drop events of the given type, whether or not the drag started on this element.    /// The imperative API equivalent to [`InteractiveElement::on_drop`].    ///    /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.    pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {        self.drop_listeners.push((            TypeId::of::<T>(),            Box::new(move |dragged_value, window, cx| {                listener(dragged_value.downcast_ref().unwrap(), window, cx);            }),        ));

Lines 1–500 of 3545.