Detailed changes
@@ -25,9 +25,13 @@ pub fn div<V>() -> Div<V> {
}
impl<V: 'static> Element<V> for Div<V> {
- type Layout = ();
+ type PaintState = ();
- fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, ()>>
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(LayoutId, Self::PaintState)>
where
Self: Sized,
{
@@ -47,14 +51,16 @@ impl<V: 'static> Element<V> for Div<V> {
cx.pop_text_style();
}
- let layout = cx.add_layout_node(style, (), children.clone())?;
-
- dbg!(layout.id(), children);
- Ok(layout)
+ Ok((cx.add_layout_node(style, children)?, ()))
}
- fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
- where
+ fn paint(
+ &mut self,
+ view: &mut V,
+ layout: &Layout,
+ paint_state: &mut Self::PaintState,
+ cx: &mut PaintContext<V>,
+ ) where
Self: Sized,
{
let style = &self.computed_style();
@@ -62,9 +68,9 @@ impl<V: 'static> Element<V> for Div<V> {
cx.push_text_style(cx.text_style().clone().refined(&style));
true
});
- style.paint_background(layout.bounds(cx), cx);
+ style.paint_background(layout.bounds, cx);
self.interaction_handlers()
- .paint(layout.order(cx), layout.bounds(cx), cx);
+ .paint(layout.order, layout.bounds, cx);
for child in &mut self.children {
child.paint(view, cx);
}
@@ -1,29 +1,25 @@
-use anyhow::Result;
-use gpui::{geometry::rect::RectF, EngineLayout};
-use smallvec::SmallVec;
-use std::marker::PhantomData;
-use util::ResultExt;
-
pub use crate::layout_context::LayoutContext;
pub use crate::paint_context::PaintContext;
-
-type LayoutId = gpui::LayoutId;
+use anyhow::Result;
+pub use gpui::{Layout, LayoutId};
+use smallvec::SmallVec;
pub trait Element<V: 'static>: 'static {
- type Layout;
+ type PaintState;
fn layout(
&mut self,
view: &mut V,
cx: &mut LayoutContext<V>,
- ) -> Result<Layout<V, Self::Layout>>
+ ) -> Result<(LayoutId, Self::PaintState)>
where
Self: Sized;
fn paint(
&mut self,
view: &mut V,
- layout: &mut Layout<V, Self::Layout>,
+ layout: &Layout,
+ state: &mut Self::PaintState,
cx: &mut PaintContext<V>,
) where
Self: Sized;
@@ -34,7 +30,7 @@ pub trait Element<V: 'static>: 'static {
{
AnyElement(Box::new(StatefulElement {
element: self,
- layout: None,
+ phase: ElementPhase::Init,
}))
}
}
@@ -48,24 +44,71 @@ trait AnyStatefulElement<V> {
/// A wrapper around an element that stores its layout state.
struct StatefulElement<V: 'static, E: Element<V>> {
element: E,
- layout: Option<Layout<V, E::Layout>>,
+ phase: ElementPhase<V, E>,
+}
+
+enum ElementPhase<V: 'static, E: Element<V>> {
+ Init,
+ PostLayout {
+ layout_id: LayoutId,
+ paint_state: E::PaintState,
+ },
+ PostPaint {
+ layout: Layout,
+ paint_state: E::PaintState,
+ },
+ Error(String),
+}
+
+impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
+ fn default() -> Self {
+ Self::Init
+ }
}
/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
- let layout = self.element.layout(view, cx)?;
- let layout_id = layout.id;
- self.layout = Some(layout);
- Ok(layout_id)
+ let result;
+ self.phase = match std::mem::take(&mut self.phase) {
+ ElementPhase::Init => match self.element.layout(view, cx) {
+ Ok((layout_id, paint_state)) => {
+ result = Ok(layout_id);
+ ElementPhase::PostLayout {
+ layout_id,
+ paint_state,
+ }
+ }
+ Err(error) => {
+ let message = error.to_string();
+ result = Err(error);
+ ElementPhase::Error(message)
+ }
+ },
+ _ => panic!("invalid element phase to call layout"),
+ };
+
+ result
}
fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
- let layout = self.layout.as_mut().expect("paint called before layout");
- if layout.engine_layout.is_none() {
- layout.engine_layout = dbg!(cx.computed_layout(dbg!(layout.id)).log_err())
- }
- self.element.paint(view, layout, cx)
+ self.phase = match std::mem::take(&mut self.phase) {
+ ElementPhase::PostLayout {
+ layout_id,
+ mut paint_state,
+ } => match cx.computed_layout(layout_id) {
+ Ok(layout) => {
+ self.element.paint(view, &layout, &mut paint_state, cx);
+ ElementPhase::PostPaint {
+ layout,
+ paint_state,
+ }
+ }
+ Err(error) => ElementPhase::Error(error.to_string()),
+ },
+ phase @ ElementPhase::Error(_) => phase,
+ _ => panic!("invalid element phase to call paint"),
+ };
}
}
@@ -82,55 +125,6 @@ impl<V> AnyElement<V> {
}
}
-pub struct Layout<V, D> {
- id: LayoutId,
- engine_layout: Option<EngineLayout>,
- element_data: Option<D>,
- view_type: PhantomData<V>,
-}
-
-impl<V: 'static, D> Layout<V, D> {
- pub fn new(id: LayoutId, element_data: D) -> Self {
- Self {
- id,
- engine_layout: None,
- element_data: Some(element_data),
- view_type: PhantomData,
- }
- }
-
- pub fn id(&self) -> LayoutId {
- self.id
- }
-
- pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
- self.engine_layout(cx).bounds
- }
-
- pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
- self.engine_layout(cx).order
- }
-
- pub fn update<F, T>(&mut self, update: F) -> T
- where
- F: FnOnce(&mut Self, &mut D) -> T,
- {
- self.element_data
- .take()
- .map(|mut element_data| {
- let result = update(self, &mut element_data);
- self.element_data = Some(element_data);
- result
- })
- .expect("reentrant calls to Layout::update are not supported")
- }
-
- fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
- self.engine_layout
- .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
- }
-}
-
pub trait ParentElement<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
@@ -6,7 +6,7 @@ use crate::{
style::{Style, StyleHelpers, Styleable},
};
use anyhow::Result;
-use gpui::platform::MouseMovedEvent;
+use gpui::{platform::MouseMovedEvent, LayoutId};
use refineable::{CascadeSlot, Refineable, RefinementCascade};
use smallvec::SmallVec;
use std::{cell::Cell, rc::Rc};
@@ -40,40 +40,44 @@ impl<E: Styleable> Styleable for Hoverable<E> {
}
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
- type Layout = E::Layout;
+ type PaintState = E::PaintState;
- fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(LayoutId, Self::PaintState)>
where
Self: Sized,
{
- self.child.layout(view, cx)
+ Ok(self.child.layout(view, cx)?)
}
fn paint(
&mut self,
view: &mut V,
- layout: &mut Layout<V, Self::Layout>,
+ layout: &Layout,
+ paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>,
) where
Self: Sized,
{
- let bounds = layout.bounds(cx);
- let order = layout.order(cx);
-
- self.hovered.set(bounds.contains_point(cx.mouse_position()));
+ self.hovered
+ .set(layout.bounds.contains_point(cx.mouse_position()));
let slot = self.cascade_slot;
let style = self.hovered.get().then_some(self.hovered_style.clone());
self.style_cascade().set(slot, style);
let hovered = self.hovered.clone();
- cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
+ let bounds = layout.bounds;
+ cx.on_event(layout.order, move |view, event: &MouseMovedEvent, cx| {
if bounds.contains_point(cx.mouse_position()) != hovered.get() {
cx.repaint();
}
});
- self.child.paint(view, layout, cx);
+ self.child.paint(view, layout, paint_state, cx);
}
}
@@ -1,10 +1,9 @@
+use crate::{element::LayoutId, style::Style};
use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut};
use gpui::{geometry::Size, MeasureParams, RenderContext, ViewContext};
pub use gpui::{taffy::tree::NodeId, LayoutContext as LegacyLayoutContext};
-use crate::{element::Layout, style::Style};
-
#[derive(Deref, DerefMut)]
pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
#[deref]
@@ -35,12 +34,11 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
Self { legacy_cx }
}
- pub fn add_layout_node<D>(
+ pub fn add_layout_node(
&mut self,
style: Style,
- element_data: D,
children: impl IntoIterator<Item = NodeId>,
- ) -> Result<Layout<V, D>> {
+ ) -> Result<LayoutId> {
let rem_size = self.rem_pixels();
let id = self
.legacy_cx
@@ -48,15 +46,10 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
.ok_or_else(|| anyhow!("no layout engine"))?
.add_node(style.to_taffy(rem_size), children)?;
- Ok(Layout::new(id, element_data))
+ Ok(id)
}
- pub fn add_measured_layout_node<D, F>(
- &mut self,
- style: Style,
- element_data: D,
- measure: F,
- ) -> Result<Layout<V, D>>
+ pub fn add_measured_layout_node<F>(&mut self, style: Style, measure: F) -> Result<LayoutId>
where
F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
{
@@ -66,6 +59,6 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
.ok_or_else(|| anyhow!("no layout engine"))?
.add_measured_node(style.to_taffy(rem_size), measure)?;
- Ok(Layout::new(layout_id, element_data))
+ Ok(layout_id)
}
}
@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut};
pub use gpui::taffy::tree::NodeId;
use gpui::{
- scene::EventHandler, EngineLayout, EventContext, LayoutId, PaintContext as LegacyPaintContext,
+ scene::EventHandler, EventContext, Layout, LayoutId, PaintContext as LegacyPaintContext,
RenderContext, ViewContext,
};
use std::{any::TypeId, rc::Rc};
@@ -65,7 +65,7 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
})
}
- pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<EngineLayout> {
+ pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
self.layout_engine()
.ok_or_else(|| anyhow!("no layout engine present"))?
.computed_layout(layout_id)
@@ -6,7 +6,7 @@ use crate::{
style::{Style, StyleHelpers, Styleable},
};
use anyhow::Result;
-use gpui::platform::MouseButtonEvent;
+use gpui::{platform::MouseButtonEvent, LayoutId};
use refineable::{CascadeSlot, Refineable, RefinementCascade};
use smallvec::SmallVec;
use std::{cell::Cell, rc::Rc};
@@ -40,9 +40,13 @@ impl<E: Styleable> Styleable for Pressable<E> {
}
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
- type Layout = E::Layout;
+ type PaintState = E::PaintState;
- fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+ fn layout(
+ &mut self,
+ view: &mut V,
+ cx: &mut LayoutContext<V>,
+ ) -> Result<(LayoutId, Self::PaintState)>
where
Self: Sized,
{
@@ -52,7 +56,8 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
fn paint(
&mut self,
view: &mut V,
- layout: &mut Layout<V, Self::Layout>,
+ layout: &Layout,
+ paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>,
) where
Self: Sized,
@@ -61,10 +66,9 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
let style = self.pressed.get().then_some(self.pressed_style.clone());
self.style_cascade().set(slot, style);
- let bounds = layout.bounds(cx);
- let order = layout.order(cx);
let pressed = self.pressed.clone();
- cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
+ let bounds = layout.bounds;
+ cx.on_event(layout.order, move |view, event: &MouseButtonEvent, cx| {
if event.is_down {
if bounds.contains_point(event.position) {
pressed.set(true);
@@ -76,7 +80,7 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
}
});
- self.child.paint(view, layout, cx);
+ self.child.paint(view, layout, paint_state, cx);
}
}
@@ -4,7 +4,7 @@ use crate::{
paint_context::PaintContext,
};
use anyhow::Result;
-use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
+use gpui::{geometry::Size, text_layout::LineLayout, LayoutId, RenderContext};
use parking_lot::Mutex;
use std::sync::Arc;
@@ -21,67 +21,71 @@ pub struct Text {
}
impl<V: 'static> Element<V> for Text {
- type Layout = Arc<Mutex<Option<TextLayout>>>;
+ type PaintState = Arc<Mutex<Option<TextLayout>>>;
fn layout(
&mut self,
view: &mut V,
cx: &mut LayoutContext<V>,
- ) -> Result<Layout<V, Self::Layout>> {
+ ) -> Result<(LayoutId, Self::PaintState)> {
let rem_size = cx.rem_pixels();
let fonts = cx.platform().fonts();
let text_style = cx.text_style();
let line_height = cx.font_cache().line_height(text_style.font_size);
let text = self.text.clone();
- let layout = Arc::new(Mutex::new(None));
-
- cx.add_measured_layout_node(Default::default(), layout.clone(), move |params| {
- let line_layout = fonts.layout_line(
- text.as_ref(),
- text_style.font_size,
- &[(text.len(), text_style.to_run())],
- );
-
- let size = Size {
- width: line_layout.width,
- height: line_height,
- };
-
- layout.lock().replace(TextLayout {
- line_layout: Arc::new(line_layout),
- line_height,
- });
-
- size
- })
+ let paint_state = Arc::new(Mutex::new(None));
+
+ let layout_id = cx.add_measured_layout_node(Default::default(), {
+ let paint_state = paint_state.clone();
+ move |params| {
+ let line_layout = fonts.layout_line(
+ text.as_ref(),
+ text_style.font_size,
+ &[(text.len(), text_style.to_run())],
+ );
+
+ let size = Size {
+ width: line_layout.width,
+ height: line_height,
+ };
+
+ paint_state.lock().replace(TextLayout {
+ line_layout: Arc::new(line_layout),
+ line_height,
+ });
+
+ size
+ }
+ });
+
+ Ok((layout_id?, paint_state))
}
fn paint<'a>(
&mut self,
view: &mut V,
- layout: &mut Layout<V, Self::Layout>,
+ layout: &Layout,
+ paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>,
) {
- let element_layout = layout.update(|layout, element_data| element_data.clone());
-
let line_layout;
let line_height;
{
- let element_layout = element_layout.lock();
- let element_layout = element_layout
+ let paint_state = paint_state.lock();
+ let paint_state = paint_state
.as_ref()
.expect("measurement has not been performed");
- line_layout = element_layout.line_layout.clone();
- line_height = element_layout.line_height;
+ line_layout = paint_state.line_layout.clone();
+ line_height = paint_state.line_height;
}
let text_style = cx.text_style();
let line =
gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
- let origin = layout.bounds(cx).origin();
+ let origin = layout.bounds.origin();
// TODO: We haven't added visible bounds to the new element system yet, so this is a placeholder.
- let visible_bounds = layout.bounds(cx);
+ let visible_bounds = layout.bounds;
line.paint(cx.scene, origin, visible_bounds, line_height, cx.legacy_cx);
}
}
@@ -62,25 +62,26 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
#where_clause
{
- type Layout = playground::element::AnyElement<#view_type_name #lifetimes>;
+ type PaintState = playground::element::AnyElement<#view_type_name #lifetimes>;
fn layout(
&mut self,
view: &mut V,
cx: &mut playground::element::LayoutContext<V>,
- ) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
+ ) -> anyhow::Result<(playground::element::LayoutId, Self::PaintState)> {
let mut rendered_element = self.render(view, cx).into_any();
let layout_id = rendered_element.layout(view, cx)?;
- Ok(playground::element::Layout::new(layout_id, rendered_element))
+ Ok((layout_id, rendered_element))
}
fn paint(
&mut self,
view: &mut V,
- layout: &mut playground::element::Layout<V, Self::Layout>,
+ layout: &playground::element::Layout,
+ rendered_element: &mut Self::PaintState,
cx: &mut playground::element::PaintContext<V>,
) {
- layout.update(|_, rendered_element| rendered_element.paint(view, cx));
+ rendered_element.paint(view, cx);
}
}
@@ -1304,8 +1304,8 @@ impl LayoutEngine {
Ok(())
}
- pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
- Ok(EngineLayout::from(self.0.layout(node)?))
+ pub fn computed_layout(&mut self, node: LayoutId) -> Result<Layout> {
+ Ok(Layout::from(self.0.layout(node)?))
}
}
@@ -1329,7 +1329,7 @@ where
}
#[derive(Debug, Clone, Default)]
-pub struct EngineLayout {
+pub struct Layout {
pub bounds: RectF,
pub order: u32,
}
@@ -1365,7 +1365,7 @@ impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
}
}
-impl From<&taffy::tree::Layout> for EngineLayout {
+impl From<&taffy::tree::Layout> for Layout {
fn from(value: &taffy::tree::Layout) -> Self {
Self {
bounds: RectF::new(
@@ -7,8 +7,8 @@ pub use assets::*;
pub mod elements;
pub mod font_cache;
mod image_data;
-pub use taffy;
pub use crate::image_data::ImageData;
+pub use taffy;
pub mod views;
pub use font_cache::FontCache;
mod clipboard;
@@ -29,8 +29,7 @@ pub mod keymap_matcher;
pub mod platform;
pub use gpui_macros::{test, Element};
pub use window::{
- Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt,
- WindowContext,
+ Axis, Layout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt, WindowContext,
};
pub use anyhow;