Checkpoint

This commit is contained in:
Nathan Sobo 2023-08-25 23:15:58 -06:00
parent 8ad736da8d
commit 3bebfbcd50
10 changed files with 161 additions and 156 deletions

View file

@ -25,9 +25,13 @@ pub fn div<V>() -> Div<V> {
} }
impl<V: 'static> Element<V> for 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 where
Self: Sized, Self: Sized,
{ {
@ -47,14 +51,16 @@ impl<V: 'static> Element<V> for Div<V> {
cx.pop_text_style(); cx.pop_text_style();
} }
let layout = cx.add_layout_node(style, (), children.clone())?; Ok((cx.add_layout_node(style, children)?, ()))
dbg!(layout.id(), children);
Ok(layout)
} }
fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>) fn paint(
where &mut self,
view: &mut V,
layout: &Layout,
paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>,
) where
Self: Sized, Self: Sized,
{ {
let style = &self.computed_style(); 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)); cx.push_text_style(cx.text_style().clone().refined(&style));
true true
}); });
style.paint_background(layout.bounds(cx), cx); style.paint_background(layout.bounds, cx);
self.interaction_handlers() self.interaction_handlers()
.paint(layout.order(cx), layout.bounds(cx), cx); .paint(layout.order, layout.bounds, cx);
for child in &mut self.children { for child in &mut self.children {
child.paint(view, cx); child.paint(view, cx);
} }

View file

@ -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::layout_context::LayoutContext;
pub use crate::paint_context::PaintContext; pub use crate::paint_context::PaintContext;
use anyhow::Result;
type LayoutId = gpui::LayoutId; pub use gpui::{Layout, LayoutId};
use smallvec::SmallVec;
pub trait Element<V: 'static>: 'static { pub trait Element<V: 'static>: 'static {
type Layout; type PaintState;
fn layout( fn layout(
&mut self, &mut self,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut LayoutContext<V>,
) -> Result<Layout<V, Self::Layout>> ) -> Result<(LayoutId, Self::PaintState)>
where where
Self: Sized; Self: Sized;
fn paint( fn paint(
&mut self, &mut self,
view: &mut V, view: &mut V,
layout: &mut Layout<V, Self::Layout>, layout: &Layout,
state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut PaintContext<V>,
) where ) where
Self: Sized; Self: Sized;
@ -34,7 +30,7 @@ pub trait Element<V: 'static>: 'static {
{ {
AnyElement(Box::new(StatefulElement { AnyElement(Box::new(StatefulElement {
element: self, element: self,
layout: None, phase: ElementPhase::Init,
})) }))
} }
} }
@ -48,24 +44,71 @@ trait AnyStatefulElement<V> {
/// A wrapper around an element that stores its layout state. /// A wrapper around an element that stores its layout state.
struct StatefulElement<V: 'static, E: Element<V>> { struct StatefulElement<V: 'static, E: Element<V>> {
element: E, 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 /// 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> { impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> { fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
let layout = self.element.layout(view, cx)?; let result;
let layout_id = layout.id; self.phase = match std::mem::take(&mut self.phase) {
self.layout = Some(layout); ElementPhase::Init => match self.element.layout(view, cx) {
Ok(layout_id) 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>) { fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
let layout = self.layout.as_mut().expect("paint called before layout"); self.phase = match std::mem::take(&mut self.phase) {
if layout.engine_layout.is_none() { ElementPhase::PostLayout {
layout.engine_layout = dbg!(cx.computed_layout(dbg!(layout.id)).log_err()) 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,
} }
self.element.paint(view, layout, cx) }
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> { pub trait ParentElement<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>; fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;

View file

@ -6,7 +6,7 @@ use crate::{
style::{Style, StyleHelpers, Styleable}, style::{Style, StyleHelpers, Styleable},
}; };
use anyhow::Result; use anyhow::Result;
use gpui::platform::MouseMovedEvent; use gpui::{platform::MouseMovedEvent, LayoutId};
use refineable::{CascadeSlot, Refineable, RefinementCascade}; use refineable::{CascadeSlot, Refineable, RefinementCascade};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::Cell, rc::Rc}; 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> { 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 where
Self: Sized, Self: Sized,
{ {
self.child.layout(view, cx) Ok(self.child.layout(view, cx)?)
} }
fn paint( fn paint(
&mut self, &mut self,
view: &mut V, view: &mut V,
layout: &mut Layout<V, Self::Layout>, layout: &Layout,
paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut PaintContext<V>,
) where ) where
Self: Sized, Self: Sized,
{ {
let bounds = layout.bounds(cx); self.hovered
let order = layout.order(cx); .set(layout.bounds.contains_point(cx.mouse_position()));
self.hovered.set(bounds.contains_point(cx.mouse_position()));
let slot = self.cascade_slot; let slot = self.cascade_slot;
let style = self.hovered.get().then_some(self.hovered_style.clone()); let style = self.hovered.get().then_some(self.hovered_style.clone());
self.style_cascade().set(slot, style); self.style_cascade().set(slot, style);
let hovered = self.hovered.clone(); 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() { if bounds.contains_point(cx.mouse_position()) != hovered.get() {
cx.repaint(); cx.repaint();
} }
}); });
self.child.paint(view, layout, cx); self.child.paint(view, layout, paint_state, cx);
} }
} }

View file

@ -1,10 +1,9 @@
use crate::{element::LayoutId, style::Style};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use gpui::{geometry::Size, MeasureParams, RenderContext, ViewContext}; use gpui::{geometry::Size, MeasureParams, RenderContext, ViewContext};
pub use gpui::{taffy::tree::NodeId, LayoutContext as LegacyLayoutContext}; pub use gpui::{taffy::tree::NodeId, LayoutContext as LegacyLayoutContext};
use crate::{element::Layout, style::Style};
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
pub struct LayoutContext<'a, 'b, 'c, 'd, V> { pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
#[deref] #[deref]
@ -35,12 +34,11 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
Self { legacy_cx } Self { legacy_cx }
} }
pub fn add_layout_node<D>( pub fn add_layout_node(
&mut self, &mut self,
style: Style, style: Style,
element_data: D,
children: impl IntoIterator<Item = NodeId>, children: impl IntoIterator<Item = NodeId>,
) -> Result<Layout<V, D>> { ) -> Result<LayoutId> {
let rem_size = self.rem_pixels(); let rem_size = self.rem_pixels();
let id = self let id = self
.legacy_cx .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"))? .ok_or_else(|| anyhow!("no layout engine"))?
.add_node(style.to_taffy(rem_size), children)?; .add_node(style.to_taffy(rem_size), children)?;
Ok(Layout::new(id, element_data)) Ok(id)
} }
pub fn add_measured_layout_node<D, F>( pub fn add_measured_layout_node<F>(&mut self, style: Style, measure: F) -> Result<LayoutId>
&mut self,
style: Style,
element_data: D,
measure: F,
) -> Result<Layout<V, D>>
where where
F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static, 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"))? .ok_or_else(|| anyhow!("no layout engine"))?
.add_measured_node(style.to_taffy(rem_size), measure)?; .add_measured_node(style.to_taffy(rem_size), measure)?;
Ok(Layout::new(layout_id, element_data)) Ok(layout_id)
} }
} }

View file

@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
pub use gpui::taffy::tree::NodeId; pub use gpui::taffy::tree::NodeId;
use gpui::{ use gpui::{
scene::EventHandler, EngineLayout, EventContext, LayoutId, PaintContext as LegacyPaintContext, scene::EventHandler, EventContext, Layout, LayoutId, PaintContext as LegacyPaintContext,
RenderContext, ViewContext, RenderContext, ViewContext,
}; };
use std::{any::TypeId, rc::Rc}; 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() self.layout_engine()
.ok_or_else(|| anyhow!("no layout engine present"))? .ok_or_else(|| anyhow!("no layout engine present"))?
.computed_layout(layout_id) .computed_layout(layout_id)

View file

@ -6,7 +6,7 @@ use crate::{
style::{Style, StyleHelpers, Styleable}, style::{Style, StyleHelpers, Styleable},
}; };
use anyhow::Result; use anyhow::Result;
use gpui::platform::MouseButtonEvent; use gpui::{platform::MouseButtonEvent, LayoutId};
use refineable::{CascadeSlot, Refineable, RefinementCascade}; use refineable::{CascadeSlot, Refineable, RefinementCascade};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::Cell, rc::Rc}; 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> { 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 where
Self: Sized, Self: Sized,
{ {
@ -52,7 +56,8 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
fn paint( fn paint(
&mut self, &mut self,
view: &mut V, view: &mut V,
layout: &mut Layout<V, Self::Layout>, layout: &Layout,
paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut PaintContext<V>,
) where ) where
Self: Sized, 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()); let style = self.pressed.get().then_some(self.pressed_style.clone());
self.style_cascade().set(slot, style); self.style_cascade().set(slot, style);
let bounds = layout.bounds(cx);
let order = layout.order(cx);
let pressed = self.pressed.clone(); 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 event.is_down {
if bounds.contains_point(event.position) { if bounds.contains_point(event.position) {
pressed.set(true); 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);
} }
} }

View file

@ -4,7 +4,7 @@ use crate::{
paint_context::PaintContext, paint_context::PaintContext,
}; };
use anyhow::Result; 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 parking_lot::Mutex;
use std::sync::Arc; use std::sync::Arc;
@ -21,21 +21,23 @@ pub struct Text {
} }
impl<V: 'static> Element<V> for Text { impl<V: 'static> Element<V> for Text {
type Layout = Arc<Mutex<Option<TextLayout>>>; type PaintState = Arc<Mutex<Option<TextLayout>>>;
fn layout( fn layout(
&mut self, &mut self,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut LayoutContext<V>,
) -> Result<Layout<V, Self::Layout>> { ) -> Result<(LayoutId, Self::PaintState)> {
let rem_size = cx.rem_pixels(); let rem_size = cx.rem_pixels();
let fonts = cx.platform().fonts(); let fonts = cx.platform().fonts();
let text_style = cx.text_style(); let text_style = cx.text_style();
let line_height = cx.font_cache().line_height(text_style.font_size); let line_height = cx.font_cache().line_height(text_style.font_size);
let text = self.text.clone(); let text = self.text.clone();
let layout = Arc::new(Mutex::new(None)); let paint_state = Arc::new(Mutex::new(None));
cx.add_measured_layout_node(Default::default(), layout.clone(), move |params| { let layout_id = cx.add_measured_layout_node(Default::default(), {
let paint_state = paint_state.clone();
move |params| {
let line_layout = fonts.layout_line( let line_layout = fonts.layout_line(
text.as_ref(), text.as_ref(),
text_style.font_size, text_style.font_size,
@ -47,41 +49,43 @@ impl<V: 'static> Element<V> for Text {
height: line_height, height: line_height,
}; };
layout.lock().replace(TextLayout { paint_state.lock().replace(TextLayout {
line_layout: Arc::new(line_layout), line_layout: Arc::new(line_layout),
line_height, line_height,
}); });
size size
}) }
});
Ok((layout_id?, paint_state))
} }
fn paint<'a>( fn paint<'a>(
&mut self, &mut self,
view: &mut V, view: &mut V,
layout: &mut Layout<V, Self::Layout>, layout: &Layout,
paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut PaintContext<V>,
) { ) {
let element_layout = layout.update(|layout, element_data| element_data.clone());
let line_layout; let line_layout;
let line_height; let line_height;
{ {
let element_layout = element_layout.lock(); let paint_state = paint_state.lock();
let element_layout = element_layout let paint_state = paint_state
.as_ref() .as_ref()
.expect("measurement has not been performed"); .expect("measurement has not been performed");
line_layout = element_layout.line_layout.clone(); line_layout = paint_state.line_layout.clone();
line_height = element_layout.line_height; line_height = paint_state.line_height;
} }
let text_style = cx.text_style(); let text_style = cx.text_style();
let line = let line =
gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]); 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. // 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); line.paint(cx.scene, origin, visible_bounds, line_height, cx.legacy_cx);
} }
} }

View file

@ -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 impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
#where_clause #where_clause
{ {
type Layout = playground::element::AnyElement<#view_type_name #lifetimes>; type PaintState = playground::element::AnyElement<#view_type_name #lifetimes>;
fn layout( fn layout(
&mut self, &mut self,
view: &mut V, view: &mut V,
cx: &mut playground::element::LayoutContext<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 mut rendered_element = self.render(view, cx).into_any();
let layout_id = rendered_element.layout(view, cx)?; let layout_id = rendered_element.layout(view, cx)?;
Ok(playground::element::Layout::new(layout_id, rendered_element)) Ok((layout_id, rendered_element))
} }
fn paint( fn paint(
&mut self, &mut self,
view: &mut V, 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>, cx: &mut playground::element::PaintContext<V>,
) { ) {
layout.update(|_, rendered_element| rendered_element.paint(view, cx)); rendered_element.paint(view, cx);
} }
} }

View file

@ -1304,8 +1304,8 @@ impl LayoutEngine {
Ok(()) Ok(())
} }
pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> { pub fn computed_layout(&mut self, node: LayoutId) -> Result<Layout> {
Ok(EngineLayout::from(self.0.layout(node)?)) Ok(Layout::from(self.0.layout(node)?))
} }
} }
@ -1329,7 +1329,7 @@ where
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct EngineLayout { pub struct Layout {
pub bounds: RectF, pub bounds: RectF,
pub order: u32, 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 { fn from(value: &taffy::tree::Layout) -> Self {
Self { Self {
bounds: RectF::new( bounds: RectF::new(

View file

@ -7,8 +7,8 @@ pub use assets::*;
pub mod elements; pub mod elements;
pub mod font_cache; pub mod font_cache;
mod image_data; mod image_data;
pub use taffy;
pub use crate::image_data::ImageData; pub use crate::image_data::ImageData;
pub use taffy;
pub mod views; pub mod views;
pub use font_cache::FontCache; pub use font_cache::FontCache;
mod clipboard; mod clipboard;
@ -29,8 +29,7 @@ pub mod keymap_matcher;
pub mod platform; pub mod platform;
pub use gpui_macros::{test, Element}; pub use gpui_macros::{test, Element};
pub use window::{ pub use window::{
Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt, Axis, Layout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt, WindowContext,
WindowContext,
}; };
pub use anyhow; pub use anyhow;