771 lines · 25.7 KB · Rust
RawDownload
//! Elements are the workhorses of GPUI. They are responsible for laying out and painting all of//! the contents of a window. Elements form a tree and are laid out according to the web layout//! standards as implemented by [taffy](https://github.com/DioxusLabs/taffy). Most of the time,//! you won't need to interact with this module or these APIs directly. Elements provide their//! own APIs and GPUI, or other element implementation, uses the APIs in this module to convert//! that element tree into the pixels you see on the screen.//!//! # Element Basics//!//! Elements are constructed by calling [`Render::render()`] on the root view of the window,//! which recursively constructs the element tree from the current state of the application,.//! These elements are then laid out by Taffy, and painted to the screen according to their own//! implementation of [`Element::paint()`]. Before the start of the next frame, the entire element//! tree and any callbacks they have registered with GPUI are dropped and the process repeats.//!//! But some state is too simple and voluminous to store in every view that needs it, e.g.//! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], associated type.//!//! # Implementing your own elements//!//! Elements are intended to be the low level, imperative API to GPUI. They are responsible for upholding,//! or breaking, GPUI's features as they deem necessary. As an example, most GPUI elements are expected//! to stay in the bounds that their parent element gives them. But with [`WindowContext::break_content_mask`],//! you can ignore this restriction and paint anywhere inside of the window's bounds. This is useful for overlays//! and popups and anything else that shows up 'on top' of other elements.//! With great power, comes great responsibility.//!//! However, most of the time, you won't need to implement your own elements. GPUI provides a number of//! elements that should cover most common use cases out of the box and it's recommended that you use those//! to construct `components`, using the [`RenderOnce`] trait and the `#[derive(IntoElement)]` macro. Only implement//! elements when you need to take manual control of the layout and painting process, such as when using//! your own custom layout algorithm or rendering a code editor. use crate::{    App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ELEMENT_ARENA, ElementId,    FocusHandle, InspectorElementId, LayoutId, Pixels, Point, Size, Style, Window,    util::FluentBuilder,};use derive_more::{Deref, DerefMut};pub(crate) use smallvec::SmallVec;use std::{    any::{Any, type_name},    fmt::{self, Debug, Display},    mem, panic,}; /// Implemented by types that participate in laying out and painting the contents of a window./// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy./// You can create custom elements by implementing this trait, see the module-level documentation/// for more details.pub trait Element: 'static + IntoElement {    /// The type of state returned from [`Element::request_layout`]. A mutable reference to this state is subsequently    /// provided to [`Element::prepaint`] and [`Element::paint`].    type RequestLayoutState: 'static;     /// The type of state returned from [`Element::prepaint`]. A mutable reference to this state is subsequently    /// provided to [`Element::paint`].    type PrepaintState: 'static;     /// If this element has a unique identifier, return it here. This is used to track elements across frames, and    /// will cause a GlobalElementId to be passed to the request_layout, prepaint, and paint methods.    ///    /// The global id can in turn be used to access state that's connected to an element with the same id across    /// frames. This id must be unique among children of the first containing element with an id.    fn id(&self) -> Option<ElementId>;     /// Source location where this element was constructed, used to disambiguate elements in the    /// inspector and navigate to their source code.    fn source_location(&self) -> Option<&'static panic::Location<'static>>;     /// Before an element can be painted, we need to know where it's going to be and how big it is.    /// Use this method to request a layout from Taffy and initialize the element's state.    fn request_layout(        &mut self,        id: Option<&GlobalElementId>,        inspector_id: Option<&InspectorElementId>,        window: &mut Window,        cx: &mut App,    ) -> (LayoutId, Self::RequestLayoutState);     /// After laying out an element, we need to commit its bounds to the current frame for hitbox    /// purposes. The state argument is the same state that was returned from [`Element::request_layout()`].    fn prepaint(        &mut self,        id: Option<&GlobalElementId>,        inspector_id: Option<&InspectorElementId>,        bounds: Bounds<Pixels>,        request_layout: &mut Self::RequestLayoutState,        window: &mut Window,        cx: &mut App,    ) -> Self::PrepaintState;     /// Once layout has been completed, this method will be called to paint the element to the screen.    /// The state argument is the same state that was returned from [`Element::request_layout()`].    fn paint(        &mut self,        id: Option<&GlobalElementId>,        inspector_id: Option<&InspectorElementId>,        bounds: Bounds<Pixels>,        request_layout: &mut Self::RequestLayoutState,        prepaint: &mut Self::PrepaintState,        window: &mut Window,        cx: &mut App,    );     /// Convert this element into a dynamically-typed [`AnyElement`].    fn into_any(self) -> AnyElement {        AnyElement::new(self)    }} /// Implemented by any type that can be converted into an element.pub trait IntoElement: Sized {    /// The specific type of element into which the implementing type is converted.    /// Useful for converting other types into elements automatically, like Strings    type Element: Element;     /// Convert self into a type that implements [`Element`].    fn into_element(self) -> Self::Element;     /// Convert self into a dynamically-typed [`AnyElement`].    fn into_any_element(self) -> AnyElement {        self.into_element().into_any()    }} impl<T: IntoElement> FluentBuilder for T {} /// An object that can be drawn to the screen. This is the trait that distinguishes "views" from/// other entities. Views are `Entity`'s which `impl Render` and drawn to the screen.pub trait Render: 'static + Sized {    /// Render this view into an element tree.    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement;} impl Render for Empty {    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {        Empty    }} /// You can derive [`IntoElement`] on any type that implements this trait./// It is used to construct reusable `components` out of plain data. Think of/// components as a recipe for a certain pattern of elements. RenderOnce allows/// you to invoke this pattern, without breaking the fluent builder pattern of/// the element APIs.pub trait RenderOnce: 'static {    /// Render this component into an element tree. Note that this method    /// takes ownership of self, as compared to [`Render::render()`] method    /// which takes a mutable reference.    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement;} /// This is a helper trait to provide a uniform interface for constructing elements that/// can accept any number of any kind of child elementspub trait ParentElement {    /// Extend this element's children with the given child elements.    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>);     /// Add a single child element to this element.    fn child(mut self, child: impl IntoElement) -> Self    where        Self: Sized,    {        self.extend(std::iter::once(child.into_element().into_any()));        self    }     /// Add multiple child elements to this element.    fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self    where        Self: Sized,    {        self.extend(children.into_iter().map(|child| child.into_any_element()));        self    }} /// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro/// for [`RenderOnce`]#[doc(hidden)]pub struct Component<C: RenderOnce> {    component: Option<C>,    #[cfg(debug_assertions)]    source: &'static core::panic::Location<'static>,} impl<C: RenderOnce> Component<C> {    /// Create a new component from the given RenderOnce type.    #[track_caller]    pub fn new(component: C) -> Self {        Component {            component: Some(component),            #[cfg(debug_assertions)]            source: core::panic::Location::caller(),        }    }} impl<C: RenderOnce> Element for Component<C> {    type RequestLayoutState = AnyElement;    type PrepaintState = ();     fn id(&self) -> Option<ElementId> {        None    }     fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {        #[cfg(debug_assertions)]        return Some(self.source);         #[cfg(not(debug_assertions))]        return None;    }     fn request_layout(        &mut self,        _id: Option<&GlobalElementId>,        _inspector_id: Option<&InspectorElementId>,        window: &mut Window,        cx: &mut App,    ) -> (LayoutId, Self::RequestLayoutState) {        window.with_global_id(ElementId::Name(type_name::<C>().into()), |_, window| {            let mut element = self                .component                .take()                .unwrap()                .render(window, cx)                .into_any_element();             let layout_id = element.request_layout(window, cx);            (layout_id, element)        })    }     fn prepaint(        &mut self,        _id: Option<&GlobalElementId>,        _inspector_id: Option<&InspectorElementId>,        _: Bounds<Pixels>,        element: &mut AnyElement,        window: &mut Window,        cx: &mut App,    ) {        window.with_global_id(ElementId::Name(type_name::<C>().into()), |_, window| {            element.prepaint(window, cx);        })    }     fn paint(        &mut self,        _id: Option<&GlobalElementId>,        _inspector_id: Option<&InspectorElementId>,        _: Bounds<Pixels>,        element: &mut Self::RequestLayoutState,        _: &mut Self::PrepaintState,        window: &mut Window,        cx: &mut App,    ) {        window.with_global_id(ElementId::Name(type_name::<C>().into()), |_, window| {            element.paint(window, cx);        })    }} impl<C: RenderOnce> IntoElement for Component<C> {    type Element = Self;     fn into_element(self) -> Self::Element {        self    }} /// A globally unique identifier for an element, used to track state across frames.#[derive(Deref, DerefMut, Default, Debug, Eq, PartialEq, Hash)]pub struct GlobalElementId(pub(crate) SmallVec<[ElementId; 32]>); impl Display for GlobalElementId {    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {        for (i, element_id) in self.0.iter().enumerate() {            if i > 0 {                write!(f, ".")?;            }            write!(f, "{}", element_id)?;        }        Ok(())    }} trait ElementObject {    fn inner_element(&mut self) -> &mut dyn Any;     fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId;     fn prepaint(&mut self, window: &mut Window, cx: &mut App);     fn paint(&mut self, window: &mut Window, cx: &mut App);     fn layout_as_root(        &mut self,        available_space: Size<AvailableSpace>,        window: &mut Window,        cx: &mut App,    ) -> Size<Pixels>;} /// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.pub struct Drawable<E: Element> {    /// The drawn element.    pub element: E,    phase: ElementDrawPhase<E::RequestLayoutState, E::PrepaintState>,} #[derive(Default)]enum ElementDrawPhase<RequestLayoutState, PrepaintState> {    #[default]    Start,    RequestLayout {        layout_id: LayoutId,        global_id: Option<GlobalElementId>,        inspector_id: Option<InspectorElementId>,        request_layout: RequestLayoutState,    },    LayoutComputed {        layout_id: LayoutId,        global_id: Option<GlobalElementId>,        inspector_id: Option<InspectorElementId>,        available_space: Size<AvailableSpace>,        request_layout: RequestLayoutState,    },    Prepaint {        node_id: DispatchNodeId,        global_id: Option<GlobalElementId>,        inspector_id: Option<InspectorElementId>,        bounds: Bounds<Pixels>,        request_layout: RequestLayoutState,        prepaint: PrepaintState,    },    Painted,} /// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.impl<E: Element> Drawable<E> {    pub(crate) fn new(element: E) -> Self {        Drawable {            element,            phase: ElementDrawPhase::Start,        }    }     fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {        match mem::take(&mut self.phase) {            ElementDrawPhase::Start => {                let global_id = self.element.id().map(|element_id| {                    window.element_id_stack.push(element_id);                    GlobalElementId(window.element_id_stack.clone())                });                 let inspector_id;                #[cfg(any(feature = "inspector", debug_assertions))]                {                    inspector_id = self.element.source_location().map(|source| {                        let path = crate::InspectorElementPath {                            global_id: GlobalElementId(window.element_id_stack.clone()),                            source_location: source,                        };                        window.build_inspector_element_id(path)                    });                }                #[cfg(not(any(feature = "inspector", debug_assertions)))]                {                    inspector_id = None;                }                 let (layout_id, request_layout) = self.element.request_layout(                    global_id.as_ref(),                    inspector_id.as_ref(),                    window,                    cx,                );                 if global_id.is_some() {                    window.element_id_stack.pop();                }                 self.phase = ElementDrawPhase::RequestLayout {                    layout_id,                    global_id,                    inspector_id,                    request_layout,                };                layout_id            }            _ => panic!("must call request_layout only once"),        }    }     pub(crate) fn prepaint(&mut self, window: &mut Window, cx: &mut App) {        match mem::take(&mut self.phase) {            ElementDrawPhase::RequestLayout {                layout_id,                global_id,                inspector_id,                mut request_layout,            }            | ElementDrawPhase::LayoutComputed {                layout_id,                global_id,                inspector_id,                mut request_layout,                ..            } => {                if let Some(element_id) = self.element.id() {                    window.element_id_stack.push(element_id);                    debug_assert_eq!(global_id.as_ref().unwrap().0, window.element_id_stack);                }                 let bounds = window.layout_bounds(layout_id);                let node_id = window.next_frame.dispatch_tree.push_node();                let prepaint = self.element.prepaint(                    global_id.as_ref(),                    inspector_id.as_ref(),                    bounds,                    &mut request_layout,                    window,                    cx,                );                window.next_frame.dispatch_tree.pop_node();                 if global_id.is_some() {                    window.element_id_stack.pop();                }                 self.phase = ElementDrawPhase::Prepaint {                    node_id,                    global_id,                    inspector_id,                    bounds,                    request_layout,                    prepaint,                };            }            _ => panic!("must call request_layout before prepaint"),        }    }     pub(crate) fn paint(        &mut self,        window: &mut Window,        cx: &mut App,    ) -> (E::RequestLayoutState, E::PrepaintState) {        match mem::take(&mut self.phase) {            ElementDrawPhase::Prepaint {                node_id,                global_id,                inspector_id,                bounds,                mut request_layout,                mut prepaint,                ..            } => {                if let Some(element_id) = self.element.id() {                    window.element_id_stack.push(element_id);                    debug_assert_eq!(global_id.as_ref().unwrap().0, window.element_id_stack);                }                 window.next_frame.dispatch_tree.set_active_node(node_id);                self.element.paint(                    global_id.as_ref(),                    inspector_id.as_ref(),                    bounds,                    &mut request_layout,                    &mut prepaint,                    window,                    cx,                );                 if global_id.is_some() {                    window.element_id_stack.pop();                }                 self.phase = ElementDrawPhase::Painted;                (request_layout, prepaint)            }            _ => panic!("must call prepaint before paint"),        }    }     pub(crate) fn layout_as_root(        &mut self,        available_space: Size<AvailableSpace>,        window: &mut Window,        cx: &mut App,    ) -> Size<Pixels> {        if matches!(&self.phase, ElementDrawPhase::Start) {            self.request_layout(window, cx);        }         let layout_id = match mem::take(&mut self.phase) {            ElementDrawPhase::RequestLayout {

Lines 1–500 of 771.