Detailed changes
@@ -1,131 +1,101 @@
-// use crate::{
-// element::{Element, ElementMetadata, ParentElement},
-// frame,
-// text::ArcCow,
-// themes::rose_pine,
-// };
-// use gpui::{platform::MouseButton, ViewContext};
-// use playground_macros::Element;
-// use std::{marker::PhantomData, rc::Rc};
-
-// struct ButtonHandlers<V, D> {
-// click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
-// }
-
-// impl<V, D> Default for ButtonHandlers<V, D> {
-// fn default() -> Self {
-// Self { click: None }
-// }
-// }
-
-// #[derive(Element)]
-// #[element_crate = "crate"]
-// pub struct Button<V: 'static, D: 'static> {
-// metadata: ElementMetadata<V>,
-// handlers: ButtonHandlers<V, D>,
-// label: Option<ArcCow<'static, str>>,
-// icon: Option<ArcCow<'static, str>>,
-// data: Rc<D>,
-// view_type: PhantomData<V>,
-// }
-
-// // Impl block for buttons without data.
-// // See below for an impl block for any button.
-// impl<V: 'static> Button<V, ()> {
-// fn new() -> Self {
-// Self {
-// metadata: Default::default(),
-// handlers: ButtonHandlers::default(),
-// label: None,
-// icon: None,
-// data: Rc::new(()),
-// view_type: PhantomData,
-// }
-// }
-
-// pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
-// Button {
-// metadata: Default::default(),
-// handlers: ButtonHandlers::default(),
-// label: self.label,
-// icon: self.icon,
-// data: Rc::new(data),
-// view_type: PhantomData,
-// }
-// }
-// }
-
-// // Impl block for *any* button.
-// impl<V: 'static, D: 'static> Button<V, D> {
-// pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
-// self.label = Some(label.into());
-// self
-// }
-
-// pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
-// self.icon = Some(icon.into());
-// self
-// }
-
-// pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
-// let data = self.data.clone();
-// Element::click(self, MouseButton::Left, move |view, _, cx| {
-// handler(view, data.as_ref(), cx);
-// })
-// }
-// }
-
-// pub fn button<V>() -> Button<V, ()> {
-// Button::new()
-// }
-
-// impl<V: 'static, D: 'static> Button<V, D> {
-// fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
-// // TODO: Drive theme from the context
-// let button = frame()
-// .fill(rose_pine::dawn().error(0.5))
-// .h_4()
-// .children(self.label.clone());
-
-// if let Some(handler) = self.handlers.click.clone() {
-// let data = self.data.clone();
-// button.mouse_down(MouseButton::Left, move |view, event, cx| {
-// handler(view, data.as_ref(), cx)
-// })
-// } else {
-// button
-// }
-// }
-// }
-
-// // impl<V: 'static, D> Element<V> for Button<V, D> {
-// // type Layout = AnyElement<V>;
-
-// // fn style_mut(&mut self) -> &mut crate::style::ElementStyle {
-// // &mut self.metadata.style
-// // }
-
-// // fn handlers_mut(&mut self) -> &mut crate::element::ElementHandlers<V> {
-// // &mut self.metadata.handlers
-// // }
-
-// // fn layout(
-// // &mut self,
-// // view: &mut V,
-// // cx: &mut crate::element::LayoutContext<V>,
-// // ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
-// // let mut element = self.render(view, cx).into_any();
-// // let node_id = element.layout(view, cx)?;
-// // Ok((node_id, element))
-// // }
-
-// // fn paint<'a>(
-// // &mut self,
-// // layout: crate::element::Layout<'a, Self::Layout>,
-// // view: &mut V,
-// // cx: &mut crate::element::PaintContext<V>,
-// // ) -> anyhow::Result<()> {
-// // layout.from_element.paint(view, cx)?;
-// // Ok(())
-// // }
-// // }
+use crate::{
+ div::div,
+ element::{Element, ParentElement},
+ style::StyleHelpers,
+ text::ArcCow,
+ themes::rose_pine,
+};
+use gpui::ViewContext;
+use playground_macros::Element;
+
+use std::{marker::PhantomData, rc::Rc};
+
+struct ButtonHandlers<V, D> {
+ click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
+}
+
+impl<V, D> Default for ButtonHandlers<V, D> {
+ fn default() -> Self {
+ Self { click: None }
+ }
+}
+
+use crate as playground;
+#[derive(Element)]
+pub struct Button<V: 'static, D: 'static> {
+ handlers: ButtonHandlers<V, D>,
+ label: Option<ArcCow<'static, str>>,
+ icon: Option<ArcCow<'static, str>>,
+ data: Rc<D>,
+ view_type: PhantomData<V>,
+}
+
+// Impl block for buttons without data.
+// See below for an impl block for any button.
+impl<V: 'static> Button<V, ()> {
+ fn new() -> Self {
+ Self {
+ handlers: ButtonHandlers::default(),
+ label: None,
+ icon: None,
+ data: Rc::new(()),
+ view_type: PhantomData,
+ }
+ }
+
+ pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
+ Button {
+ handlers: ButtonHandlers::default(),
+ label: self.label,
+ icon: self.icon,
+ data: Rc::new(data),
+ view_type: PhantomData,
+ }
+ }
+}
+
+// Impl block for *any* button.
+impl<V: 'static, D: 'static> Button<V, D> {
+ pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
+ self.label = Some(label.into());
+ self
+ }
+
+ pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
+ self.icon = Some(icon.into());
+ self
+ }
+
+ // pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
+ // let data = self.data.clone();
+ // Self::click(self, MouseButton::Left, move |view, _, cx| {
+ // handler(view, data.as_ref(), cx);
+ // })
+ // }
+}
+
+pub fn button<V>() -> Button<V, ()> {
+ Button::new()
+}
+
+impl<V: 'static, D: 'static> Button<V, D> {
+ fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ // TODO: Drive theme from the context
+ let button = div()
+ .fill(rose_pine::dawn().error(0.5))
+ .h_4()
+ .children(self.label.clone());
+
+ button
+
+ // TODO: Event handling
+ // if let Some(handler) = self.handlers.click.clone() {
+ // let data = self.data.clone();
+ // // button.mouse_down(MouseButton::Left, move |view, event, cx| {
+ // // handler(view, data.as_ref(), cx)
+ // // })
+ // } else {
+ // button
+ // }
+ }
+}
@@ -1,8 +1,8 @@
use crate::{
- element::{AnyElement, Element, Layout},
+ element::{AnyElement, Element, Layout, ParentElement},
layout_context::LayoutContext,
paint_context::PaintContext,
- style::{Style, StyleRefinement, Styleable},
+ style::{Style, StyleHelpers, StyleRefinement, Styleable},
};
use anyhow::Result;
use gpui::{platform::MouseMovedEvent, EventContext, LayoutId};
@@ -14,14 +14,6 @@ pub struct Div<V> {
children: SmallVec<[AnyElement<V>; 2]>,
}
-impl<V> Styleable for Div<V> {
- type Style = Style;
-
- fn declared_style(&mut self) -> &mut StyleRefinement {
- &mut self.style
- }
-}
-
pub fn div<V>() -> Div<V> {
Div {
style: Default::default(),
@@ -55,6 +47,22 @@ impl<V: 'static> Element<V> for Div<V> {
}
}
+impl<V> Styleable for Div<V> {
+ type Style = Style;
+
+ fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
+ &mut self.style
+ }
+}
+
+impl<V> StyleHelpers for Div<V> {}
+
+impl<V: 'static> ParentElement<V> for Div<V> {
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+ &mut self.children
+ }
+}
+
pub trait Interactive<V> {
fn declared_interactions(&mut self) -> &mut Interactions<V>;
@@ -1,12 +1,12 @@
use anyhow::Result;
use derive_more::{Deref, DerefMut};
-use gpui::geometry::rect::RectF;
-use gpui::EngineLayout;
+use gpui::{geometry::rect::RectF, EngineLayout};
+use smallvec::SmallVec;
use std::marker::PhantomData;
use util::ResultExt;
-use crate::layout_context::LayoutContext;
-use crate::paint_context::PaintContext;
+pub use crate::layout_context::LayoutContext;
+pub use crate::paint_context::PaintContext;
type LayoutId = gpui::LayoutId;
@@ -21,11 +21,11 @@ pub struct Layout<V, D> {
}
impl<V: 'static, D> Layout<V, D> {
- pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: D) -> Self {
+ pub fn new(id: LayoutId, element_data: D) -> Self {
Self {
id,
- engine_layout,
- element_data,
+ engine_layout: None,
+ element_data: element_data,
view_type: PhantomData,
}
}
@@ -44,6 +44,14 @@ impl<V: 'static, D> Layout<V, D> {
}
}
+impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
+ pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+ let mut element = self.element_data.take().unwrap();
+ element.paint(view, self.id, cx);
+ self.element_data = Some(element);
+ }
+}
+
pub trait Element<V: 'static>: 'static {
type Layout;
@@ -97,6 +105,9 @@ impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
let layout = self.layout.as_mut().expect("paint called before layout");
+ if layout.engine_layout.is_none() {
+ layout.engine_layout = cx.computed_layout(layout_id).log_err()
+ }
self.element.paint(view, layout, cx)
}
}
@@ -115,7 +126,7 @@ impl<V> AnyElement<V> {
}
pub trait ParentElement<V: 'static> {
- fn children_mut(&mut self) -> &mut Vec<AnyElement<V>>;
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
fn child(mut self, child: impl IntoElement<V>) -> Self
where
@@ -49,6 +49,6 @@ 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, None, element_data))
+ Ok(Layout::new(id, element_data))
}
}
@@ -1,8 +1,14 @@
#![allow(dead_code, unused_variables)]
+use crate::{color::black, style::StyleHelpers};
+use element::Element;
+use gpui::{
+ geometry::{rect::RectF, vector::vec2f},
+ platform::WindowOptions,
+};
use log::LevelFilter;
use simplelog::SimpleLogger;
-
-use themes::ThemeColors;
+use themes::{rose_pine, ThemeColors};
+use view::view;
mod adapter;
mod color;
@@ -21,32 +27,33 @@ fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
gpui::App::new(()).unwrap().run(|cx| {
- // cx.add_window(
- // WindowOptions {
- // bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
- // vec2f(0., 0.),
- // vec2f(400., 300.),
- // )),
- // center: true,
- // ..Default::default()
- // },
- // |_| view(|_| playground(&rose_pine::moon())),
- // );
+ cx.add_window(
+ WindowOptions {
+ bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
+ vec2f(0., 0.),
+ vec2f(400., 300.),
+ )),
+ center: true,
+ ..Default::default()
+ },
+ |_| view(|_| playground(&rose_pine::moon())),
+ );
cx.platform().activate(true);
});
}
-// fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
-// todo!()
-// // frame()
-// // .text_color(black())
-// // .h_full()
-// // .w_half()
-// // .fill(theme.success(0.5))
-// // .hover()
-// // .fill(theme.error(0.5))
-// // .child(button().label("Hello").click(|_, _, _| println!("click!")))
-// }
+fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+ use div::div;
+
+ div()
+ .text_color(black())
+ .h_full()
+ .w_24()
+ .fill(theme.success(0.5))
+ // .hover()
+ // .fill(theme.error(0.5))
+ // .child(button().label("Hello").click(|_, _, _| println!("click!")))
+}
// todo!()
// // column()
@@ -257,7 +257,7 @@ pub trait Styleable {
}
}
-// Tailwind-style helpers methods that take and return mut self
+// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.
//
// Example:
// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
@@ -265,4 +265,22 @@ pub trait Styleable {
use crate as playground; // Macro invocation references this crate as playground.
pub trait StyleHelpers: Styleable<Style = Style> {
styleable_helpers!();
+
+ fn fill<F>(mut self, fill: F) -> Self
+ where
+ F: Into<Fill>,
+ Self: Sized,
+ {
+ self.declared_style().fill = Some(fill.into());
+ self
+ }
+
+ fn text_color<C>(mut self, color: C) -> Self
+ where
+ C: Into<Hsla>,
+ Self: Sized,
+ {
+ self.declared_style().text_color = Some(color.into());
+ self
+ }
}
@@ -1,72 +1,57 @@
use proc_macro::TokenStream;
-use quote::{format_ident, quote};
-use syn::{
- parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, Lit, Meta,
- WhereClause,
-};
+use proc_macro2::Ident;
+use quote::quote;
+use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics};
use crate::derive_into_element::impl_into_element;
pub fn derive_element(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let type_name = ast.ident;
+ let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
- let crate_name: String = ast
- .attrs
- .iter()
- .find_map(|attr| {
- if attr.path.is_ident("element_crate") {
- match attr.parse_meta() {
- Ok(Meta::NameValue(nv)) => {
- if let Lit::Str(s) = nv.lit {
- Some(s.value())
- } else {
- None
- }
- }
- _ => None,
- }
+ let (impl_generics, type_generics, where_clause, view_type_name, lifetimes) =
+ if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
+ if let GenericParam::Type(type_param) = param {
+ Some(type_param.ident.clone())
} else {
None
}
- })
- .unwrap_or_else(|| String::from("playground"));
-
- let crate_name = format_ident!("{}", crate_name);
-
- let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
- let placeholder_view_type_name: Ident = parse_quote! { V };
- let view_type_name: Ident;
- let impl_generics: syn::ImplGenerics<'_>;
- let type_generics: Option<syn::TypeGenerics<'_>>;
- let where_clause: Option<&'_ WhereClause>;
-
- match ast.generics.params.iter().find_map(|param| {
- if let GenericParam::Type(type_param) = param {
- Some(type_param.ident.clone())
- } else {
- None
- }
- }) {
- Some(type_name) => {
- view_type_name = type_name;
+ }) {
+ let mut lifetimes = vec![];
+ for param in ast.generics.params.iter() {
+ if let GenericParam::Lifetime(lifetime_def) = param {
+ lifetimes.push(lifetime_def.lifetime.clone());
+ }
+ }
let generics = ast.generics.split_for_impl();
- impl_generics = generics.0;
- type_generics = Some(generics.1);
- where_clause = generics.2;
- }
- _ => {
- view_type_name = placeholder_view_type_name;
+ (
+ generics.0,
+ Some(generics.1),
+ generics.2,
+ first_type_param,
+ lifetimes,
+ )
+ } else {
let generics = placeholder_view_generics.split_for_impl();
- impl_generics = generics.0;
- type_generics = None;
- where_clause = generics.2;
- }
- }
+ let placeholder_view_type_name: Ident = parse_quote! { V };
+ (
+ generics.0,
+ None,
+ generics.2,
+ placeholder_view_type_name,
+ vec![],
+ )
+ };
+
+ let lifetimes = if !lifetimes.is_empty() {
+ quote! { <#(#lifetimes),*> }
+ } else {
+ quote! {}
+ };
let impl_into_element = impl_into_element(
&impl_generics,
- &crate_name,
&view_type_name,
&type_name,
&type_generics,
@@ -74,37 +59,28 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
);
let gen = quote! {
- impl #impl_generics #crate_name::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
{
- type Layout = #crate_name::element::AnyElement<V>;
-
- fn declared_style(&mut self) -> &mut #crate_name::style::StyleRefinement {
- &mut self.metadata.style
- }
-
- fn handlers_mut(&mut self) -> &mut Vec<#crate_name::element::EventHandler<V>> {
- &mut self.metadata.handlers
- }
+ type Layout = Option<playground::element::AnyElement<#view_type_name #lifetimes>>;
fn layout(
&mut self,
view: &mut V,
- cx: &mut #crate_name::element::LayoutContext<V>,
- ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+ cx: &mut playground::element::LayoutContext<V>,
+ ) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
let mut element = self.render(view, cx).into_any();
- let node_id = element.layout(view, cx)?;
- Ok((node_id, element))
+ let layout_id = element.layout(view, cx)?;
+ Ok(playground::element::Layout::new(layout_id, Some(element)))
}
- fn paint<'a>(
+ fn paint(
&mut self,
- layout: #crate_name::element::Layout<'a, Self::Layout>,
view: &mut V,
- cx: &mut #crate_name::element::PaintContext<V>,
- ) -> anyhow::Result<()> {
- layout.from_element.paint(view, cx)?;
- Ok(())
+ layout: &mut playground::element::Layout<V, Self::Layout>,
+ cx: &mut playground::element::PaintContext<V>,
+ ) {
+ layout.paint(view, cx);
}
}
@@ -1,36 +1,13 @@
use proc_macro::TokenStream;
-use quote::{format_ident, quote};
+use quote::quote;
use syn::{
- parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, Lit, Meta,
- WhereClause,
+ parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, WhereClause,
};
pub fn derive_into_element(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let type_name = ast.ident;
- let crate_name: String = ast
- .attrs
- .iter()
- .find_map(|attr| {
- if attr.path.is_ident("element_crate") {
- match attr.parse_meta() {
- Ok(Meta::NameValue(nv)) => {
- if let Lit::Str(s) = nv.lit {
- Some(s.value())
- } else {
- None
- }
- }
- _ => None,
- }
- } else {
- None
- }
- })
- .unwrap_or_else(|| String::from("playground"));
- let crate_name = format_ident!("{}", crate_name);
-
let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
let placeholder_view_type_name: Ident = parse_quote! { V };
let view_type_name: Ident;
@@ -63,7 +40,6 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
impl_into_element(
&impl_generics,
- &crate_name,
&view_type_name,
&type_name,
&type_generics,
@@ -74,14 +50,13 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
pub fn impl_into_element(
impl_generics: &syn::ImplGenerics<'_>,
- crate_name: &Ident,
view_type_name: &Ident,
type_name: &Ident,
type_generics: &Option<syn::TypeGenerics<'_>>,
where_clause: &Option<&WhereClause>,
) -> proc_macro2::TokenStream {
quote! {
- impl #impl_generics #crate_name::element::IntoElement<#view_type_name> for #type_name #type_generics
+ impl #impl_generics playground::element::IntoElement<#view_type_name> for #type_name #type_generics
#where_clause
{
type Element = Self;