Merge ElementContext into WindowContext (#10979)

The new `ElementContext` was originally introduced to ensure the element
APIs could only be used inside of elements. Unfortunately, there were
many places where some of those APIs needed to be used, so
`WindowContext::with_element_context` was introduced, which defeated the
original safety purposes of having a specific context for elements.

This pull request merges `ElementContext` into `WindowContext` and adds
(debug) runtime checks to APIs that can only be used during certain
phases of element drawing.

Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2024-04-25 12:54:39 +02:00 committed by GitHub
parent 031580f4dc
commit 6a7761e620
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 2378 additions and 2367 deletions

View file

@ -32,12 +32,12 @@
//! your own custom layout algorithm or rendering a code editor.
use crate::{
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, LayoutId,
Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
use std::{any::Any, fmt::Debug, mem};
/// 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.
@ -54,7 +54,7 @@ pub trait Element: 'static + IntoElement {
/// 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, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState);
fn request_layout(&mut self, cx: &mut WindowContext) -> (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()`].
@ -62,7 +62,7 @@ pub trait Element: 'static + IntoElement {
&mut self,
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
cx: &mut ElementContext,
cx: &mut WindowContext,
) -> Self::PrepaintState;
/// Once layout has been completed, this method will be called to paint the element to the screen.
@ -72,7 +72,7 @@ pub trait Element: 'static + IntoElement {
bounds: Bounds<Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
cx: &mut ElementContext,
cx: &mut WindowContext,
);
/// Convert this element into a dynamically-typed [`AnyElement`].
@ -164,18 +164,13 @@ impl<C: RenderOnce> Element for Component<C> {
type RequestLayoutState = AnyElement;
type PrepaintState = ();
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
let mut element = self
.0
.take()
.unwrap()
.render(cx.deref_mut())
.into_any_element();
fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let mut element = self.0.take().unwrap().render(cx).into_any_element();
let layout_id = element.request_layout(cx);
(layout_id, element)
}
fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut ElementContext) {
fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut WindowContext) {
element.prepaint(cx);
}
@ -184,7 +179,7 @@ impl<C: RenderOnce> Element for Component<C> {
_: Bounds<Pixels>,
element: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
cx: &mut ElementContext,
cx: &mut WindowContext,
) {
element.paint(cx)
}
@ -205,16 +200,16 @@ pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
trait ElementObject {
fn inner_element(&mut self) -> &mut dyn Any;
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
fn prepaint(&mut self, cx: &mut ElementContext);
fn prepaint(&mut self, cx: &mut WindowContext);
fn paint(&mut self, cx: &mut ElementContext);
fn paint(&mut self, cx: &mut WindowContext);
fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
cx: &mut WindowContext,
) -> Size<Pixels>;
}
@ -249,14 +244,14 @@ enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
impl<E: Element> Drawable<E> {
fn new(element: E) -> Self {
pub(crate) fn new(element: E) -> Self {
Drawable {
element,
phase: ElementDrawPhase::Start,
}
}
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
match mem::take(&mut self.phase) {
ElementDrawPhase::Start => {
let (layout_id, request_layout) = self.element.request_layout(cx);
@ -270,7 +265,7 @@ impl<E: Element> Drawable<E> {
}
}
fn prepaint(&mut self, cx: &mut ElementContext) {
pub(crate) fn prepaint(&mut self, cx: &mut WindowContext) {
match mem::take(&mut self.phase) {
ElementDrawPhase::RequestLayoutState {
layout_id,
@ -296,7 +291,10 @@ impl<E: Element> Drawable<E> {
}
}
fn paint(&mut self, cx: &mut ElementContext) -> E::RequestLayoutState {
pub(crate) fn paint(
&mut self,
cx: &mut WindowContext,
) -> (E::RequestLayoutState, E::PrepaintState) {
match mem::take(&mut self.phase) {
ElementDrawPhase::PrepaintState {
node_id,
@ -309,16 +307,16 @@ impl<E: Element> Drawable<E> {
self.element
.paint(bounds, &mut request_layout, &mut prepaint, cx);
self.phase = ElementDrawPhase::Painted;
request_layout
(request_layout, prepaint)
}
_ => panic!("must call prepaint before paint"),
}
}
fn layout_as_root(
pub(crate) fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
cx: &mut WindowContext,
) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) {
self.request_layout(cx);
@ -368,22 +366,22 @@ where
&mut self.element
}
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
Drawable::request_layout(self, cx)
}
fn prepaint(&mut self, cx: &mut ElementContext) {
fn prepaint(&mut self, cx: &mut WindowContext) {
Drawable::prepaint(self, cx);
}
fn paint(&mut self, cx: &mut ElementContext) {
fn paint(&mut self, cx: &mut WindowContext) {
Drawable::paint(self, cx);
}
fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
cx: &mut WindowContext,
) -> Size<Pixels> {
Drawable::layout_as_root(self, available_space, cx)
}
@ -411,18 +409,18 @@ impl AnyElement {
/// 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 {
pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
self.0.request_layout(cx)
}
/// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
/// request autoscroll before the final paint pass is confirmed.
pub fn prepaint(&mut self, cx: &mut ElementContext) {
pub fn prepaint(&mut self, cx: &mut WindowContext) {
self.0.prepaint(cx)
}
/// Paints the element stored in this `AnyElement`.
pub fn paint(&mut self, cx: &mut ElementContext) {
pub fn paint(&mut self, cx: &mut WindowContext) {
self.0.paint(cx)
}
@ -430,13 +428,13 @@ impl AnyElement {
pub fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
cx: &mut WindowContext,
) -> Size<Pixels> {
self.0.layout_as_root(available_space, cx)
}
/// Prepaints this element at the given absolute origin.
pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut ElementContext) {
pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut WindowContext) {
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
}
@ -445,7 +443,7 @@ impl AnyElement {
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
cx: &mut WindowContext,
) {
self.layout_as_root(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
@ -456,7 +454,7 @@ impl Element for AnyElement {
type RequestLayoutState = ();
type PrepaintState = ();
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
let layout_id = self.request_layout(cx);
(layout_id, ())
}
@ -465,7 +463,7 @@ impl Element for AnyElement {
&mut self,
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
cx: &mut ElementContext,
cx: &mut WindowContext,
) {
self.prepaint(cx)
}
@ -475,7 +473,7 @@ impl Element for AnyElement {
_: Bounds<Pixels>,
_: &mut Self::RequestLayoutState,
_: &mut Self::PrepaintState,
cx: &mut ElementContext,
cx: &mut WindowContext,
) {
self.paint(cx)
}
@ -508,15 +506,15 @@ impl Element for Empty {
type RequestLayoutState = ();
type PrepaintState = ();
fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
(cx.request_layout(&crate::Style::default(), None), ())
fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
(cx.request_layout(&Style::default(), None), ())
}
fn prepaint(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::RequestLayoutState,
_cx: &mut ElementContext,
_cx: &mut WindowContext,
) {
}
@ -525,7 +523,7 @@ impl Element for Empty {
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
_cx: &mut ElementContext,
_cx: &mut WindowContext,
) {
}
}