Skip to content

Commit 427f454

Browse files
committed
calculate layout with taffy
1 parent a44646e commit 427f454

File tree

12 files changed

+2150
-29
lines changed

12 files changed

+2150
-29
lines changed

Cargo.lock

Lines changed: 990 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/gosub_rendering/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ license = "MIT"
77

88
[dependencies]
99
gosub_html5 = { path = "../gosub_html5" }
10+
gosub_styling = { path = "../gosub_styling" }
11+
taffy = "0.4.1"
12+
anyhow = "1.0.81"
13+
regex = "1.10.4"

crates/gosub_rendering/src/layout.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use taffy::prelude::*;
2+
3+
use gosub_html5::node::NodeId as GosubID;
4+
use gosub_styling::render_tree::{RenderNodeData, RenderTree};
5+
6+
use crate::style::get_style_from_node;
7+
8+
pub fn generate_taffy_tree(rt: &mut RenderTree) -> anyhow::Result<(TaffyTree<GosubID>, NodeId)> {
9+
let mut tree: TaffyTree<GosubID> = TaffyTree::with_capacity(rt.nodes.len());
10+
11+
rt.get_root();
12+
13+
let root = add_children_to_tree(rt, &mut tree, rt.root)?;
14+
15+
Ok((tree, root))
16+
}
17+
18+
fn add_children_to_tree(
19+
rt: &mut RenderTree,
20+
tree: &mut TaffyTree<GosubID>,
21+
node_id: GosubID,
22+
) -> anyhow::Result<NodeId> {
23+
let Some(node_children) = rt.get_children(node_id) else {
24+
return Err(anyhow::anyhow!("Node not found {:?}", node_id));
25+
};
26+
27+
let mut children = Vec::with_capacity(node_children.len());
28+
29+
//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)
30+
for child in node_children.clone() {
31+
match add_children_to_tree(rt, tree, child) {
32+
Ok(node) => children.push(node),
33+
Err(e) => eprintln!("Error adding child to tree: {:?}", e),
34+
}
35+
}
36+
37+
let Some(node) = rt.get_node_mut(node_id) else {
38+
return Err(anyhow::anyhow!("Node not found"));
39+
};
40+
41+
let style = get_style_from_node(node);
42+
43+
let node = rt.get_node(node_id).unwrap();
44+
if let RenderNodeData::Text(text) = &node.data {
45+
println!("Text: {:?}", text.text);
46+
println!("Style: {:?}", style.size);
47+
}
48+
49+
let node = tree
50+
.new_with_children(style, &children)
51+
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
52+
53+
tree.set_node_context(node, Some(node_id))?;
54+
55+
Ok(node)
56+
}

crates/gosub_rendering/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
//! This crate supplies functionality to render CSSOM and DOM trees into a viewable display.
44
//!
55
6+
pub mod layout;
67
pub mod render_tree;
8+
pub mod style;

crates/gosub_rendering/src/style.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
mod parse;
2+
mod parse_properties;
3+
4+
use gosub_styling::render_tree::RenderTreeNode;
5+
use taffy::Style;
6+
7+
const SCROLLBAR_WIDTH: f32 = 16.0;
8+
9+
pub fn get_style_from_node(node: &mut RenderTreeNode) -> Style {
10+
let display = parse_properties::parse_display(node);
11+
let overflow = parse_properties::parse_overflow(node);
12+
let position = parse_properties::parse_position(node);
13+
let inset = parse_properties::parse_inset(node);
14+
let size = parse_properties::parse_size(node);
15+
let min_size = parse_properties::parse_min_size(node);
16+
let max_size = parse_properties::parse_max_size(node);
17+
let aspect_ratio = parse_properties::parse_aspect_ratio(node);
18+
let margin = parse_properties::parse_margin(node);
19+
let padding = parse_properties::parse_padding(node);
20+
let border = parse_properties::parse_border(node);
21+
let align_items = parse_properties::parse_align_items(node);
22+
let align_self = parse_properties::parse_align_self(node);
23+
let justify_items = parse_properties::parse_justify_items(node);
24+
let justify_self = parse_properties::parse_justify_self(node);
25+
let align_content = parse_properties::parse_align_content(node);
26+
let justify_content = parse_properties::parse_justify_content(node);
27+
let gap = parse_properties::parse_gap(node);
28+
let flex_direction = parse_properties::parse_flex_direction(node);
29+
let flex_wrap = parse_properties::parse_flex_wrap(node);
30+
let flex_basis = parse_properties::parse_flex_basis(node);
31+
let flex_grow = parse_properties::parse_flex_grow(node);
32+
let flex_shrink = parse_properties::parse_flex_shrink(node);
33+
let grid_template_rows = parse_properties::parse_grid_template_rows(node);
34+
let grid_template_columns = parse_properties::parse_grid_template_columns(node);
35+
let grid_auto_rows = parse_properties::parse_grid_auto_rows(node);
36+
let grid_auto_columns = parse_properties::parse_grid_auto_columns(node);
37+
let grid_auto_flow = parse_properties::parse_grid_auto_flow(node);
38+
let grid_row = parse_properties::parse_grid_row(node);
39+
let grid_column = parse_properties::parse_grid_column(node);
40+
41+
Style {
42+
display,
43+
overflow,
44+
scrollbar_width: SCROLLBAR_WIDTH,
45+
position,
46+
inset,
47+
size,
48+
min_size,
49+
max_size,
50+
aspect_ratio,
51+
margin,
52+
padding,
53+
border,
54+
align_items,
55+
align_self,
56+
justify_items,
57+
justify_self,
58+
align_content,
59+
justify_content,
60+
gap,
61+
flex_direction,
62+
flex_wrap,
63+
flex_basis,
64+
flex_grow,
65+
flex_shrink,
66+
grid_template_rows,
67+
grid_template_columns,
68+
grid_auto_rows,
69+
grid_auto_columns,
70+
grid_auto_flow,
71+
grid_row,
72+
grid_column,
73+
}
74+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
use taffy::prelude::*;
2+
use taffy::{
3+
AlignContent, AlignItems, Dimension, GridPlacement, LengthPercentage, LengthPercentageAuto,
4+
TrackSizingFunction,
5+
};
6+
7+
use gosub_styling::css_values::CssValue;
8+
use gosub_styling::render_tree::{RenderNodeData, RenderTreeNode};
9+
10+
pub(crate) fn parse_len(node: &mut RenderTreeNode, name: &str) -> LengthPercentage {
11+
let Some(property) = node.get_property(name) else {
12+
return LengthPercentage::Length(0.0);
13+
};
14+
15+
property.compute_value();
16+
17+
match &property.actual {
18+
CssValue::Percentage(value) => LengthPercentage::Percent(*value),
19+
CssValue::Unit(..) => LengthPercentage::Length(property.actual.unit_to_px()),
20+
CssValue::String(_) => LengthPercentage::Length(property.actual.unit_to_px()), //HACK
21+
_ => LengthPercentage::Length(0.0),
22+
}
23+
}
24+
25+
pub(crate) fn parse_len_auto(node: &mut RenderTreeNode, name: &str) -> LengthPercentageAuto {
26+
let Some(property) = node.get_property(name) else {
27+
return LengthPercentageAuto::Auto;
28+
};
29+
30+
property.compute_value();
31+
32+
match &property.actual {
33+
CssValue::String(value) => match value.as_str() {
34+
"auto" => LengthPercentageAuto::Auto,
35+
_ => LengthPercentageAuto::Length(property.actual.unit_to_px()), //HACK
36+
},
37+
CssValue::Percentage(value) => LengthPercentageAuto::Percent(*value),
38+
CssValue::Unit(..) => LengthPercentageAuto::Length(property.actual.unit_to_px()),
39+
_ => LengthPercentageAuto::Auto,
40+
}
41+
}
42+
43+
pub(crate) fn parse_dimension(node: &mut RenderTreeNode, name: &str) -> Dimension {
44+
let mut auto = Dimension::Auto;
45+
if let RenderNodeData::Text(text) = &node.data {
46+
if name == "width" {
47+
auto = Dimension::Length(text.width);
48+
} else if name == "height" {
49+
auto = Dimension::Length(text.height);
50+
}
51+
}
52+
53+
let Some(property) = node.get_property(name) else {
54+
return auto;
55+
};
56+
57+
property.compute_value();
58+
59+
if name == "width" {
60+
println!("Width: {:?}", property.actual);
61+
}
62+
63+
match &property.actual {
64+
CssValue::String(value) => match value.as_str() {
65+
"auto" => auto,
66+
s if s.ends_with('%') => {
67+
let value = s.trim_end_matches('%').parse::<f32>().unwrap_or(0.0);
68+
Dimension::Percent(value)
69+
}
70+
_ => Dimension::Length(property.actual.unit_to_px()), //HACK
71+
},
72+
CssValue::Percentage(value) => Dimension::Percent(*value),
73+
CssValue::Unit(..) => Dimension::Length(property.actual.unit_to_px()),
74+
_ => auto,
75+
}
76+
}
77+
78+
pub(crate) fn parse_align_i(node: &mut RenderTreeNode, name: &str) -> Option<AlignItems> {
79+
let display = node.get_property(name)?;
80+
display.compute_value();
81+
82+
let CssValue::String(ref value) = display.actual else {
83+
return None;
84+
};
85+
86+
match value.as_str() {
87+
"start" => Some(AlignItems::Start),
88+
"end" => Some(AlignItems::End),
89+
"flex-start" => Some(AlignItems::FlexStart),
90+
"flex-end" => Some(AlignItems::FlexEnd),
91+
"center" => Some(AlignItems::Center),
92+
"baseline" => Some(AlignItems::Baseline),
93+
"stretch" => Some(AlignItems::Stretch),
94+
_ => None,
95+
}
96+
}
97+
98+
pub(crate) fn parse_align_c(node: &mut RenderTreeNode, name: &str) -> Option<AlignContent> {
99+
let display = node.get_property(name)?;
100+
101+
display.compute_value();
102+
103+
let CssValue::String(ref value) = display.actual else {
104+
return None;
105+
};
106+
107+
match value.as_str() {
108+
"start" => Some(AlignContent::Start),
109+
"end" => Some(AlignContent::End),
110+
"flex-start" => Some(AlignContent::FlexStart),
111+
"flex-end" => Some(AlignContent::FlexEnd),
112+
"center" => Some(AlignContent::Center),
113+
"stretch" => Some(AlignContent::Stretch),
114+
"space-between" => Some(AlignContent::SpaceBetween),
115+
"space-around" => Some(AlignContent::SpaceAround),
116+
_ => None,
117+
}
118+
}
119+
120+
pub(crate) fn parse_tracking_sizing_function(
121+
node: &mut RenderTreeNode,
122+
name: &str,
123+
) -> Vec<TrackSizingFunction> {
124+
let Some(display) = node.get_property(name) else {
125+
return Vec::new();
126+
};
127+
128+
display.compute_value();
129+
130+
let CssValue::String(ref _value) = display.actual else {
131+
return Vec::new();
132+
};
133+
134+
Vec::new() //TODO: Implement this
135+
}
136+
137+
#[allow(dead_code)]
138+
pub(crate) fn parse_non_repeated_tracking_sizing_function(
139+
_node: &mut RenderTreeNode,
140+
_name: &str,
141+
) -> NonRepeatedTrackSizingFunction {
142+
todo!("implement parse_non_repeated_tracking_sizing_function")
143+
}
144+
145+
pub(crate) fn parse_grid_auto(
146+
node: &mut RenderTreeNode,
147+
name: &str,
148+
) -> Vec<NonRepeatedTrackSizingFunction> {
149+
let Some(display) = node.get_property(name) else {
150+
return Vec::new();
151+
};
152+
153+
display.compute_value();
154+
155+
let CssValue::String(ref _value) = display.actual else {
156+
return Vec::new();
157+
};
158+
159+
Vec::new() //TODO: Implement this
160+
}
161+
162+
pub(crate) fn parse_grid_placement(node: &mut RenderTreeNode, name: &str) -> GridPlacement {
163+
let Some(display) = node.get_property(name) else {
164+
return GridPlacement::Auto;
165+
};
166+
167+
display.compute_value();
168+
169+
match &display.actual {
170+
CssValue::String(value) => {
171+
if value.starts_with("span") {
172+
let value = value.trim_start_matches("span").trim();
173+
174+
if let Ok(value) = value.parse::<u16>() {
175+
GridPlacement::from_span(value)
176+
} else {
177+
GridPlacement::Auto
178+
}
179+
} else if let Ok(value) = value.parse::<i16>() {
180+
GridPlacement::from_line_index(value)
181+
} else {
182+
GridPlacement::Auto
183+
}
184+
}
185+
CssValue::Number(value) => GridPlacement::from_line_index(*value as i16),
186+
_ => GridPlacement::Auto,
187+
}
188+
}

0 commit comments

Comments
 (0)