From 19165c4584482652ec187284c9d19baff75d55c6 Mon Sep 17 00:00:00 2001 From: Ada Bohm Date: Sat, 21 Feb 2026 20:35:24 +0100 Subject: [PATCH] Shrink Node structure --- nelsie/src/pyinterface/extract.rs | 31 ++++++---- renderer/src/lib.rs | 2 +- renderer/src/node.rs | 99 ++++++++++++++++++++++++------- renderer/src/render/layout.rs | 14 ++--- renderer/src/render/node.rs | 4 +- 5 files changed, 109 insertions(+), 41 deletions(-) diff --git a/nelsie/src/pyinterface/extract.rs b/nelsie/src/pyinterface/extract.rs index 9f3eacac..e97ad94b 100644 --- a/nelsie/src/pyinterface/extract.rs +++ b/nelsie/src/pyinterface/extract.rs @@ -15,8 +15,8 @@ use renderer::taffy::{ AlignContent, AlignItems, GridPlacement, Line, NonRepeatedTrackSizingFunction, }; use renderer::{ - Length, LengthOrAuto, LengthOrExpr, Node, NodeChild, NodeId, Page, Rectangle, Register, - Resources, Text, + Length, LengthOrAuto, LengthOrExpr, Node, NodeChild, NodeId, NodeUncommon, Page, Rectangle, + Register, Resources, Text, }; #[derive(FromPyObject)] @@ -370,11 +370,25 @@ fn obj_to_node( ) }; + let uncommon = { + let u = NodeUncommon { + flex_shrink: node.flex_shrink, + flex_wrap: Default::default(), + grid_template_rows, + grid_template_columns, + grid_row, + grid_column, + reverse: node.reverse, + url: node.url, + }; + if u.is_default() { + None + } else { + Some(Box::new(u)) + } + }; + Ok(Node { - grid_template_rows, - grid_template_columns, - grid_row, - grid_column, node_id: NodeId::new(node.node_id), width: node.width.map(|x| x.0), height: node.height.map(|x| x.0), @@ -383,10 +397,7 @@ fn obj_to_node( y: node.y.map(|x| x.expr), border_radius: node.border_radius, row: node.row, - reverse: node.reverse, - flex_wrap: Default::default(), flex_grow: node.flex_grow, - flex_shrink: node.flex_shrink, align_items: node.align_items.map(|x| x.into()), align_self: node.align_self.map(|x| x.into()), justify_self: node.justify_self.map(|x| x.into()), @@ -405,7 +416,7 @@ fn obj_to_node( bg_color: node.bg_color.map(|x| x.into()), z_level: node.z_level, content, - url: node.url, + uncommon, children: node .children .try_iter()? diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index c072bf57..6c9fa6ef 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -19,7 +19,7 @@ pub use document::{Document, Register, RenderingOptions}; pub use error::RendererError as Error; pub use image::{InMemoryBinImage, InMemorySvgImage}; pub use layout_info::PageLayout; -pub use node::{ContentId, Node, NodeChild}; +pub use node::{ContentId, Node, NodeChild, NodeUncommon}; pub use page::Page; pub use rectangle::Rectangle; pub use resources::Resources; diff --git a/renderer/src/node.rs b/renderer/src/node.rs index 407580ad..f9f0cd81 100644 --- a/renderer/src/node.rs +++ b/renderer/src/node.rs @@ -29,6 +29,33 @@ impl ContentId { } } +/// Fields that are rarely non-default; boxed behind an `Option` to keep `Node` small. +/// When all fields hold their default values the `uncommon` field in `Node` is `None`. +#[derive(Debug)] +pub struct NodeUncommon { + pub flex_shrink: f32, + pub flex_wrap: FlexWrap, + pub grid_template_rows: Vec, + pub grid_template_columns: Vec, + pub grid_row: Line, + pub grid_column: Line, + pub reverse: bool, + pub url: Option, +} + +impl NodeUncommon { + pub fn is_default(&self) -> bool { + self.flex_shrink == 1.0 + && self.flex_wrap == FlexWrap::default() + && self.grid_template_rows.is_empty() + && self.grid_template_columns.is_empty() + && self.grid_row == Line::default() + && self.grid_column == Line::default() + && !self.reverse + && self.url.is_none() + } +} + #[derive(Debug)] pub struct Node { pub node_id: NodeId, @@ -47,14 +74,8 @@ pub struct Node { pub row: bool, - pub reverse: bool, - - pub flex_wrap: FlexWrap, - pub flex_grow: f32, - pub flex_shrink: f32, - pub align_items: Option, pub align_self: Option, pub justify_self: Option, @@ -64,13 +85,6 @@ pub struct Node { pub column_gap: Length, pub row_gap: Length, - pub grid_template_rows: Vec, - - pub grid_template_columns: Vec, - - pub grid_row: Line, - pub grid_column: Line, - pub p_top: Length, pub p_bottom: Length, pub p_left: Length, @@ -87,10 +101,60 @@ pub struct Node { pub content: Option, - pub url: Option, + pub uncommon: Option>, } impl Node { + #[inline] + pub fn reverse(&self) -> bool { + self.uncommon.as_ref().is_some_and(|u| u.reverse) + } + + #[inline] + pub fn flex_shrink(&self) -> f32 { + self.uncommon.as_ref().map_or(1.0, |u| u.flex_shrink) + } + + #[inline] + pub fn flex_wrap(&self) -> FlexWrap { + self.uncommon + .as_ref() + .map_or_else(FlexWrap::default, |u| u.flex_wrap) + } + + #[inline] + pub fn grid_template_rows(&self) -> &[NonRepeatedTrackSizingFunction] { + self.uncommon + .as_ref() + .map_or(&[], |u| &u.grid_template_rows) + } + + #[inline] + pub fn grid_template_columns(&self) -> &[NonRepeatedTrackSizingFunction] { + self.uncommon + .as_ref() + .map_or(&[], |u| &u.grid_template_columns) + } + + #[inline] + pub fn grid_row(&self) -> Line { + self.uncommon + .as_ref() + .map_or_else(Line::default, |u| u.grid_row) + } + + #[inline] + pub fn grid_column(&self) -> Line { + self.uncommon + .as_ref() + .map_or_else(Line::default, |u| u.grid_column) + } + + #[inline] + pub fn url(&self) -> Option<&str> { + self.uncommon.as_ref().and_then(|u| u.url.as_deref()) + } + pub fn child_nodes(&self) -> impl Iterator { self.children.iter().filter_map(|child| match child { NodeChild::Node(node) => Some(node), @@ -117,11 +181,4 @@ impl Node { pub fn add_child_node(&mut self, node: Node) { self.children.push(NodeChild::Node(node)); } - - // fn collect_z_levels(&self, out: &mut BTreeSet) { - // out.insert(self.z_level); - // for child in self.child_nodes() { - // child.collect_z_levels(out); - // } - // } } diff --git a/renderer/src/render/layout.rs b/renderer/src/render/layout.rs index cf3fbf78..b373a128 100644 --- a/renderer/src/render/layout.rs +++ b/renderer/src/render/layout.rs @@ -303,7 +303,7 @@ fn compute_layout_helper( .or(content_h) .unwrap_or(tf::Dimension::Auto); - let flex_direction = match (node.row, node.reverse) { + let flex_direction = match (node.row, node.reverse()) { (false, false) => tf::FlexDirection::Column, (true, false) => tf::FlexDirection::Row, (false, true) => tf::FlexDirection::ColumnReverse, @@ -337,12 +337,12 @@ fn compute_layout_helper( dbg!(node.justify_content.at_step(self.step));*/ let grid_template_rows = node - .grid_template_rows + .grid_template_rows() .iter() .map(|x| tf::TrackSizingFunction::Single(*x)) .collect_vec(); let grid_template_columns = node - .grid_template_columns + .grid_template_columns() .iter() .map(|x| tf::TrackSizingFunction::Single(*x)) .collect_vec(); @@ -360,9 +360,9 @@ fn compute_layout_helper( aspect_ratio: content_aspect_ratio, padding, margin, - flex_wrap: node.flex_wrap, + flex_wrap: node.flex_wrap(), flex_grow: node.flex_grow, - flex_shrink: node.flex_shrink, + flex_shrink: node.flex_shrink(), align_items: node.align_items.or({ if is_grid { None @@ -386,8 +386,8 @@ fn compute_layout_helper( }, grid_template_rows, grid_template_columns, - grid_row: node.grid_row, - grid_column: node.grid_column, + grid_row: node.grid_row(), + grid_column: node.grid_column(), ..Default::default() }; taffy.new_with_children(style, &tf_children).unwrap() diff --git a/renderer/src/render/node.rs b/renderer/src/render/node.rs index a4657209..7bc4d2f6 100644 --- a/renderer/src/render/node.rs +++ b/renderer/src/render/node.rs @@ -20,9 +20,9 @@ pub(crate) fn render_node(node: &Node, layout: &ComputedLayout, canvas: &mut Can canvas.add_content(node.z_level, rect, *content_id); } - if let Some(url) = &node.url { + if let Some(url) = node.url() { let rect = &layout.node_layout(node.node_id).unwrap().rect; - canvas.add_link(Link::new(rect.clone(), url.clone())); + canvas.add_link(Link::new(rect.clone(), url.to_owned())); } for child in &node.children {