zed/crates/gpui/src/element.rs

550 lines
18 KiB
Rust
Raw Normal View History

//! 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
2024-01-21 22:26:45 +00:00
//! 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.
2024-01-21 22:26:45 +00:00
//! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], associated type.
//! If an element returns an [`ElementId`] from [`IntoElement::element_id()`], and that element id
//! appears in the same place relative to other views and ElementIds in the frame, then the previous
//! frame's state will be passed to the element's layout and paint methods.
//!
//! # Implementing your own elements
//!
//! Elements are intended to be the low level, imperative API to GPUI. They are responsible for upholding,
2024-01-21 04:07:25 +00:00
//! 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
2024-01-21 04:19:01 +00:00
//! elements that should cover most common use cases out of the box and it's recommended that you use those
2024-01-21 22:26:45 +00:00
//! to construct `components`, using the [`RenderOnce`] trait and the `#[derive(IntoElement)]` macro. Only implement
2024-01-21 04:19:01 +00:00
//! 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::{
2024-01-22 03:43:35 +00:00
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId,
Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
};
2023-10-10 18:41:28 +00:00
use derive_more::{Deref, DerefMut};
2023-09-21 19:46:31 +00:00
pub(crate) use smallvec::SmallVec;
2024-01-22 03:43:35 +00:00
use std::{any::Any, fmt::Debug, ops::DerefMut};
2023-09-19 19:19:22 +00:00
2024-01-02 18:30:58 +00:00
/// 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.
2023-12-31 15:33:40 +00:00
pub trait Element: 'static + IntoElement {
/// The type of state to store for this element between frames. See the module-level documentation
/// for details.
2023-12-31 15:33:40 +00:00
type State: 'static;
2023-11-19 03:05:47 +00:00
/// 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.
2024-01-01 19:23:42 +00:00
fn request_layout(
2023-12-31 15:33:40 +00:00
&mut self,
state: Option<Self::State>,
cx: &mut ElementContext,
2023-12-31 15:33:40 +00:00
) -> (LayoutId, Self::State);
/// 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, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext);
2023-12-31 15:33:40 +00:00
/// Convert this element into a dynamically-typed [`AnyElement`].
2023-12-31 15:33:40 +00:00
fn into_any(self) -> AnyElement {
AnyElement::new(self)
}
2023-11-19 03:05:47 +00:00
}
2023-12-31 15:33:40 +00:00
/// Implemented by any type that can be converted into an element.
2023-11-22 18:19:43 +00:00
pub trait IntoElement: Sized {
2023-12-31 15:33:40 +00:00
/// The specific type of element into which the implementing type is converted.
/// Useful for converting other types into elements automatically, like Strings
2023-12-31 15:33:40 +00:00
type Element: Element;
2023-09-19 19:19:22 +00:00
/// The [`ElementId`] of self once converted into an [`Element`].
2023-12-31 15:33:40 +00:00
/// If present, the resulting element's state will be carried across frames.
2023-11-14 08:15:48 +00:00
fn element_id(&self) -> Option<ElementId>;
2023-10-10 18:41:28 +00:00
/// Convert self into a type that implements [`Element`].
2023-11-22 18:19:43 +00:00
fn into_element(self) -> Self::Element;
2023-09-19 19:19:22 +00:00
/// Convert self into a dynamically-typed [`AnyElement`].
2023-11-22 18:19:43 +00:00
fn into_any_element(self) -> AnyElement {
self.into_element().into_any()
2023-11-19 03:05:47 +00:00
}
2023-12-31 15:33:40 +00:00
/// Convert into an element, then draw in the current window at the given origin.
/// The available space argument is provided to the layout engine to determine the size of the
// root element. Once the element is drawn, its associated element state is yielded to the
// given callback.
2023-12-15 00:20:27 +00:00
fn draw_and_update_state<T, R>(
self,
origin: Point<Pixels>,
available_space: Size<T>,
cx: &mut ElementContext,
f: impl FnOnce(&mut <Self::Element as Element>::State, &mut ElementContext) -> R,
) -> R
where
T: Clone + Default + Debug + Into<AvailableSpace>,
{
2023-11-22 18:19:43 +00:00
let element = self.into_element();
let element_id = element.element_id();
let element = DrawableElement {
element: Some(element),
phase: ElementDrawPhase::Start,
};
let frame_state =
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
if let Some(mut frame_state) = frame_state {
f(&mut frame_state, cx)
} else {
cx.with_element_state(element_id.unwrap(), |element_state, cx| {
let mut element_state = element_state.unwrap();
let result = f(&mut element_state, cx);
(result, element_state)
})
}
}
2023-11-19 03:05:47 +00:00
}
impl<T: IntoElement> FluentBuilder for T {}
/// An object that can be drawn to the screen. This is the trait that distinguishes `Views` from
/// models. Views are drawn to the screen and care about the current window's state, models are not and do not.
2023-12-31 15:33:40 +00:00
pub trait Render: 'static + Sized {
/// Render this view into an element tree.
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
2023-12-31 15:33:40 +00:00
}
2023-09-19 19:19:22 +00:00
impl Render for () {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {}
}
/// 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.
2023-12-31 15:33:40 +00:00
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, cx: &mut WindowContext) -> impl IntoElement;
2023-09-19 19:19:22 +00:00
}
/// This is a helper trait to provide a uniform interface for constructing elements that
/// can accept any number of any kind of child elements
2023-12-31 15:33:40 +00:00
pub trait ParentElement {
/// Extend this element's children with the given child elements.
fn extend(&mut self, elements: impl Iterator<Item = AnyElement>);
2023-12-31 15:33:40 +00:00
/// Add a single child element to this element.
2023-12-31 15:33:40 +00:00
fn child(mut self, child: impl IntoElement) -> Self
where
Self: Sized,
{
self.extend(std::iter::once(child.into_element().into_any()));
2023-12-31 15:33:40 +00:00
self
}
2023-11-19 03:05:47 +00:00
/// Add multiple child elements to this element.
2023-12-31 15:33:40 +00:00
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()));
2023-12-31 15:33:40 +00:00
self
}
2023-11-19 03:05:47 +00:00
}
/// An element for rendering components. An implementation detail of the [`IntoElement`] derive macro
/// for [`RenderOnce`]
#[doc(hidden)]
pub struct Component<C: RenderOnce>(Option<C>);
2023-11-19 03:05:47 +00:00
impl<C: RenderOnce> Component<C> {
/// Create a new component from the given RenderOnce type.
2023-11-19 03:05:47 +00:00
pub fn new(component: C) -> Self {
Component(Some(component))
}
2023-09-19 19:19:22 +00:00
}
2023-11-22 18:19:43 +00:00
impl<C: RenderOnce> Element for Component<C> {
type State = AnyElement;
2023-11-19 03:05:47 +00:00
2023-12-31 15:33:40 +00:00
fn request_layout(
2023-11-19 03:05:47 +00:00
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
2023-11-19 03:05:47 +00:00
) -> (LayoutId, Self::State) {
let mut element = self
.0
.take()
.unwrap()
.render(cx.deref_mut())
.into_any_element();
let layout_id = element.request_layout(cx);
(layout_id, element)
2023-11-19 03:05:47 +00:00
}
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
element.paint(cx)
2023-11-19 03:05:47 +00:00
}
}
2023-11-22 18:19:43 +00:00
impl<C: RenderOnce> IntoElement for Component<C> {
2023-11-19 03:05:47 +00:00
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
2023-11-22 18:19:43 +00:00
fn into_element(self) -> Self::Element {
2023-11-19 03:05:47 +00:00
self
}
}
/// A globally unique identifier for an element, used to track state across frames.
2023-10-11 04:14:47 +00:00
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
2023-10-10 18:41:28 +00:00
2023-11-20 02:32:31 +00:00
trait ElementObject {
fn element_id(&self) -> Option<ElementId>;
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
fn paint(&mut self, cx: &mut ElementContext);
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels>;
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
);
2023-09-19 19:19:22 +00:00
}
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
pub(crate) struct DrawableElement<E: Element> {
element: Option<E>,
phase: ElementDrawPhase<E::State>,
2023-09-19 19:19:22 +00:00
}
#[derive(Default)]
enum ElementDrawPhase<S> {
2023-09-19 19:19:22 +00:00
#[default]
2023-10-18 12:12:50 +00:00
Start,
2023-09-19 19:19:22 +00:00
LayoutRequested {
layout_id: LayoutId,
frame_state: Option<S>,
2023-09-19 19:19:22 +00:00
},
LayoutComputed {
layout_id: LayoutId,
available_space: Size<AvailableSpace>,
frame_state: Option<S>,
},
2023-09-19 19:19:22 +00:00
}
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
impl<E: Element> DrawableElement<E> {
2023-09-19 19:19:22 +00:00
fn new(element: E) -> Self {
DrawableElement {
element: Some(element),
phase: ElementDrawPhase::Start,
2023-09-19 19:19:22 +00:00
}
}
2023-10-11 04:14:47 +00:00
fn element_id(&self) -> Option<ElementId> {
self.element.as_ref()?.element_id()
}
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
{
let layout_id = cx.with_element_state(id, |element_state, cx| {
2023-12-31 15:33:40 +00:00
self.element
.as_mut()
.unwrap()
.request_layout(element_state, cx)
});
(layout_id, None)
} else {
2023-12-31 15:33:40 +00:00
let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
(layout_id, Some(frame_state))
2023-10-11 04:14:47 +00:00
};
self.phase = ElementDrawPhase::LayoutRequested {
2023-09-19 19:19:22 +00:00
layout_id,
frame_state,
};
2023-10-11 04:14:47 +00:00
layout_id
2023-09-19 19:19:22 +00:00
}
fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
match self.phase {
ElementDrawPhase::LayoutRequested {
2023-09-19 19:19:22 +00:00
layout_id,
frame_state,
}
| ElementDrawPhase::LayoutComputed {
layout_id,
frame_state,
..
2023-09-19 19:19:22 +00:00
} => {
2023-10-20 15:31:42 +00:00
let bounds = cx.layout_bounds(layout_id);
if let Some(mut frame_state) = frame_state {
self.element
.take()
.unwrap()
.paint(bounds, &mut frame_state, cx);
Some(frame_state)
} else {
let element_id = self
.element
.as_ref()
.unwrap()
.element_id()
.expect("if we don't have frame state, we should have element state");
cx.with_element_state(element_id, |element_state, cx| {
2023-10-18 12:12:50 +00:00
let mut element_state = element_state.unwrap();
self.element
.take()
.unwrap()
.paint(bounds, &mut element_state, cx);
2023-10-18 12:12:50 +00:00
((), element_state)
});
None
2023-09-19 19:19:22 +00:00
}
}
2023-10-18 12:12:50 +00:00
_ => panic!("must call layout before paint"),
}
2023-09-19 19:19:22 +00:00
}
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) {
2024-01-01 19:23:42 +00:00
self.request_layout(cx);
}
let layout_id = match &mut self.phase {
ElementDrawPhase::LayoutRequested {
layout_id,
frame_state,
} => {
cx.compute_layout(*layout_id, available_space);
let layout_id = *layout_id;
self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
available_space,
frame_state: frame_state.take(),
};
layout_id
}
ElementDrawPhase::LayoutComputed {
layout_id,
available_space: prev_available_space,
..
} => {
if available_space != *prev_available_space {
cx.compute_layout(*layout_id, available_space);
*prev_available_space = available_space;
}
*layout_id
}
_ => panic!("cannot measure after painting"),
};
cx.layout_bounds(layout_id).size
}
fn draw(
mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Option<E::State> {
2023-11-20 02:32:31 +00:00
self.measure(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
}
2023-09-19 19:19:22 +00:00
}
impl<E> ElementObject for Option<DrawableElement<E>>
where
E: Element,
E::State: 'static,
{
fn element_id(&self) -> Option<ElementId> {
self.as_ref().unwrap().element_id()
}
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
2024-01-01 19:23:42 +00:00
DrawableElement::request_layout(self.as_mut().unwrap(), cx)
}
fn paint(&mut self, cx: &mut ElementContext) {
DrawableElement::paint(self.take().unwrap(), cx);
}
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
}
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) {
DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
}
}
/// A dynamically typed element that can be used to store any element type.
pub struct AnyElement(ArenaBox<dyn ElementObject>);
2023-11-20 02:32:31 +00:00
impl AnyElement {
pub(crate) fn new<E>(element: E) -> Self
where
2023-11-20 02:32:31 +00:00
E: 'static + Element,
E::State: Any,
{
let element = ELEMENT_ARENA
.with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element))))
.map(|element| element as &mut dyn ElementObject);
AnyElement(element)
2023-10-12 17:30:00 +00:00
}
/// Request the layout ID of the element stored in this `AnyElement`.
/// Used for laying out child elements in a parent element.
pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
self.0.request_layout(cx)
2023-10-18 12:12:50 +00:00
}
/// Paints the element stored in this `AnyElement`.
pub fn paint(&mut self, cx: &mut ElementContext) {
2023-12-15 17:30:32 +00:00
self.0.paint(cx)
2023-09-19 19:19:22 +00:00
}
/// Initializes this element and performs layout within the given available space to determine its size.
pub fn measure(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
2023-12-15 17:30:32 +00:00
self.0.measure(available_space, cx)
}
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
pub fn draw(
2023-12-14 22:15:18 +00:00
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) {
2023-12-15 17:30:32 +00:00
self.0.draw(origin, available_space, cx)
2023-09-19 19:19:22 +00:00
}
2023-11-22 21:41:48 +00:00
/// Returns the element ID of the element stored in this `AnyElement`, if any.
2023-11-22 21:41:48 +00:00
pub fn inner_id(&self) -> Option<ElementId> {
2023-12-15 17:30:32 +00:00
self.0.element_id()
2023-11-22 21:41:48 +00:00
}
2023-09-19 19:19:22 +00:00
}
impl Element for AnyElement {
type State = ();
2023-12-31 15:33:40 +00:00
fn request_layout(
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let layout_id = self.request_layout(cx);
(layout_id, ())
}
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) {
2023-12-14 22:15:18 +00:00
self.paint(cx)
}
}
2023-11-22 18:19:43 +00:00
impl IntoElement for AnyElement {
2023-11-19 03:05:47 +00:00
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
2023-11-22 18:19:43 +00:00
fn into_element(self) -> Self::Element {
2023-09-19 19:19:22 +00:00
self
}
fn into_any_element(self) -> AnyElement {
self
}
}
2023-12-13 21:40:19 +00:00
/// The empty element, which renders nothing.
pub type Empty = ();
impl IntoElement for () {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element {
self
}
}
impl Element for () {
type State = ();
2023-12-31 15:33:40 +00:00
fn request_layout(
2023-12-13 21:40:19 +00:00
&mut self,
_state: Option<Self::State>,
cx: &mut ElementContext,
2023-12-13 21:40:19 +00:00
) -> (LayoutId, Self::State) {
(cx.request_layout(&crate::Style::default(), None), ())
}
2023-12-14 22:15:18 +00:00
fn paint(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::State,
_cx: &mut ElementContext,
2023-12-14 22:15:18 +00:00
) {
}
2023-12-13 21:40:19 +00:00
}