working uno-like styles generation

This commit is contained in:
Daniel Bulant 2024-03-02 11:24:53 +01:00
parent 4f454684d3
commit 0d43e248b1
14 changed files with 1408 additions and 228 deletions

1105
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,5 +6,6 @@ members = [
"mangades", "mangades",
"rusalka", "rusalka",
"rusalka-macro", "rusalka-macro",
"mangades-plain" "mangades-plain",
"uno-gen"
] ]

View file

@ -4,7 +4,7 @@ use mangui::{MainEntry, SharedNode};
use mangui::femtovg::Paint; use mangui::femtovg::Paint;
use mangui::nodes::text::Text; use mangui::nodes::text::Text;
use mangui::nodes::{Style, TaffyStyle, ToShared}; use mangui::nodes::{Style, TaffyStyle, ToShared};
use mangui::taffy::{AlignItems, FlexDirection, JustifyContent, LengthPercentage, Rect}; use mangui::taffy::{AlignItems, FlexDirection, JustifyContent, LengthPercentage, LengthPercentageAuto, Point, Rect};
use uno_gen::uno; use uno_gen::uno;
use crate::anilist::load_demo_async; use crate::anilist::load_demo_async;
use crate::tokens::TEXT_LARGE; use crate::tokens::TEXT_LARGE;
@ -57,16 +57,11 @@ async fn main() {
..Default::default() ..Default::default()
}) })
.to_shared(); .to_shared();
let i = 2; let i = LengthPercentageAuto::Length(5.);
uno!(gap-1 flex p-5px mt-1 mb-2 ml-[i] overflow-hidden);
let title = Text::new("Mangades".to_owned(), TEXT_LARGE) let title = Text::new("Mangades".to_owned(), TEXT_LARGE)
.style(Style { .style(Style {
layout: TaffyStyle {
padding: Rect { left: LengthPercentage::Length(10.), right: LengthPercentage::Length(10.), top: LengthPercentage::Length(10.), bottom: LengthPercentage::Length(10.) },
..Default::default()
},
text_fill: Some(Paint::color(*tokens::WHITE)), text_fill: Some(Paint::color(*tokens::WHITE)),
..Default::default() ..uno!(p-10)
}) })
.to_shared(); .to_shared();

View file

@ -0,0 +1,23 @@
use lazy_static::lazy_static;
use mangui::cosmic_text::Metrics;
use mangui::femtovg::Color;
lazy_static!{
pub static ref BACKGROUND: Color = Color::hex("282C34");
pub static ref RED: Color = Color::hex("E06C75");
pub static ref GREEN: Color = Color::hex("98C379");
pub static ref YELLOW: Color = Color::hex("E5C07B");
pub static ref BLUE: Color = Color::hex("61AFEF");
pub static ref MAGENTA: Color = Color::hex("C678DD");
pub static ref CYAN: Color = Color::hex("56B6C2");
pub static ref GRAY: Color = Color::hex("ABB2BF");
pub static ref WHITE: Color = Color::hex("FFFFFF");
pub static ref BLACK: Color = Color::hex("000000");
}
pub static TEXT_NORMAL: Metrics = Metrics::new(16., 20.);
pub static TEXT_SMALL: Metrics = Metrics::new(14., 18.);
pub static TEXT_TINY: Metrics = Metrics::new(12., 16.);
pub static TEXT_LARGE: Metrics = Metrics::new(20., 24.);
pub static TEXT_HUGE: Metrics = Metrics::new(24., 30.);
pub static TEXT_GIANT: Metrics = Metrics::new(32., 36.);

View file

@ -2,20 +2,20 @@ use std::sync::Arc;
use mangui::SharedNode; use mangui::SharedNode;
pub fn detach(node: &SharedNode) { pub fn detach(node: &SharedNode) {
if let Some(parent) = node.read().unwrap().parent() { if let Some(parent) = node.lock().unwrap().parent() {
parent.write().unwrap().remove_child(node).unwrap(); parent.lock().unwrap().remove_child(node).unwrap();
} }
node.clone().write().unwrap().set_parent(None); node.clone().lock().unwrap().set_parent(None);
} }
pub fn insert(parent: &SharedNode, node: &SharedNode, before: Option<&SharedNode>) { pub fn insert(parent: &SharedNode, node: &SharedNode, before: Option<&SharedNode>) {
if node.read().unwrap().parent().is_some() && !Arc::ptr_eq(&node.read().unwrap().parent().unwrap(), parent) { if node.lock().unwrap().parent().is_some() && !Arc::ptr_eq(&node.lock().unwrap().parent().unwrap(), parent) {
detach(node); detach(node);
} }
match before { match before {
Some(before) => { Some(before) => {
parent.write().unwrap().add_child_before(node.clone(), before).unwrap(); parent.lock().unwrap().add_child_before(node.clone(), before).unwrap();
node.write().unwrap().set_parent(Some(Arc::downgrade(parent))); node.lock().unwrap().set_parent(Some(Arc::downgrade(parent)));
}, },
None => { None => {
append(parent, node); append(parent, node);
@ -24,6 +24,6 @@ pub fn insert(parent: &SharedNode, node: &SharedNode, before: Option<&SharedNode
} }
pub fn append(parent: &SharedNode, node: &SharedNode) { pub fn append(parent: &SharedNode, node: &SharedNode) {
parent.write().unwrap().add_child(node.clone()).unwrap(); parent.lock().unwrap().add_child(node.clone()).unwrap();
node.write().unwrap().set_parent(Some(Arc::downgrade(parent))); node.lock().unwrap().set_parent(Some(Arc::downgrade(parent)));
} }

View file

@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
femtovg = "0.8.2" femtovg = { path = "../femtovg" }
glutin = "0.31.3" glutin = "0.31.3"
raw-window-handle = "0.5.0" raw-window-handle = "0.5.0"
winit = { version = "0.29.10" } winit = { version = "0.29.10" }

View file

@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Debug, sync::{Arc, Mutex}};
use super::NodeEvent; use super::NodeEvent;
/// A node event handler /// A node event handler
pub type EventHandler = dyn FnMut(&NodeEvent); pub type EventHandler = dyn FnMut(&NodeEvent) + Send;
pub type InnerEventHandlerDataset = Arc<Mutex<HashMap<usize, Arc<Mutex<Box<EventHandler>>>>>>; pub type InnerEventHandlerDataset = Arc<Mutex<HashMap<usize, Arc<Mutex<Box<EventHandler>>>>>>;

View file

@ -31,16 +31,17 @@ use crate::nodes::{update_taffynode_children, MeasureContext, Node, render_recur
pub mod nodes; pub mod nodes;
pub mod events; pub mod events;
pub use taffy; pub use taffy;
pub use femtovg; pub use femtovg;
pub use cosmic_text; pub use cosmic_text;
pub type CurrentRenderer = OpenGl; pub type CurrentRenderer = OpenGl;
pub type SharedNode = Arc<RwLock<dyn Node>>; pub type SharedNode = Arc<Mutex<dyn Node>>;
pub type WeakSharedNode = Weak<RwLock<dyn Node>>; pub type WeakSharedNode = Weak<Mutex<dyn Node>>;
type WeakNode = Weak<RwLock<dyn Node>>; type WeakNode = Weak<Mutex<dyn Node>>;
type NodePtr = Option<Vec<WeakNode>>; type NodePtr = Option<Vec<WeakNode>>;
type NodeLayoutMap = PtrWeakKeyHashMap<Weak<RwLock<dyn Node>>, taffy::tree::NodeId>; type NodeLayoutMap = PtrWeakKeyHashMap<Weak<Mutex<dyn Node>>, taffy::tree::NodeId>;
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref FONT_SYSTEM: Mutex<FontSystem> = Mutex::new(FontSystem::new()); pub static ref FONT_SYSTEM: Mutex<FontSystem> = Mutex::new(FontSystem::new());
@ -80,7 +81,7 @@ pub fn run_event_loop(entry: MainEntry) -> () {
let mut taffy_map = NodeLayoutMap::new(); let mut taffy_map = NodeLayoutMap::new();
{ {
let cloned = entry.root.clone(); let cloned = entry.root.clone();
let root = cloned.read().unwrap(); let root = cloned.lock().unwrap();
let root_style = root.deref().style(); let root_style = root.deref().style();
let root_layout = root_style.layout.to_owned(); let root_layout = root_style.layout.to_owned();
let taffy_root_node = taffy.new_leaf(root_layout).unwrap(); let taffy_root_node = taffy.new_leaf(root_layout).unwrap();
@ -241,7 +242,7 @@ pub fn run_event_loop(entry: MainEntry) -> () {
let width: NonZeroU32 = NonZeroU32::new(size.width).unwrap(); let width: NonZeroU32 = NonZeroU32::new(size.width).unwrap();
let height: NonZeroU32 = NonZeroU32::new(size.height).unwrap(); let height: NonZeroU32 = NonZeroU32::new(size.height).unwrap();
surface.resize(&buffer_context, width, height); surface.resize(&buffer_context, width, height);
let mut groot = entry.root.write().unwrap(); let mut groot = entry.root.lock().unwrap();
// let scale_factor = window.scale_factor(); // let scale_factor = window.scale_factor();
groot.resize(size.width as f32, size.height as f32); groot.resize(size.width as f32, size.height as f32);
drop(groot); drop(groot);
@ -263,7 +264,7 @@ pub fn run_event_loop(entry: MainEntry) -> () {
} }
prepare_render_recursively(&root, &mut context); prepare_render_recursively(&root, &mut context);
for (node, taffy_node) in context.node_layout.iter() { for (node, taffy_node) in context.node_layout.iter() {
let node = node.read().unwrap(); let node = node.lock().unwrap();
let node_style = node.style(); let node_style = node.style();
context.taffy.set_style(*taffy_node, node_style.layout.to_owned()).unwrap(); context.taffy.set_style(*taffy_node, node_style.layout.to_owned()).unwrap();
} }
@ -279,7 +280,7 @@ pub fn run_event_loop(entry: MainEntry) -> () {
Some(node) => { Some(node) => {
match node.upgrade() { match node.upgrade() {
Some(node) => { Some(node) => {
node.write().unwrap().measure(&mut measure_context, known_dimensions, available_space) node.lock().unwrap().measure(&mut measure_context, known_dimensions, available_space)
}, },
None => Size::ZERO None => Size::ZERO
} }

View file

@ -45,8 +45,6 @@ pub struct Image {
pub style: Style, pub style: Style,
/// The image to be rendered. /// The image to be rendered.
pub image: ImageLoad, pub image: ImageLoad,
/// Border radius
pub radius: f32,
pub events: EventHandlerDatabase, pub events: EventHandlerDatabase,
pub parent: Option<WeakNode> pub parent: Option<WeakNode>
} }
@ -58,10 +56,6 @@ impl Image {
..Default::default() ..Default::default()
} }
} }
pub fn radius(mut self, radius: f32) -> Image {
self.radius = radius;
self
}
pub fn style(mut self, style: Style) -> Image { pub fn style(mut self, style: Style) -> Image {
self.style = style; self.style = style;
self self
@ -123,10 +117,13 @@ impl Node for Image {
0., 0.,
layout.size.width, layout.size.width,
layout.size.height, layout.size.height,
self.radius self.style.border_radius
); );
match &self.image { match &self.image {
ImageLoad::Loaded(image) => { ImageLoad::Loaded(image) => {
if let Some(background) = &self.style.background {
context.canvas.fill_path(&path, background);
}
context.canvas.fill_path( context.canvas.fill_path(
&path, &path,
&Paint::image(image.image, 0., 0., layout.size.width, layout.size.height, 0., 1.) &Paint::image(image.image, 0., 0., layout.size.width, layout.size.height, 0., 1.)

View file

@ -1,7 +1,10 @@
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use femtovg::{Paint, Path};
use crate::{nodes::{Node, NodeChildren, Style}, events::handler::EventHandlerDatabase, WeakNode, SharedNode}; use crate::{nodes::{Node, NodeChildren, Style}, events::handler::EventHandlerDatabase, WeakNode, SharedNode};
use taffy::style::Dimension; use taffy::style::Dimension;
use crate::nodes::primitives::draw_rect;
use crate::nodes::RenderContext;
/// A simple layout node which contains children. /// A simple layout node which contains children.
#[derive(Default)] #[derive(Default)]
@ -18,7 +21,7 @@ impl Layout {
style: Style::default(), style: Style::default(),
children, children,
events: EventHandlerDatabase::default(), events: EventHandlerDatabase::default(),
parent: None ..Default::default()
} }
} }
pub fn empty() -> Layout { pub fn empty() -> Layout {
@ -26,7 +29,7 @@ impl Layout {
style: Style::default(), style: Style::default(),
children: NodeChildren::default(), children: NodeChildren::default(),
events: EventHandlerDatabase::default(), events: EventHandlerDatabase::default(),
parent: None ..Default::default()
} }
} }
@ -57,6 +60,10 @@ impl Node for Layout {
self.style.layout.size.height = Dimension::Length(height); self.style.layout.size.height = Dimension::Length(height);
} }
fn render_pre_children(&mut self, context: &mut RenderContext, layout: taffy::Layout) {
if let Some(background) = &self.style.background { draw_rect(layout.size, background, self.style.border_radius, &mut context.canvas); }
}
fn add_child_at(&mut self, child: crate::SharedNode, index: usize) -> Result<(), super::ChildAddError> { fn add_child_at(&mut self, child: crate::SharedNode, index: usize) -> Result<(), super::ChildAddError> {
let mut index = index; let mut index = index;
if let Some(i) = self.has_child(&child) { if let Some(i) = self.has_child(&child) {

View file

@ -6,14 +6,14 @@ pub mod text;
pub mod text_render_cache; pub mod text_render_cache;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use femtovg::{Canvas, Color}; use femtovg::{Canvas, Color, Paint};
use crate::events::Location; use crate::events::Location;
use crate::events::handler::InnerEventHandlerDataset; use crate::events::handler::InnerEventHandlerDataset;
use crate::{NodeLayoutMap, NodePtr, CurrentRenderer, SharedNode, WeakNode}; use crate::{NodeLayoutMap, NodePtr, CurrentRenderer, SharedNode, WeakNode};
pub use taffy::style::Style as TaffyStyle; pub use taffy::style::Style as TaffyStyle;
use taffy::{Layout, Overflow, Size, TaffyTree}; use taffy::{Layout, Overflow, Point, Size, TaffyTree};
pub type CanvasRenderer = Canvas<CurrentRenderer>; pub type CanvasRenderer = Canvas<CurrentRenderer>;
@ -38,9 +38,9 @@ impl RenderContext {
pub fn fill_rect(&mut self, x: u32, y: u32, width: u32, height: u32, color: Color) { pub fn fill_rect(&mut self, x: u32, y: u32, width: u32, height: u32, color: Color) {
let transform = self.canvas.transform(); let transform = self.canvas.transform();
let x = transform[0] * x as f32 + transform[2] * y as f32 + transform[4]; let x = transform[0] * x as f32 + transform[2] * y as f32 + transform[4];
let y = transform[1] * x as f32 + transform[3] * y as f32 + transform[5]; let y = transform[1] * x + transform[3] * y as f32 + transform[5];
let width = transform[0] * width as f32 + transform[2] * height as f32; let width = transform[0] * width as f32 + transform[2] * height as f32;
let height = transform[1] * width as f32 + transform[3] * height as f32; let height = transform[1] * width + transform[3] * height as f32;
self.canvas.clear_rect(x as u32, y as u32, width as u32, height as u32, color); self.canvas.clear_rect(x as u32, y as u32, width as u32, height as u32, color);
} }
} }
@ -50,10 +50,34 @@ pub enum Cursor {
#[default] #[default]
Default Default
} }
#[derive(Clone, Default, Debug)]
/// Transform is handled by UI lib - components shouldn't need to read this.
pub struct Transform {
/// Translation in x and y direction (in pixels; scaled by parents)
pub position: Point<f32>,
/// Scale in x and y direction
pub scale: Size<f32>,
/// Rotation in radians
pub rotation: f32
}
/// Styles for the node. Note that the styles aren't inherited (yet?)
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
pub struct Style { pub struct Style {
pub layout: TaffyStyle, pub layout: TaffyStyle,
pub cursor: Cursor pub cursor: Cursor,
pub background: Option<Paint>,
/// defaults to black
pub text_fill: Option<Paint>,
/// font size in pixels. Default is 16
pub font_size: Option<f32>,
/// multiplier of line height in relation to font size. Default is 1.2
pub line_height: Option<f32>,
/// border radius in pixels
pub border_radius: f32,
/// Various transformation (position, scale and rotation)
pub transform: Option<Transform>
} }
type NodeChildren = Vec<SharedNode>; type NodeChildren = Vec<SharedNode>;
@ -105,7 +129,7 @@ pub enum ChildAddError {
/// - [`Node::render_pre_children`] is called /// - [`Node::render_pre_children`] is called
/// - children are rendered /// - children are rendered
/// - [`Node::render_post_children`] is called /// - [`Node::render_post_children`] is called
pub trait Node: Debug { pub trait Node: Debug + Send {
/// Return style. /// Return style.
///insert ///insert
/// If you're using [`Style`] in your struct directly, your implementation can be as simple as: /// If you're using [`Style`] in your struct directly, your implementation can be as simple as:
@ -275,7 +299,7 @@ pub trait ToShared {
impl<T: Node + 'static> ToShared for T { impl<T: Node + 'static> ToShared for T {
fn to_shared(self) -> SharedNode { fn to_shared(self) -> SharedNode {
Arc::new(RwLock::new(self)) Arc::new(Mutex::new(self))
} }
} }
@ -283,7 +307,7 @@ impl<T: Node + 'static> ToShared for T {
/// The target element should be the last one in path (event handlers are ran in reverse order) /// The target element should be the last one in path (event handlers are ran in reverse order)
pub(crate) fn run_event_handlers(path: Vec<SharedNode>, event: crate::events::NodeEvent) { pub(crate) fn run_event_handlers(path: Vec<SharedNode>, event: crate::events::NodeEvent) {
for node in path.iter().rev() { for node in path.iter().rev() {
let node = node.read().unwrap(); let node = node.lock().unwrap();
if let Some(handlers) = node.event_handlers() { if let Some(handlers) = node.event_handlers() {
drop(node); drop(node);
for handler in handlers.lock().unwrap().values_mut() { for handler in handlers.lock().unwrap().values_mut() {
@ -294,7 +318,7 @@ pub(crate) fn run_event_handlers(path: Vec<SharedNode>, event: crate::events::No
} }
pub(crate) fn run_single_event_handlers(node: SharedNode, event: crate::events::NodeEvent) { pub(crate) fn run_single_event_handlers(node: SharedNode, event: crate::events::NodeEvent) {
let node = node.read().unwrap(); let node = node.lock().unwrap();
if let Some(handlers) = node.event_handlers() { if let Some(handlers) = node.event_handlers() {
drop(node); drop(node);
for handler in handlers.lock().unwrap().values_mut() { for handler in handlers.lock().unwrap().values_mut() {
@ -305,7 +329,7 @@ pub(crate) fn run_single_event_handlers(node: SharedNode, event: crate::events::
/// Attempts to get path to the element at the target location. Assumes elements are always inside their parents. /// Attempts to get path to the element at the target location. Assumes elements are always inside their parents.
pub(crate) fn get_element_at(node: &SharedNode, context: &RenderContext, location: Location) -> Option<Vec<SharedNode>> { pub(crate) fn get_element_at(node: &SharedNode, context: &RenderContext, location: Location) -> Option<Vec<SharedNode>> {
let node_borrowed = node.read().unwrap(); let node_borrowed = node.lock().unwrap();
let children = node_borrowed.children(); let children = node_borrowed.children();
let taffy_node = context.node_layout.get(node); let taffy_node = context.node_layout.get(node);
let taffy_node = match taffy_node { let taffy_node = match taffy_node {
@ -340,7 +364,7 @@ pub(crate) fn update_taffynode_children(node: &SharedNode, context: &mut RenderC
Some(taffy_node) => taffy_node, Some(taffy_node) => taffy_node,
None => { None => {
let taffy_node = context.taffy.new_leaf_with_context( let taffy_node = context.taffy.new_leaf_with_context(
node.read().unwrap().style().layout.to_owned(), node.lock().unwrap().style().layout.to_owned(),
Arc::downgrade(node) Arc::downgrade(node)
).unwrap(); ).unwrap();
context.node_layout.insert(node.clone(), taffy_node); context.node_layout.insert(node.clone(), taffy_node);
@ -350,13 +374,13 @@ pub(crate) fn update_taffynode_children(node: &SharedNode, context: &mut RenderC
let taffy_node = taffy_node.to_owned(); let taffy_node = taffy_node.to_owned();
match node.read().unwrap().children() { match node.lock().unwrap().children() {
None => {}, None => {},
Some(children) => { Some(children) => {
let mut t_children = Vec::with_capacity(children.len()); let mut t_children = Vec::with_capacity(children.len());
for child in children { for child in children {
t_children.push(update_taffynode_children(child, context).to_owned()); t_children.push(update_taffynode_children(child, context).to_owned());
child.write().unwrap().set_parent(Some(Arc::downgrade(node))); child.lock().unwrap().set_parent(Some(Arc::downgrade(node)));
} }
context.taffy.set_children(taffy_node, t_children.as_slice()).unwrap(); context.taffy.set_children(taffy_node, t_children.as_slice()).unwrap();
} }
@ -366,13 +390,18 @@ pub(crate) fn update_taffynode_children(node: &SharedNode, context: &mut RenderC
} }
pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext) { pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext) {
let read_node = node.read().unwrap(); let read_node = node.lock().unwrap();
let styles = read_node.style(); let styles = read_node.style();
let taffy_node = context.node_layout.get(node).unwrap(); let taffy_node = context.node_layout.get(node).unwrap();
let layout = *context.taffy.layout(*taffy_node).unwrap(); let layout = *context.taffy.layout(*taffy_node).unwrap();
let sself = node.clone(); let sself = node.clone();
context.canvas.save(); context.canvas.save();
context.canvas.translate(layout.location.x, layout.location.y); let offset = styles.transform.as_ref().map(|t| (t.position.x, t.position.y)).unwrap_or((0., 0.));
context.canvas.translate(layout.location.x + offset.0, layout.location.y + offset.1);
if let Some(transform) = &styles.transform {
context.canvas.scale(transform.scale.width, transform.scale.height);
context.canvas.rotate(transform.rotation);
}
let clip_width = matches!(styles.layout.overflow.x, Overflow::Hidden | Overflow::Clip); let clip_width = matches!(styles.layout.overflow.x, Overflow::Hidden | Overflow::Clip);
let clip_height = matches!(styles.layout.overflow.y, Overflow::Hidden | Overflow::Clip); let clip_height = matches!(styles.layout.overflow.y, Overflow::Hidden | Overflow::Clip);
if clip_width || clip_height { if clip_width || clip_height {
@ -384,18 +413,18 @@ pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext)
); );
} }
drop(read_node); drop(read_node);
sself.write().unwrap().render_pre_children(context, layout); sself.lock().unwrap().render_pre_children(context, layout);
if let Some(children) = sself.read().unwrap().children() { if let Some(children) = sself.lock().unwrap().children() {
for child in children { for child in children {
render_recursively(child, context); render_recursively(child, context);
} }
} }
sself.write().unwrap().render_post_children(context, layout); sself.lock().unwrap().render_post_children(context, layout);
context.canvas.restore(); context.canvas.restore();
} }
pub(crate) fn prepare_render_recursively(node: &SharedNode, context: &mut RenderContext) { pub(crate) fn prepare_render_recursively(node: &SharedNode, context: &mut RenderContext) {
let mut write_node = node.write().unwrap(); let mut write_node = node.lock().unwrap();
write_node.prepare_render(context); write_node.prepare_render(context);
if let Some(children) = write_node.children() { if let Some(children) = write_node.children() {
for child in children { for child in children {

View file

@ -1,12 +1,11 @@
use femtovg::{Color, Paint, Path}; use femtovg::{Color, Paint, Path};
use taffy::Layout; use taffy::{Layout, Size};
use crate::{nodes::{Node, NodeChildren, RenderContext, Style}, events::handler::EventHandlerDatabase, WeakNode, SharedNode}; use crate::{nodes::{Node, NodeChildren, RenderContext, Style}, events::handler::EventHandlerDatabase, WeakNode, SharedNode};
use crate::nodes::CanvasRenderer;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct Rectangle { pub struct Rectangle {
pub style: Style, pub style: Style,
pub fill: Paint,
pub radius: f32,
pub events: EventHandlerDatabase, pub events: EventHandlerDatabase,
pub parent: Option<WeakNode> pub parent: Option<WeakNode>
} }
@ -15,8 +14,6 @@ impl Rectangle {
pub fn new() -> Rectangle { pub fn new() -> Rectangle {
Rectangle { Rectangle {
style: Style::default(), style: Style::default(),
fill: Paint::color(Color::rgb(0, 0, 0)),
radius: 0.,
events: EventHandlerDatabase::default(), events: EventHandlerDatabase::default(),
parent: None parent: None
} }
@ -31,18 +28,7 @@ impl Node for Rectangle {
None None
} }
fn render_pre_children(&mut self, context: &mut RenderContext, layout: Layout) { fn render_pre_children(&mut self, context: &mut RenderContext, layout: Layout) {
let mut path = Path::new(); draw_rect(layout.size, self.style.background.as_ref().unwrap_or(&Paint::color(Color::black())), self.style.border_radius, &mut context.canvas);
path.rounded_rect(
0.,
0.,
layout.size.width,
layout.size.height,
self.radius
);
context.canvas.fill_path(
&path,
&self.fill
);
} }
fn event_handlers(&self) -> Option<crate::events::handler::InnerEventHandlerDataset> { fn event_handlers(&self) -> Option<crate::events::handler::InnerEventHandlerDataset> {
Some(self.events.handlers.clone()) Some(self.events.handlers.clone())
@ -57,3 +43,18 @@ impl Node for Rectangle {
} }
} }
} }
pub fn draw_rect(size: Size<f32>, fill: &Paint, radius: f32, canvas: &mut CanvasRenderer) {
let mut path = Path::new();
path.rounded_rect(
0.,
0.,
size.width,
size.height,
radius
);
canvas.fill_path(
&path,
fill
);
}

View file

@ -1,10 +1,10 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, RwLock};
use crate::{events::handler::EventHandlerDatabase, SharedNode, WeakNode, FONT_SYSTEM}; use crate::{events::handler::EventHandlerDatabase, SharedNode, WeakNode, FONT_SYSTEM};
use super::{text_render_cache::RENDER_CACHE, Node, NodeChildren, Style, MeasureContext, RenderContext}; use super::{text_render_cache::RENDER_CACHE, Node, NodeChildren, Style, MeasureContext, RenderContext};
use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Stretch}; use cosmic_text::{Attrs, Buffer, Metrics, Shaping};
use femtovg::{Color, Paint, Path};
use taffy::{AvailableSpace, Size}; use taffy::{AvailableSpace, Size};
use femtovg::{Color, Paint, Path};
use crate::nodes::primitives::draw_rect;
use crate::nodes::text_render_cache::TextConfig; use crate::nodes::text_render_cache::TextConfig;
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -13,16 +13,13 @@ pub struct Text {
pub text: String, pub text: String,
pub events: EventHandlerDatabase, pub events: EventHandlerDatabase,
pub parent: Option<WeakNode>, pub parent: Option<WeakNode>,
pub metrics: Metrics, pub buffer: Option<Buffer>
pub buffer: Option<Buffer>,
pub paint: Paint
} }
impl Text { impl Text {
pub fn new(text: String, metrics: Metrics) -> Text { pub fn new(text: String, metrics: Metrics) -> Text {
Text { Text {
text, text,
metrics,
..Default::default() ..Default::default()
} }
} }
@ -30,18 +27,18 @@ impl Text {
self.text = text; self.text = text;
self self
} }
pub fn metrics(mut self, metrics: Metrics) -> Self {
self.metrics = metrics;
self
}
pub fn paint(mut self, paint: Paint) -> Self {
self.paint = paint;
self
}
pub fn style(mut self, style: Style) -> Self { pub fn style(mut self, style: Style) -> Self {
self.style = style; self.style = style;
self self
} }
fn get_metrics(&self) -> Metrics {
let fontSize = self.style.font_size.unwrap_or(16.);
Metrics {
font_size: fontSize,
line_height: fontSize * self.style.line_height.unwrap_or(1.2)
}
}
} }
impl Node for Text { impl Node for Text {
@ -55,7 +52,7 @@ impl Node for Text {
fn prepare_render(&mut self, _context: &mut RenderContext) { fn prepare_render(&mut self, _context: &mut RenderContext) {
if let None = self.buffer { if let None = self.buffer {
self.buffer = Some(Buffer::new(&mut FONT_SYSTEM.lock().unwrap(), self.metrics)); self.buffer = Some(Buffer::new(&mut FONT_SYSTEM.lock().unwrap(), self.get_metrics()));
} }
let buf = self.buffer.as_mut().unwrap(); let buf = self.buffer.as_mut().unwrap();
let mut font = FONT_SYSTEM.lock().unwrap(); let mut font = FONT_SYSTEM.lock().unwrap();
@ -63,6 +60,8 @@ impl Node for Text {
} }
fn render_pre_children(&mut self, context: &mut super::RenderContext, layout: taffy::prelude::Layout) { fn render_pre_children(&mut self, context: &mut super::RenderContext, layout: taffy::prelude::Layout) {
if let Some(background) = &self.style.background { draw_rect(layout.size, background, self.style.border_radius, &mut context.canvas); }
let metrics = self.get_metrics();
// this can crash, but it should crash earlier during measure -> see the comment there. // this can crash, but it should crash earlier during measure -> see the comment there.
let buf = self.buffer.as_mut().unwrap(); let buf = self.buffer.as_mut().unwrap();
let mut font = FONT_SYSTEM.lock().unwrap(); let mut font = FONT_SYSTEM.lock().unwrap();
@ -72,41 +71,17 @@ impl Node for Text {
); );
// the height * scale factor is an ugly hack to fix height of the text... not sure why it's wrong in the first place // the height * scale factor is an ugly hack to fix height of the text... not sure why it's wrong in the first place
buf.set_size(&mut font, layout.content_size.width - offset_size.0, (layout.content_size.height * context.scale_factor) - offset_size.1); buf.set_size(&mut font, layout.content_size.width - offset_size.0, (layout.content_size.height * context.scale_factor) - offset_size.1);
buf.set_metrics(&mut font, self.metrics.scale(context.scale_factor)); buf.set_metrics(&mut font, metrics.scale(context.scale_factor));
// fill_to_cmds requires FONT_SYSTEM lock. // fill_to_cmds requires FONT_SYSTEM lock.
drop(font); drop(font);
let mut path = Path::new();
path.rounded_rect(
0.,
0.,
layout.size.width,
layout.size.height,
0.
);
context.canvas.fill_path(
&path,
&Paint::color(Color::rgb(255, 0, 0))
);
let position = ( let position = (
layout.padding.left + layout.border.left, layout.padding.left + layout.border.left,
layout.padding.top + layout.border.top layout.padding.top + layout.border.top
); );
let mut path = Path::new();
path.rounded_rect(
position.0,
position.1,
layout.content_size.width - offset_size.0,
layout.content_size.height - offset_size.1,
0.
);
context.canvas.fill_path(
&path,
&Paint::color(Color::rgb(0, 0, 255))
);
let cmds = RENDER_CACHE.lock().unwrap() let cmds = RENDER_CACHE.lock().unwrap()
.fill_to_cmds(&mut context.canvas, buf, position, context.scale_factor, TextConfig { hint: false, subpixel: false }) .fill_to_cmds(&mut context.canvas, buf, position, context.scale_factor, TextConfig { hint: false, subpixel: false })
.unwrap(); .unwrap();
context.canvas.draw_glyph_commands(cmds, &self.paint, context.scale_factor); context.canvas.draw_glyph_commands(cmds, self.style.text_fill.as_ref().unwrap_or(&Paint::color(Color::black())), context.scale_factor);
} }
fn measure(&mut self, context: &mut MeasureContext, known_dimensions: Size<Option<f32>>, available_space: Size<AvailableSpace>) -> Size<f32> { fn measure(&mut self, context: &mut MeasureContext, known_dimensions: Size<Option<f32>>, available_space: Size<AvailableSpace>) -> Size<f32> {
@ -115,12 +90,13 @@ impl Node for Text {
AvailableSpace::MaxContent => f32::INFINITY, AvailableSpace::MaxContent => f32::INFINITY,
AvailableSpace::Definite(width) => width, AvailableSpace::Definite(width) => width,
}); });
let metrics = self.get_metrics();
// yes, this can crash if someone removes `buffer` during render from another thread. // yes, this can crash if someone removes `buffer` during render from another thread.
// though they're asking for it, so let them crash. // though they're asking for it, so let them crash.
let buf = self.buffer.as_mut().unwrap(); let buf = self.buffer.as_mut().unwrap();
let mut font = FONT_SYSTEM.lock().unwrap(); let mut font = FONT_SYSTEM.lock().unwrap();
buf.set_size(&mut font, width_constraint, f32::INFINITY); buf.set_size(&mut font, width_constraint, f32::INFINITY);
buf.set_metrics(&mut font, self.metrics.scale(context.scale_factor)); buf.set_metrics(&mut font, metrics.scale(context.scale_factor));
// Compute layout // Compute layout
buf.shape_until_scroll(&mut font, true); buf.shape_until_scroll(&mut font, true);

View file

@ -1,7 +1,85 @@
use proc_macro2::{Span, TokenStream, TokenTree}; use proc_macro2::{Span, TokenStream, TokenTree};
use quote::{IdentFragment, quote_spanned}; use quote::{IdentFragment, quote, quote_spanned, ToTokens};
use quote::spanned::Spanned; use quote::spanned::Spanned;
macro_rules! impl_enum_totokens {
(
$name:ident,
$prefix:path
$(, $( $variant:ident ),+)?
$(; $( $qvariant:ident ( $( $qual:ident ),+ ) ),* )?
$(| $( $qvariant2:ident ( $( $qual2:ident => $qtype:tt ),+ ) ),* )?
) => {
impl ToTokens for $name {
fn to_tokens(&self, stream: &mut TokenStream) {
stream.extend(match self {
$(
$(
$name::$variant => {
quote! {
$prefix::$variant
}
}
),+
)?
$(
$(
$name::$qvariant($($qual),+) => {
quote! {
$prefix::$qvariant($(#$qual),+)
}
}
)+
)?
$(
$(
$name::$qvariant2($($qual2),+) => {
quote! {
$prefix::$qvariant2($( $qtype ),+)
}
}
)+
)?
});
}
}
}
}
macro_rules! impl_struct_usersettable_totokens {
(
$name:ident,
$prefix:path,
$($variant:ident),+
$(| $( $qvariant:ident => $qtype:tt ),* )?
) => {
impl ToTokens for $name {
fn to_tokens(&self, stream: &mut TokenStream) {
let $name { $($variant),+, .. } = self;
let mut substream = TokenStream::new();
$(
if !$variant.is_empty() {
substream.extend(quote! { $variant: #$variant, });
}
)+
$(
$(
if !$qvariant.is_empty() {
substream.extend(quote! { $qvariant: #$qtype, });
}
),*
)?
stream.extend(quote! {
$prefix {
#substream
..Default::default()
}
});
}
}
}
}
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum UserSettable<T> { enum UserSettable<T> {
Value(T), Value(T),
@ -28,6 +106,14 @@ impl<T> UserSettable<T> {
} }
} }
} }
fn is_empty(&self) -> bool {
match self {
UserSettable::Value(_) => false,
UserSettable::Arbitrary(_) => false,
UserSettable::None => true
}
}
} }
impl<T: Default> UserSettable<T> { impl<T: Default> UserSettable<T> {
@ -48,18 +134,79 @@ impl<T: Default> UserSettable<T> {
} }
} }
impl<T: ToTokens> ToTokens for UserSettable<T> {
fn to_tokens(&self, stream: &mut TokenStream) {
stream.extend(match self {
UserSettable::Value(value) => value.to_token_stream(),
UserSettable::Arbitrary(stream) => stream.clone(),
UserSettable::None => quote! { Default::default() }
});
}
}
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
struct Point<T> { struct Point<T> {
x: UserSettable<T>, x: UserSettable<T>,
y: UserSettable<T> y: UserSettable<T>
} }
impl<T: ToTokens> ToTokens for Point<T> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Point { x, y } = self;
stream.extend(quote! {
mangui::taffy::Point { x: #x, y: #y }
});
}
}
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
struct Size<T> { struct Size<T> {
width: UserSettable<T>, width: UserSettable<T>,
height: UserSettable<T> height: UserSettable<T>
} }
impl<T: ToTokens> ToTokens for Size<T> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Size { width, height } = self;
stream.extend(quote! {
mangui::taffy::Size { width: #width, height: #height }
});
}
}
#[derive(Clone, Default, Debug)]
struct Rect<T> {
pub left: UserSettable<T>,
pub right: UserSettable<T>,
pub top: UserSettable<T>,
pub bottom: UserSettable<T>,
}
impl<T: ToTokens> ToTokens for Rect<T> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Rect { left, right, top, bottom } = self;
let mut substream = TokenStream::new();
if !left.is_empty() {
substream.extend(quote! { left: #left, });
}
if !right.is_empty() {
substream.extend(quote! { right: #right, });
}
if !top.is_empty() {
substream.extend(quote! { top: #top, });
}
if !bottom.is_empty() {
substream.extend(quote! { bottom: #bottom, });
}
if left.is_empty() || right.is_empty() || top.is_empty() || bottom.is_empty() {
substream.extend(quote! { ..Rect::zero() });
}
stream.extend(quote! {
mangui::taffy::geometry::Rect { #substream }
});
}
}
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
struct Color { struct Color {
r: f32, r: f32,
@ -68,6 +215,15 @@ struct Color {
a: f32 a: f32
} }
impl ToTokens for Color {
fn to_tokens(&self, stream: &mut TokenStream) {
let Color { r, g, b, a } = self;
stream.extend(quote! {
mangui::femtovg::Color::rgba(#r, #g, #b, #a)
});
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum Paint { enum Paint {
Color(Color) Color(Color)
@ -84,12 +240,26 @@ impl Default for Paint {
} }
} }
impl ToTokens for Paint {
fn to_tokens(&self, stream: &mut TokenStream) {
stream.extend(match self {
Paint::Color(color) => {
quote! {
mangui::femtovg::Paint::color(#color)
}
}
});
}
}
#[derive(Copy, Clone, Default, Debug)] #[derive(Copy, Clone, Default, Debug)]
enum Cursor { enum Cursor {
#[default] #[default]
Default Default
} }
impl_enum_totokens!(Cursor, mangui::femtovg::Cursor, Default);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
struct Transform { struct Transform {
pub position: UserSettable<Point<f32>>, pub position: UserSettable<Point<f32>>,
@ -97,6 +267,8 @@ struct Transform {
pub rotation: UserSettable<f32> pub rotation: UserSettable<f32>
} }
impl_struct_usersettable_totokens!(Transform, mangui::nodes::Transform, position, scale, rotation);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
struct Style { struct Style {
pub layout: UserSettable<TaffyStyle>, pub layout: UserSettable<TaffyStyle>,
@ -109,13 +281,11 @@ struct Style {
pub transform: UserSettable<Transform> pub transform: UserSettable<Transform>
} }
#[derive(Clone, Default, Debug)] impl_struct_usersettable_totokens!(
struct Rect<T> { Style,
pub left: UserSettable<T>, mangui::nodes::Style,
pub right: UserSettable<T>, layout, cursor, background, text_fill, font_size, line_height, border_radius, transform
pub top: UserSettable<T>, );
pub bottom: UserSettable<T>,
}
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum Position { enum Position {
@ -124,6 +294,8 @@ enum Position {
Absolute, Absolute,
} }
impl_enum_totokens!(Position, mangui::taffy::Position, Relative, Absolute);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum Display { enum Display {
Block, Block,
@ -133,6 +305,8 @@ enum Display {
None, None,
} }
impl_enum_totokens!(Display, mangui::taffy::Display, Block, Flex, Grid, None);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum Overflow { enum Overflow {
#[default] #[default]
@ -142,6 +316,8 @@ enum Overflow {
Scroll, Scroll,
} }
impl_enum_totokens!(Overflow, mangui::taffy::Overflow, Visible, Clip, Hidden, Scroll);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum LengthPercentageAuto { enum LengthPercentageAuto {
Length(f32), Length(f32),
@ -150,6 +326,8 @@ enum LengthPercentageAuto {
Auto, Auto,
} }
impl_enum_totokens!(LengthPercentageAuto, mangui::taffy::LengthPercentageAuto, Auto; Length(i), Percent(i));
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum Dimension { enum Dimension {
Length(f32), Length(f32),
@ -158,12 +336,16 @@ enum Dimension {
Auto, Auto,
} }
impl_enum_totokens!(Dimension, mangui::taffy::Dimension, Auto; Length(i), Percent(i));
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum LengthPercentage { enum LengthPercentage {
Length(f32), Length(f32),
Percent(f32), Percent(f32),
} }
impl_enum_totokens!(LengthPercentage, mangui::taffy::LengthPercentage; Length(i), Percent(i));
impl Default for LengthPercentage { impl Default for LengthPercentage {
fn default() -> Self { fn default() -> Self {
LengthPercentage::Length(0.) LengthPercentage::Length(0.)
@ -181,6 +363,8 @@ enum AlignItems {
Stretch, Stretch,
} }
impl_enum_totokens!(AlignItems, mangui::taffy::AlignItems, Start, End, FlexStart, FlexEnd, Center, Baseline, Stretch);
type AlignSelf = AlignItems; type AlignSelf = AlignItems;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -197,6 +381,8 @@ enum AlignContent {
} }
type JustifyContent = AlignContent; type JustifyContent = AlignContent;
impl_enum_totokens!(AlignContent, mangui::taffy::AlignContent, Start, End, FlexStart, FlexEnd, Center, Stretch, SpaceBetween, SpaceEvenly, SpaceAround);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum FlexDirection { enum FlexDirection {
#[default] #[default]
@ -206,6 +392,8 @@ enum FlexDirection {
ColumnReverse, ColumnReverse,
} }
impl_enum_totokens!(FlexDirection, mangui::taffy::FlexDirection, Row, Column, RowReverse, ColumnReverse);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum FlexWrap { enum FlexWrap {
#[default] #[default]
@ -214,6 +402,8 @@ enum FlexWrap {
WrapReverse, WrapReverse,
} }
impl_enum_totokens!(FlexWrap, mangui::taffy::FlexWrap, NoWrap, Wrap, WrapReverse);
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
struct Line<T> { struct Line<T> {
@ -221,11 +411,30 @@ struct Line<T> {
pub end: T, pub end: T,
} }
impl<T: ToTokens> ToTokens for Line<T> {
fn to_tokens(&self, stream: &mut TokenStream) {
let Line { start, end } = self;
stream.extend(quote! {
mangui::taffy::Line::new(#start, #end)
});
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct MinMax<Min, Max> { struct MinMax<Min, Max> {
pub min: Min, pub min: Min,
pub max: Max, pub max: Max,
} }
impl<Min: ToTokens, Max: ToTokens> ToTokens for MinMax<Min, Max> {
fn to_tokens(&self, stream: &mut TokenStream) {
let MinMax { min, max } = self;
stream.extend(quote! {
mangui::taffy::MinMax::new(#min, #max)
});
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum MinTrackSizingFunction { enum MinTrackSizingFunction {
Fixed(LengthPercentage), Fixed(LengthPercentage),
@ -233,6 +442,9 @@ enum MinTrackSizingFunction {
MaxContent, MaxContent,
Auto, Auto,
} }
impl_enum_totokens!(MinTrackSizingFunction, mangui::taffy::MinTrackSizingFunction, MinContent, MaxContent, Auto; Fixed(i));
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum MaxTrackSizingFunction { enum MaxTrackSizingFunction {
Fixed(LengthPercentage), Fixed(LengthPercentage),
@ -243,6 +455,9 @@ enum MaxTrackSizingFunction {
Fraction(f32), Fraction(f32),
} }
type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>; type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
impl_enum_totokens!(MaxTrackSizingFunction, mangui::taffy::MaxTrackSizingFunction, MinContent, MaxContent, Auto; FitContent(i), Fraction(i), Fixed(i));
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum GridTrackRepetition { enum GridTrackRepetition {
AutoFill, AutoFill,
@ -250,12 +465,16 @@ enum GridTrackRepetition {
Count(u16), Count(u16),
} }
impl_enum_totokens!(GridTrackRepetition, mangui::taffy::GridTrackRepetition, AutoFill, AutoFit; Count(i));
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum TrackSizingFunction { enum TrackSizingFunction {
Single(NonRepeatedTrackSizingFunction), Single(NonRepeatedTrackSizingFunction),
Repeat(GridTrackRepetition, Vec<NonRepeatedTrackSizingFunction>), Repeat(GridTrackRepetition, Vec<NonRepeatedTrackSizingFunction>),
} }
impl_enum_totokens!(TrackSizingFunction, mangui::taffy::TrackSizingFunction; Single(i) | Repeat(repeat => (#repeat), functions => (vec![#( #functions ),*])));
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum GridAutoFlow { enum GridAutoFlow {
#[default] #[default]
@ -265,8 +484,20 @@ enum GridAutoFlow {
ColumnDense, ColumnDense,
} }
impl_enum_totokens!(GridAutoFlow, mangui::taffy::GridAutoFlow, Row, Column, RowDense, ColumnDense);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct GridLine(i16); struct GridLine(i16);
impl ToTokens for GridLine {
fn to_tokens(&self, stream: &mut TokenStream) {
let GridLine(line) = self;
stream.extend(quote! {
mangui::taffy::GridLine::new(#line)
});
}
}
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
enum GridPlacement { enum GridPlacement {
#[default] #[default]
@ -274,6 +505,10 @@ enum GridPlacement {
Line(GridLine), Line(GridLine),
Span(u16), Span(u16),
} }
impl_enum_totokens!(GridPlacement, mangui::taffy::GridPlacement, Auto; Line(i), Span(i));
/// Styles for positioning. Note that grid template rows/columns and auto rows/columns are not supported yet (generated)
#[derive(Clone, Default, Debug)] #[derive(Clone, Default, Debug)]
struct TaffyStyle { struct TaffyStyle {
pub display: UserSettable<Display>, pub display: UserSettable<Display>,
@ -309,6 +544,17 @@ struct TaffyStyle {
pub grid_column: UserSettable<Line<GridPlacement>>, pub grid_column: UserSettable<Line<GridPlacement>>,
} }
impl_struct_usersettable_totokens!(
TaffyStyle,
mangui::taffy::Style,
display, overflow, scrollbar_width, position, inset, size, min_size, max_size,
aspect_ratio, margin, padding, border,
align_items, align_self, justify_items, justify_self, align_content, justify_content,
gap,
flex_direction, flex_wrap, flex_basis, flex_grow, flex_shrink,
grid_auto_flow, grid_row, grid_column
);
trait ValueToUserSettable<T> { trait ValueToUserSettable<T> {
fn to_user_settable(self, span: Span, inverse: bool) -> Result<UserSettable<T>, RuleParseError>; fn to_user_settable(self, span: Span, inverse: bool) -> Result<UserSettable<T>, RuleParseError>;
} }
@ -479,7 +725,6 @@ struct RuleParseError {
#[proc_macro] #[proc_macro]
pub fn uno(item: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn uno(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item = TokenStream::from(item); let item = TokenStream::from(item);
let mut output = TokenStream::new();
dbg!(&item); dbg!(&item);
let rules = parse_rules(item); let rules = parse_rules(item);
@ -503,9 +748,9 @@ pub fn uno(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
} }
}; };
dbg!(style); dbg!(&style);
output.into() style.to_token_stream().into()
} }
fn process_rules(rules: Vec<Rule>) -> Result<Style, RuleParseError> { fn process_rules(rules: Vec<Rule>) -> Result<Style, RuleParseError> {