diff --git a/crates/gpui/playground/docs/thoughts.md b/crates/gpui/playground/docs/thoughts.md index 147d8bdc81a93a06e9f01266d499b7573c13d72a..9416f2593313b973ada409770b41b1f1f074ba09 100644 --- a/crates/gpui/playground/docs/thoughts.md +++ b/crates/gpui/playground/docs/thoughts.md @@ -1,3 +1,25 @@ +Much of element styling is now handled by an external engine. + + +How do I make an element hover. + +There's a hover style. + +Hoverable needs to wrap another element. That element can be styled. + +```rs +struct Hoverable { + +} + +impl Element for Hoverable { + +} + +``` + + + ```rs #[derive(Styled, Interactive)] pub struct Div { @@ -30,4 +52,21 @@ struct Interactions { } +``` + + +```rs + + +trait Stylable { + type Style; + + fn with_style(self, style: Self::Style) -> Self; +} + + + + + + ``` diff --git a/crates/gpui/playground/src/div.rs b/crates/gpui/playground/src/div.rs new file mode 100644 index 0000000000000000000000000000000000000000..8a6be0e5324c90628e3c86a2140d3779f9fc7939 --- /dev/null +++ b/crates/gpui/playground/src/div.rs @@ -0,0 +1,83 @@ +use crate::style::StyleRefinement; +use playground_macros::styleable_trait; +use refineable::Refineable; + +trait Element { + type Style; + + fn hover(self) -> Hover + where + Self: Sized, + Self::Style: Refineable, + ::Refinement: Default, + { + Hover { + child: self, + style: <>::Style as Refineable>::Refinement::default(), + } + } +} + +use crate as playground; +styleable_trait!(); + +struct Hover> +where + E::Style: Refineable, +{ + child: E, + style: ::Refinement, +} + +struct Div { + style: StyleRefinement, +} + +impl Styleable for Div { + fn declared_style(&mut self) -> &mut StyleRefinement { + &mut self.style + } +} + +fn div() -> Div { + Div { + style: Default::default(), + } +} + +impl Element for Div { + type Style = StyleRefinement; +} + +#[test] +fn test() { + let elt = div().w_auto(); +} + +// trait Element { +// type Style; + +// fn layout() +// } + +// trait Stylable: Element { +// type Style; + +// fn with_style(self, style: Self::Style) -> Self; +// } + +// pub struct HoverStyle { +// default: S, +// hovered: S, +// } + +// struct Hover> { +// child: C, +// style: HoverStyle, +// } + +// impl> Hover { +// fn new(child: C, style: HoverStyle) -> Self { +// Self { child, style } +// } +// } diff --git a/crates/gpui/playground/src/playground.rs b/crates/gpui/playground/src/playground.rs index ec7aeecb473087c531e434c48c2e898c84d9cf02..d4147047bf842793b53bf3d67e0e97701d60d655 100644 --- a/crates/gpui/playground/src/playground.rs +++ b/crates/gpui/playground/src/playground.rs @@ -16,6 +16,7 @@ use view::view; mod adapter; mod color; mod components; +mod div; mod element; mod frame; mod hoverable; diff --git a/crates/gpui/playground_macros/src/playground_macros.rs b/crates/gpui/playground_macros/src/playground_macros.rs index 4b0de7101586dd9ae12694f9a43ed994e3707c4d..6e387b03cda76f48e0741291946da707ea3ae5f0 100644 --- a/crates/gpui/playground_macros/src/playground_macros.rs +++ b/crates/gpui/playground_macros/src/playground_macros.rs @@ -2,8 +2,14 @@ use proc_macro::TokenStream; mod derive_element; mod derive_into_element; +mod styleable_trait; mod tailwind_lengths; +#[proc_macro] +pub fn styleable_trait(args: TokenStream) -> TokenStream { + styleable_trait::styleable_trait(args) +} + #[proc_macro_derive(Element, attributes(element_crate))] pub fn derive_element(input: TokenStream) -> TokenStream { derive_element::derive_element(input) diff --git a/crates/gpui/playground_macros/src/styleable_trait.rs b/crates/gpui/playground_macros/src/styleable_trait.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a952bc23648e4ea7d876eb862c53ccca030efe5 --- /dev/null +++ b/crates/gpui/playground_macros/src/styleable_trait.rs @@ -0,0 +1,125 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; + +fn tailwind_lengths() -> Vec<(&'static str, TokenStream2)> { + vec![ + ("0", quote! { DefinedLength::Pixels(0.) }), + ("1", quote! { DefinedLength::Rems(0.25) }), + ("2", quote! { DefinedLength::Rems(0.5) }), + ("3", quote! { DefinedLength::Rems(0.75) }), + ("4", quote! { DefinedLength::Rems(1.0) }), + ("5", quote! { DefinedLength::Rems(1.25) }), + ("6", quote! { DefinedLength::Rems(1.5) }), + ("8", quote! { DefinedLength::Rems(2.0) }), + ("10", quote! { DefinedLength::Rems(2.5) }), + ("12", quote! { DefinedLength::Rems(3.0) }), + ("16", quote! { DefinedLength::Rems(4.0) }), + ("20", quote! { DefinedLength::Rems(5.0) }), + ("24", quote! { DefinedLength::Rems(6.0) }), + ("32", quote! { DefinedLength::Rems(8.0) }), + ("40", quote! { DefinedLength::Rems(10.0) }), + ("48", quote! { DefinedLength::Rems(12.0) }), + ("56", quote! { DefinedLength::Rems(14.0) }), + ("64", quote! { DefinedLength::Rems(16.0) }), + ("auto", quote! { Length::Auto }), + ("px", quote! { DefinedLength::Pixels(1.0) }), + ("full", quote! { DefinedLength::Percent(100.0) }), + // ("screen_50", quote! { DefinedLength::Vh(50.0) }), + // ("screen_75", quote! { DefinedLength::Vh(75.0) }), + // ("screen", quote! { DefinedLength::Vh(100.0) }), + ] +} + +fn tailwind_prefixes() -> Vec<(&'static str, bool, Vec)> { + vec![ + ("w", true, vec![quote! { size.width }]), + ("h", true, vec![quote! { size.height }]), + ("min_w", false, vec![quote! { min_size.width }]), + ("min_h", false, vec![quote! { min_size.height }]), + ("max_w", false, vec![quote! { max_size.width }]), + ("max_h", false, vec![quote! { max_size.height }]), + ( + "m", + true, + vec![quote! { margin.top }, quote! { margin.bottom }], + ), + ("mt", true, vec![quote! { margin.top }]), + ("mb", true, vec![quote! { margin.bottom }]), + ( + "mx", + true, + vec![quote! { margin.left }, quote! { margin.right }], + ), + ("ml", true, vec![quote! { margin.left }]), + ("mr", true, vec![quote! { margin.right }]), + ( + "p", + false, + vec![quote! { padding.top }, quote! { padding.bottom }], + ), + ("pt", false, vec![quote! { padding.top }]), + ("pb", false, vec![quote! { padding.bottom }]), + ( + "px", + false, + vec![quote! { padding.left }, quote! { padding.right }], + ), + ("pl", false, vec![quote! { padding.left }]), + ("pr", false, vec![quote! { padding.right }]), + ("top", true, vec![quote! { inset.top }]), + ("bottom", true, vec![quote! { inset.bottom }]), + ("left", true, vec![quote! { inset.left }]), + ("right", true, vec![quote! { inset.right }]), + ] +} + +pub fn styleable_trait(_item: TokenStream) -> TokenStream { + let mut methods = Vec::new(); + + for (prefix, auto_allowed, fields) in tailwind_prefixes() { + for (suffix, length_tokens) in tailwind_lengths() { + if !auto_allowed && suffix == "auto" { + // Conditional to skip "auto" + continue; + } + + let method_name = format_ident!("{}_{}", prefix, suffix); + + let field_assignments = fields + .iter() + .map(|field_tokens| { + quote! { + style.#field_tokens = Some(gpui::geometry::#length_tokens.into()); + } + }) + .collect::>(); + + let method = quote! { + fn #method_name(mut self) -> Self where Self: Sized { + let mut style = self.declared_style(); + #(#field_assignments)* + self + } + }; + + methods.push(method); + } + } + + let output = quote! { + pub trait Styleable { + fn declared_style(&mut self) -> &mut playground::style::StyleRefinement; + + fn style(&mut self) -> playground::style::Style { + let mut style = playground::style::Style::default(); + style.refine(self.declared_style()); + style + } + + #(#methods)* + } + }; + + output.into() +}