Checkpoint

This commit is contained in:
Nathan Sobo 2023-08-19 22:33:28 -06:00
parent afff46b335
commit 6663d3f8eb
7 changed files with 350 additions and 367 deletions

View file

@ -1,131 +1,131 @@
use crate::{ // use crate::{
element::{Element, ElementMetadata, ParentElement}, // element::{Element, ElementMetadata, ParentElement},
frame, // frame,
text::ArcCow, // text::ArcCow,
themes::rose_pine, // themes::rose_pine,
}; // };
use gpui::{platform::MouseButton, ViewContext}; // use gpui::{platform::MouseButton, ViewContext};
use playground_macros::Element; // use playground_macros::Element;
use std::{marker::PhantomData, rc::Rc}; // use std::{marker::PhantomData, rc::Rc};
struct ButtonHandlers<V, D> { // struct ButtonHandlers<V, D> {
click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>, // click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
} // }
impl<V, D> Default for ButtonHandlers<V, D> { // impl<V, D> Default for ButtonHandlers<V, D> {
fn default() -> Self { // fn default() -> Self {
Self { click: None } // 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(())
// } // }
// } // }
// #[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(())
// // }
// // }

View file

@ -78,7 +78,6 @@ pub trait Element<V> {
} }
} }
use crate as playground; // Macro invocation below references this crate as playground.
pub trait Styleable { pub trait Styleable {
type Style: refineable::Refineable; type Style: refineable::Refineable;
@ -89,12 +88,15 @@ pub trait Styleable {
style.refine(self.declared_style()); style.refine(self.declared_style());
style style
} }
}
// Tailwind-style helpers methods that take and return mut self // Tailwind-style helpers methods that take and return mut self
// //
// Example: // Example:
// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind. // // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
// fn p_2(mut self) -> Self where Self: Sized; // fn p_2(mut self) -> Self where Self: Sized;
use crate as playground; // Macro invocation references this crate as playground.
pub trait StyleHelpers: Styleable<Style = Style> {
styleable_helpers!(); styleable_helpers!();
} }
@ -139,6 +141,8 @@ impl<V: 'static> Element<V> for Div<V> {
Self: Sized, Self: Sized,
{ {
let style = self.style(); let style = self.style();
style.paint_background::<V, Self>(layout, cx);
} }
} }

View file

@ -1,17 +1,15 @@
use crate::{ use crate::{
adapter::Adapter, adapter::Adapter,
color::Hsla, color::Hsla,
hoverable::Hoverable,
style::{Display, Fill, Overflow, Position, StyleRefinement}, style::{Display, Fill, Overflow, Position, StyleRefinement},
}; };
use anyhow::Result; use anyhow::Result;
pub use gpui::LayoutContext; pub use gpui::LayoutContext;
use gpui::{ use gpui::{
geometry::{DefinedLength, Length, PointRefinement}, geometry::PointRefinement,
platform::{MouseButton, MouseButtonEvent}, platform::{MouseButton, MouseButtonEvent},
EngineLayout, EventContext, RenderContext, ViewContext, EngineLayout, EventContext, RenderContext, ViewContext,
}; };
use playground_macros::tailwind_lengths;
use refineable::Refineable; use refineable::Refineable;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
@ -338,94 +336,6 @@ pub trait Element<V: 'static>: 'static {
self self
} }
#[tailwind_lengths]
fn inset_(mut self, length: DefinedLength) -> Self
where
Self: Sized,
{
let inset = &mut self.declared_style().inset;
inset.top = Some(length);
inset.right = Some(length);
inset.bottom = Some(length);
inset.left = Some(length);
self
}
fn w(mut self, width: impl Into<Length>) -> Self
where
Self: Sized,
{
self.declared_style().size.width = Some(width.into());
self
}
fn w_auto(mut self) -> Self
where
Self: Sized,
{
self.declared_style().size.width = Some(Length::Auto);
self
}
#[tailwind_lengths]
fn w_(mut self, length: DefinedLength) -> Self
where
Self: Sized,
{
self.declared_style().size.width = Some(length);
self
}
#[tailwind_lengths]
fn min_w_(mut self, length: DefinedLength) -> Self
where
Self: Sized,
{
self.declared_style().min_size.width = Some(length);
self
}
fn h(mut self, height: impl Into<Length>) -> Self
where
Self: Sized,
{
self.declared_style().size.height = Some(height.into());
self
}
fn h_auto(mut self) -> Self
where
Self: Sized,
{
self.declared_style().size.height = Some(Length::Auto);
self
}
#[tailwind_lengths]
fn h_(mut self, height: DefinedLength) -> Self
where
Self: Sized,
{
self.declared_style().size.height = Some(height);
self
}
#[tailwind_lengths]
fn min_h_(mut self, length: DefinedLength) -> Self
where
Self: Sized,
{
self.declared_style().min_size.height = Some(length);
self
}
fn hover(self) -> Hoverable<V, Self>
where
Self: Sized,
{
Hoverable::new(self)
}
fn fill(mut self, fill: impl Into<Fill>) -> Self fn fill(mut self, fill: impl Into<Fill>) -> Self
where where
Self: Sized, Self: Sized,

View file

@ -1,17 +1,8 @@
#![allow(dead_code, unused_variables)] #![allow(dead_code, unused_variables)]
use color::black;
use components::button;
use element::{Element, ParentElement};
use frame::frame;
use gpui::{
geometry::{rect::RectF, vector::vec2f},
platform::WindowOptions,
};
use log::LevelFilter; use log::LevelFilter;
use simplelog::SimpleLogger; use simplelog::SimpleLogger;
use themes::{rose_pine, ThemeColors}; use themes::ThemeColors;
use view::view;
mod adapter; mod adapter;
mod color; mod color;
@ -31,31 +22,32 @@ fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
gpui::App::new(()).unwrap().run(|cx| { gpui::App::new(()).unwrap().run(|cx| {
cx.add_window( // cx.add_window(
WindowOptions { // WindowOptions {
bounds: gpui::platform::WindowBounds::Fixed(RectF::new( // bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
vec2f(0., 0.), // vec2f(0., 0.),
vec2f(400., 300.), // vec2f(400., 300.),
)), // )),
center: true, // center: true,
..Default::default() // ..Default::default()
}, // },
|_| view(|_| playground(&rose_pine::moon())), // |_| view(|_| playground(&rose_pine::moon())),
); // );
cx.platform().activate(true); cx.platform().activate(true);
}); });
} }
fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> { // fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
frame() // todo!()
.text_color(black()) // // frame()
.h_full() // // .text_color(black())
.w_half() // // .h_full()
.fill(theme.success(0.5)) // // .w_half()
.hover() // // .fill(theme.success(0.5))
.fill(theme.error(0.5)) // // .hover()
.child(button().label("Hello").click(|_, _, _| println!("click!"))) // // .fill(theme.error(0.5))
} // // .child(button().label("Hello").click(|_, _, _| println!("click!")))
// }
// todo!() // todo!()
// // column() // // column()

View file

@ -1,8 +1,13 @@
use crate::color::Hsla; use crate::{
color::Hsla,
div::{Element, Layout},
element::PaintContext,
};
use gpui::{ use gpui::{
fonts::TextStyleRefinement, fonts::TextStyleRefinement,
geometry::{ geometry::{
DefinedLength, Edges, EdgesRefinement, Length, Point, PointRefinement, Size, SizeRefinement, AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point, PointRefinement,
Size, SizeRefinement,
}, },
}; };
use refineable::Refineable; use refineable::Refineable;
@ -49,10 +54,10 @@ pub struct Style {
pub margin: Edges<Length>, pub margin: Edges<Length>,
/// How large should the padding be on each side? /// How large should the padding be on each side?
#[refineable] #[refineable]
pub padding: Edges<DefinedLength>, pub padding: Edges<DefiniteLength>,
/// How large should the border be on each side? /// How large should the border be on each side?
#[refineable] #[refineable]
pub border: Edges<DefinedLength>, pub border: Edges<DefiniteLength>,
// Alignment properties // Alignment properties
/// How this node's children aligned in the cross/block axis? /// How this node's children aligned in the cross/block axis?
@ -65,7 +70,7 @@ pub struct Style {
pub justify_content: Option<JustifyContent>, pub justify_content: Option<JustifyContent>,
/// How large should the gaps between items in a flex container be? /// How large should the gaps between items in a flex container be?
#[refineable] #[refineable]
pub gap: Size<DefinedLength>, pub gap: Size<DefiniteLength>,
// Flexbox properies // Flexbox properies
/// Which direction does the main axis flow in? /// Which direction does the main axis flow in?
@ -81,47 +86,14 @@ pub struct Style {
/// The fill color of this element /// The fill color of this element
pub fill: Option<Fill>, pub fill: Option<Fill>,
/// The radius of the corners of this element
#[refineable]
pub corner_radii: CornerRadii,
/// The color of text within this element. Cascades to children unless overridden. /// The color of text within this element. Cascades to children unless overridden.
pub text_color: Option<Hsla>, pub text_color: Option<Hsla>,
} }
impl Style { impl Style {
pub const DEFAULT: Style = Style {
display: Display::DEFAULT,
overflow: Point {
x: Overflow::Visible,
y: Overflow::Visible,
},
scrollbar_width: 0.0,
position: Position::Relative,
inset: Edges::auto(),
margin: Edges::<Length>::zero(),
padding: Edges::<DefinedLength>::zero(),
border: Edges::<DefinedLength>::zero(),
size: Size::auto(),
min_size: Size::auto(),
max_size: Size::auto(),
aspect_ratio: None,
gap: Size::zero(),
// Aligment
align_items: None,
align_self: None,
align_content: None,
justify_content: None,
// Flexbox
flex_direction: FlexDirection::Row,
flex_wrap: FlexWrap::NoWrap,
flex_grow: 0.0,
flex_shrink: 1.0,
flex_basis: Length::Auto,
fill: None,
text_color: None,
};
pub fn new() -> Self {
Self::DEFAULT.clone()
}
pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style { pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
taffy::style::Style { taffy::style::Style {
display: self.display, display: self.display,
@ -149,11 +121,61 @@ impl Style {
..Default::default() // Ignore grid properties for now ..Default::default() // Ignore grid properties for now
} }
} }
/// Paints the background of an element styled with this style.
/// Return the bounds in which to paint the content.
pub fn paint_background<V: 'static, E: Element<V>>(
&self,
layout: &mut Layout<V, E::Layout>,
cx: &mut PaintContext<V>,
) {
let bounds = layout.bounds(cx);
let rem_size = cx.rem_pixels();
if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
cx.scene.push_quad(gpui::Quad {
bounds,
background: Some(color.into()),
corner_radii: self.corner_radii.to_gpui(rem_size),
border: Default::default(),
});
}
}
} }
impl Default for Style { impl Default for Style {
fn default() -> Self { fn default() -> Self {
Self::DEFAULT.clone() Style {
display: Display::DEFAULT,
overflow: Point {
x: Overflow::Visible,
y: Overflow::Visible,
},
scrollbar_width: 0.0,
position: Position::Relative,
inset: Edges::auto(),
margin: Edges::<Length>::zero(),
padding: Edges::<DefiniteLength>::zero(),
border: Edges::<DefiniteLength>::zero(),
size: Size::auto(),
min_size: Size::auto(),
max_size: Size::auto(),
aspect_ratio: None,
gap: Size::zero(),
// Aligment
align_items: None,
align_self: None,
align_content: None,
justify_content: None,
// Flexbox
flex_direction: FlexDirection::Row,
flex_wrap: FlexWrap::NoWrap,
flex_grow: 0.0,
flex_shrink: 1.0,
flex_basis: Length::Auto,
fill: None,
text_color: None,
corner_radii: CornerRadii::default(),
}
} }
} }
@ -202,3 +224,22 @@ impl From<Hsla> for Fill {
Self::Color(color) Self::Color(color)
} }
} }
#[derive(Clone, Refineable, Default)]
pub struct CornerRadii {
top_left: AbsoluteLength,
top_right: AbsoluteLength,
bottom_left: AbsoluteLength,
bottom_right: AbsoluteLength,
}
impl CornerRadii {
pub fn to_gpui(&self, rem_size: f32) -> gpui::scene::CornerRadii {
gpui::scene::CornerRadii {
top_left: self.top_left.to_pixels(rem_size),
top_right: self.top_right.to_pixels(rem_size),
bottom_left: self.bottom_left.to_pixels(rem_size),
bottom_right: self.bottom_right.to_pixels(rem_size),
}
}
}

View file

@ -38,7 +38,7 @@ fn generate_methods() -> Vec<TokenStream2> {
.iter() .iter()
.map(|field_tokens| { .map(|field_tokens| {
quote! { quote! {
style.#field_tokens = Some(gpui::geometry::#length_tokens.into()); style.#field_tokens = Some(gpui::geometry::#length_tokens);
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -60,30 +60,30 @@ fn generate_methods() -> Vec<TokenStream2> {
fn tailwind_lengths() -> Vec<(&'static str, TokenStream2)> { fn tailwind_lengths() -> Vec<(&'static str, TokenStream2)> {
vec![ vec![
("0", quote! { DefinedLength::Pixels(0.) }), ("0", quote! { pixels(0.) }),
("1", quote! { DefinedLength::Rems(0.25) }), ("1", quote! { rems(0.25) }),
("2", quote! { DefinedLength::Rems(0.5) }), ("2", quote! { rems(0.5) }),
("3", quote! { DefinedLength::Rems(0.75) }), ("3", quote! { rems(0.75) }),
("4", quote! { DefinedLength::Rems(1.0) }), ("4", quote! { rems(1.) }),
("5", quote! { DefinedLength::Rems(1.25) }), ("5", quote! { rems(1.25) }),
("6", quote! { DefinedLength::Rems(1.5) }), ("6", quote! { rems(1.5) }),
("8", quote! { DefinedLength::Rems(2.0) }), ("8", quote! { rems(2.0) }),
("10", quote! { DefinedLength::Rems(2.5) }), ("10", quote! { rems(2.5) }),
("12", quote! { DefinedLength::Rems(3.0) }), ("12", quote! { rems(3.) }),
("16", quote! { DefinedLength::Rems(4.0) }), ("16", quote! { rems(4.) }),
("20", quote! { DefinedLength::Rems(5.0) }), ("20", quote! { rems(5.) }),
("24", quote! { DefinedLength::Rems(6.0) }), ("24", quote! { rems(6.) }),
("32", quote! { DefinedLength::Rems(8.0) }), ("32", quote! { rems(8.) }),
("40", quote! { DefinedLength::Rems(10.0) }), ("40", quote! { rems(10.) }),
("48", quote! { DefinedLength::Rems(12.0) }), ("48", quote! { rems(12.) }),
("56", quote! { DefinedLength::Rems(14.0) }), ("56", quote! { rems(14.) }),
("64", quote! { DefinedLength::Rems(16.0) }), ("64", quote! { rems(16.) }),
("auto", quote! { Length::Auto }), ("auto", quote! { auto() }),
("px", quote! { DefinedLength::Pixels(1.0) }), ("px", quote! { pixels(1.) }),
("full", quote! { DefinedLength::Percent(100.0) }), ("full", quote! { relative(1.) }),
// ("screen_50", quote! { DefinedLength::Vh(50.0) }), // ("screen_50", quote! { DefiniteLength::Vh(50.0) }),
// ("screen_75", quote! { DefinedLength::Vh(75.0) }), // ("screen_75", quote! { DefiniteLength::Vh(75.0) }),
// ("screen", quote! { DefinedLength::Vh(100.0) }), // ("screen", quote! { DefiniteLength::Vh(100.0) }),
] ]
} }

View file

@ -187,11 +187,11 @@ where
} }
} }
impl Size<DefinedLength> { impl Size<DefiniteLength> {
pub const fn zero() -> Self { pub fn zero() -> Self {
Self { Self {
width: DefinedLength::Pixels(0.), width: pixels(0.),
height: DefinedLength::Pixels(0.), height: pixels(0.),
} }
} }
@ -204,7 +204,7 @@ impl Size<DefinedLength> {
} }
impl Size<Length> { impl Size<Length> {
pub const fn auto() -> Self { pub fn auto() -> Self {
Self { Self {
width: Length::Auto, width: Length::Auto,
height: Length::Auto, height: Length::Auto,
@ -230,13 +230,13 @@ pub struct Edges<T: Clone + Default> {
pub left: T, pub left: T,
} }
impl Edges<DefinedLength> { impl Edges<DefiniteLength> {
pub const fn zero() -> Self { pub fn zero() -> Self {
Self { Self {
top: DefinedLength::Pixels(0.0), top: pixels(0.),
right: DefinedLength::Pixels(0.0), right: pixels(0.),
bottom: DefinedLength::Pixels(0.0), bottom: pixels(0.),
left: DefinedLength::Pixels(0.0), left: pixels(0.),
} }
} }
@ -251,7 +251,7 @@ impl Edges<DefinedLength> {
} }
impl Edges<Length> { impl Edges<Length> {
pub const fn auto() -> Self { pub fn auto() -> Self {
Self { Self {
top: Length::Auto, top: Length::Auto,
right: Length::Auto, right: Length::Auto,
@ -260,12 +260,12 @@ impl Edges<Length> {
} }
} }
pub const fn zero() -> Self { pub fn zero() -> Self {
Self { Self {
top: Length::Defined(DefinedLength::Pixels(0.0)), top: pixels(0.),
right: Length::Defined(DefinedLength::Pixels(0.0)), right: pixels(0.),
bottom: Length::Defined(DefinedLength::Pixels(0.0)), bottom: pixels(0.),
left: Length::Defined(DefinedLength::Pixels(0.0)), left: pixels(0.),
} }
} }
@ -282,72 +282,108 @@ impl Edges<Length> {
} }
} }
/// A non-auto length that can be defined in pixels, rems, or percent of parent.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum DefinedLength { pub enum AbsoluteLength {
Pixels(f32), Pixels(f32),
Rems(f32), Rems(f32),
Percent(f32), // 0. - 100.
} }
impl DefinedLength { impl AbsoluteLength {
pub fn to_pixels(&self, rem_size: f32) -> f32 {
match self {
AbsoluteLength::Pixels(pixels) => *pixels,
AbsoluteLength::Rems(rems) => rems * rem_size,
}
}
}
impl Default for AbsoluteLength {
fn default() -> Self {
Self::Pixels(0.0)
}
}
/// A non-auto length that can be defined in pixels, rems, or percent of parent.
#[derive(Clone, Copy)]
pub enum DefiniteLength {
Absolute(AbsoluteLength),
Relative(f32), // Percent, from 0 to 100.
}
impl DefiniteLength {
fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage { fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
match self { match self {
DefinedLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels), DefiniteLength::Absolute(length) => match length {
DefinedLength::Rems(rems) => taffy::style::LengthPercentage::Length(rems * rem_size), AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
DefinedLength::Percent(percent) => { AbsoluteLength::Rems(rems) => {
taffy::style::LengthPercentage::Percent(*percent / 100.) taffy::style::LengthPercentage::Length(rems * rem_size)
}
},
DefiniteLength::Relative(fraction) => {
taffy::style::LengthPercentage::Percent(*fraction)
} }
} }
} }
} }
impl Default for DefinedLength { impl From<AbsoluteLength> for DefiniteLength {
fn from(length: AbsoluteLength) -> Self {
Self::Absolute(length)
}
}
impl Default for DefiniteLength {
fn default() -> Self { fn default() -> Self {
Self::Pixels(0.) Self::Absolute(AbsoluteLength::default())
} }
} }
/// A length that can be defined in pixels, rems, percent of parent, or auto. /// A length that can be defined in pixels, rems, percent of parent, or auto.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Length { pub enum Length {
Defined(DefinedLength), Definite(DefiniteLength),
Auto, Auto,
} }
pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
DefiniteLength::Relative(fraction).into()
}
pub fn rems<T: From<AbsoluteLength>>(rems: f32) -> T {
AbsoluteLength::Rems(rems).into()
}
pub fn pixels<T: From<AbsoluteLength>>(pixels: f32) -> T {
AbsoluteLength::Pixels(pixels).into()
}
pub fn auto() -> Length { pub fn auto() -> Length {
Length::Auto Length::Auto
} }
pub fn percent(percent: f32) -> DefinedLength {
DefinedLength::Percent(percent)
}
pub fn rems(rems: f32) -> DefinedLength {
DefinedLength::Rems(rems)
}
pub fn pixels(pixels: f32) -> DefinedLength {
DefinedLength::Pixels(pixels)
}
impl Length { impl Length {
pub fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto { pub fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
match self { match self {
Length::Defined(length) => length.to_taffy(rem_size).into(), Length::Definite(length) => length.to_taffy(rem_size).into(),
Length::Auto => taffy::prelude::LengthPercentageAuto::Auto, Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
} }
} }
} }
impl From<DefinedLength> for Length { impl From<DefiniteLength> for Length {
fn from(value: DefinedLength) -> Self { fn from(length: DefiniteLength) -> Self {
Length::Defined(value) Self::Definite(length)
}
}
impl From<AbsoluteLength> for Length {
fn from(length: AbsoluteLength) -> Self {
Self::Definite(length.into())
} }
} }
impl Default for Length { impl Default for Length {
fn default() -> Self { fn default() -> Self {
Self::Defined(DefinedLength::default()) Self::Definite(DefiniteLength::default())
} }
} }