Skip to content

Commit

Permalink
calculate layout with taffy
Browse files Browse the repository at this point in the history
  • Loading branch information
Sharktheone committed Mar 28, 2024
1 parent a44646e commit 427f454
Show file tree
Hide file tree
Showing 12 changed files with 2,150 additions and 29 deletions.
996 changes: 990 additions & 6 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions crates/gosub_rendering/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ license = "MIT"

[dependencies]
gosub_html5 = { path = "../gosub_html5" }
gosub_styling = { path = "../gosub_styling" }
taffy = "0.4.1"
anyhow = "1.0.81"
regex = "1.10.4"
56 changes: 56 additions & 0 deletions crates/gosub_rendering/src/layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use taffy::prelude::*;

use gosub_html5::node::NodeId as GosubID;
use gosub_styling::render_tree::{RenderNodeData, RenderTree};

use crate::style::get_style_from_node;

pub fn generate_taffy_tree(rt: &mut RenderTree) -> anyhow::Result<(TaffyTree<GosubID>, NodeId)> {
let mut tree: TaffyTree<GosubID> = TaffyTree::with_capacity(rt.nodes.len());

rt.get_root();

let root = add_children_to_tree(rt, &mut tree, rt.root)?;

Ok((tree, root))
}

fn add_children_to_tree(
rt: &mut RenderTree,
tree: &mut TaffyTree<GosubID>,
node_id: GosubID,
) -> anyhow::Result<NodeId> {
let Some(node_children) = rt.get_children(node_id) else {
return Err(anyhow::anyhow!("Node not found {:?}", node_id));
};

let mut children = Vec::with_capacity(node_children.len());

//clone, so we can drop the borrow of RT, we would be copying the NodeID anyway, so it's not a big deal (only a few bytes)
for child in node_children.clone() {
match add_children_to_tree(rt, tree, child) {
Ok(node) => children.push(node),
Err(e) => eprintln!("Error adding child to tree: {:?}", e),
}
}

let Some(node) = rt.get_node_mut(node_id) else {
return Err(anyhow::anyhow!("Node not found"));
};

let style = get_style_from_node(node);

let node = rt.get_node(node_id).unwrap();
if let RenderNodeData::Text(text) = &node.data {
println!("Text: {:?}", text.text);
println!("Style: {:?}", style.size);
}

let node = tree
.new_with_children(style, &children)
.map_err(|e| anyhow::anyhow!(e.to_string()))?;

tree.set_node_context(node, Some(node_id))?;

Ok(node)
}
2 changes: 2 additions & 0 deletions crates/gosub_rendering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
//! This crate supplies functionality to render CSSOM and DOM trees into a viewable display.
//!

pub mod layout;
pub mod render_tree;
pub mod style;
74 changes: 74 additions & 0 deletions crates/gosub_rendering/src/style.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
mod parse;
mod parse_properties;

use gosub_styling::render_tree::RenderTreeNode;
use taffy::Style;

const SCROLLBAR_WIDTH: f32 = 16.0;

pub fn get_style_from_node(node: &mut RenderTreeNode) -> Style {
let display = parse_properties::parse_display(node);
let overflow = parse_properties::parse_overflow(node);
let position = parse_properties::parse_position(node);
let inset = parse_properties::parse_inset(node);
let size = parse_properties::parse_size(node);
let min_size = parse_properties::parse_min_size(node);
let max_size = parse_properties::parse_max_size(node);
let aspect_ratio = parse_properties::parse_aspect_ratio(node);
let margin = parse_properties::parse_margin(node);
let padding = parse_properties::parse_padding(node);
let border = parse_properties::parse_border(node);
let align_items = parse_properties::parse_align_items(node);
let align_self = parse_properties::parse_align_self(node);
let justify_items = parse_properties::parse_justify_items(node);
let justify_self = parse_properties::parse_justify_self(node);
let align_content = parse_properties::parse_align_content(node);
let justify_content = parse_properties::parse_justify_content(node);
let gap = parse_properties::parse_gap(node);
let flex_direction = parse_properties::parse_flex_direction(node);
let flex_wrap = parse_properties::parse_flex_wrap(node);
let flex_basis = parse_properties::parse_flex_basis(node);
let flex_grow = parse_properties::parse_flex_grow(node);
let flex_shrink = parse_properties::parse_flex_shrink(node);
let grid_template_rows = parse_properties::parse_grid_template_rows(node);
let grid_template_columns = parse_properties::parse_grid_template_columns(node);
let grid_auto_rows = parse_properties::parse_grid_auto_rows(node);
let grid_auto_columns = parse_properties::parse_grid_auto_columns(node);
let grid_auto_flow = parse_properties::parse_grid_auto_flow(node);
let grid_row = parse_properties::parse_grid_row(node);
let grid_column = parse_properties::parse_grid_column(node);

Style {
display,
overflow,
scrollbar_width: 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_template_rows,
grid_template_columns,
grid_auto_rows,
grid_auto_columns,
grid_auto_flow,
grid_row,
grid_column,
}
}
188 changes: 188 additions & 0 deletions crates/gosub_rendering/src/style/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
use taffy::prelude::*;
use taffy::{
AlignContent, AlignItems, Dimension, GridPlacement, LengthPercentage, LengthPercentageAuto,
TrackSizingFunction,
};

use gosub_styling::css_values::CssValue;
use gosub_styling::render_tree::{RenderNodeData, RenderTreeNode};

pub(crate) fn parse_len(node: &mut RenderTreeNode, name: &str) -> LengthPercentage {
let Some(property) = node.get_property(name) else {
return LengthPercentage::Length(0.0);
};

property.compute_value();

match &property.actual {
CssValue::Percentage(value) => LengthPercentage::Percent(*value),
CssValue::Unit(..) => LengthPercentage::Length(property.actual.unit_to_px()),
CssValue::String(_) => LengthPercentage::Length(property.actual.unit_to_px()), //HACK
_ => LengthPercentage::Length(0.0),
}
}

pub(crate) fn parse_len_auto(node: &mut RenderTreeNode, name: &str) -> LengthPercentageAuto {
let Some(property) = node.get_property(name) else {
return LengthPercentageAuto::Auto;
};

property.compute_value();

match &property.actual {
CssValue::String(value) => match value.as_str() {
"auto" => LengthPercentageAuto::Auto,
_ => LengthPercentageAuto::Length(property.actual.unit_to_px()), //HACK
},
CssValue::Percentage(value) => LengthPercentageAuto::Percent(*value),
CssValue::Unit(..) => LengthPercentageAuto::Length(property.actual.unit_to_px()),
_ => LengthPercentageAuto::Auto,
}
}

pub(crate) fn parse_dimension(node: &mut RenderTreeNode, name: &str) -> Dimension {
let mut auto = Dimension::Auto;
if let RenderNodeData::Text(text) = &node.data {
if name == "width" {
auto = Dimension::Length(text.width);
} else if name == "height" {
auto = Dimension::Length(text.height);
}
}

let Some(property) = node.get_property(name) else {
return auto;
};

property.compute_value();

if name == "width" {
println!("Width: {:?}", property.actual);
}

match &property.actual {
CssValue::String(value) => match value.as_str() {
"auto" => auto,
s if s.ends_with('%') => {
let value = s.trim_end_matches('%').parse::<f32>().unwrap_or(0.0);
Dimension::Percent(value)
}
_ => Dimension::Length(property.actual.unit_to_px()), //HACK
},
CssValue::Percentage(value) => Dimension::Percent(*value),
CssValue::Unit(..) => Dimension::Length(property.actual.unit_to_px()),
_ => auto,
}
}

pub(crate) fn parse_align_i(node: &mut RenderTreeNode, name: &str) -> Option<AlignItems> {
let display = node.get_property(name)?;
display.compute_value();

let CssValue::String(ref value) = display.actual else {
return None;
};

match value.as_str() {
"start" => Some(AlignItems::Start),
"end" => Some(AlignItems::End),
"flex-start" => Some(AlignItems::FlexStart),
"flex-end" => Some(AlignItems::FlexEnd),
"center" => Some(AlignItems::Center),
"baseline" => Some(AlignItems::Baseline),
"stretch" => Some(AlignItems::Stretch),
_ => None,
}
}

pub(crate) fn parse_align_c(node: &mut RenderTreeNode, name: &str) -> Option<AlignContent> {
let display = node.get_property(name)?;

display.compute_value();

let CssValue::String(ref value) = display.actual else {
return None;
};

match value.as_str() {
"start" => Some(AlignContent::Start),
"end" => Some(AlignContent::End),
"flex-start" => Some(AlignContent::FlexStart),
"flex-end" => Some(AlignContent::FlexEnd),
"center" => Some(AlignContent::Center),
"stretch" => Some(AlignContent::Stretch),
"space-between" => Some(AlignContent::SpaceBetween),
"space-around" => Some(AlignContent::SpaceAround),
_ => None,
}
}

pub(crate) fn parse_tracking_sizing_function(
node: &mut RenderTreeNode,
name: &str,
) -> Vec<TrackSizingFunction> {
let Some(display) = node.get_property(name) else {
return Vec::new();
};

display.compute_value();

let CssValue::String(ref _value) = display.actual else {
return Vec::new();
};

Vec::new() //TODO: Implement this
}

#[allow(dead_code)]
pub(crate) fn parse_non_repeated_tracking_sizing_function(
_node: &mut RenderTreeNode,
_name: &str,
) -> NonRepeatedTrackSizingFunction {
todo!("implement parse_non_repeated_tracking_sizing_function")
}

pub(crate) fn parse_grid_auto(
node: &mut RenderTreeNode,
name: &str,
) -> Vec<NonRepeatedTrackSizingFunction> {
let Some(display) = node.get_property(name) else {
return Vec::new();
};

display.compute_value();

let CssValue::String(ref _value) = display.actual else {
return Vec::new();
};

Vec::new() //TODO: Implement this
}

pub(crate) fn parse_grid_placement(node: &mut RenderTreeNode, name: &str) -> GridPlacement {
let Some(display) = node.get_property(name) else {
return GridPlacement::Auto;
};

display.compute_value();

match &display.actual {
CssValue::String(value) => {
if value.starts_with("span") {
let value = value.trim_start_matches("span").trim();

if let Ok(value) = value.parse::<u16>() {
GridPlacement::from_span(value)
} else {
GridPlacement::Auto
}
} else if let Ok(value) = value.parse::<i16>() {
GridPlacement::from_line_index(value)
} else {
GridPlacement::Auto
}
}
CssValue::Number(value) => GridPlacement::from_line_index(*value as i16),
_ => GridPlacement::Auto,
}
}
Loading

0 comments on commit 427f454

Please sign in to comment.