From ff622c00a02abb4b701a694471461b6b07e979c2 Mon Sep 17 00:00:00 2001 From: SGavrl <277843151+SGavrl@users.noreply.github.com> Date: Thu, 2 Jul 2026 21:43:36 +0000 Subject: [PATCH] Fix table rendering layout and alignment --- crates/obscura-render/src/dom.rs | 36 +++++++++++++++++++++++++++++- crates/obscura-render/src/lib.rs | 13 ++++++++++- crates/obscura-render/src/paint.rs | 11 +++++---- crates/obscura-render/src/style.rs | 16 +++++++++++++ 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/crates/obscura-render/src/dom.rs b/crates/obscura-render/src/dom.rs index c584d3e..ca3480e 100644 --- a/crates/obscura-render/src/dom.rs +++ b/crates/obscura-render/src/dom.rs @@ -77,6 +77,9 @@ pub fn layout_dom(tree: &DomTree, viewport: (f32, f32)) -> DomLayout { crate::style::apply_inline(style, &format!("width: {}", width)); } } + if let Some(align) = node.get_attribute("align") { + crate::style::apply_inline(style, &format!("text-align: {}", align)); + } if let Some(height) = node.get_attribute("height") { if height.chars().all(|c| c.is_ascii_digit()) { crate::style::apply_inline(style, &format!("height: {}px", height)); @@ -84,6 +87,16 @@ pub fn layout_dom(tree: &DomTree, viewport: (f32, f32)) -> DomLayout { crate::style::apply_inline(style, &format!("height: {}", height)); } } + // For table cells with right alignment, make them grow to push content to the right + if let Some(elem) = node.as_element() { + let local = elem.local.as_ref(); + if local == "td" || local == "th" { + let is_title = node.get_attribute("class").unwrap_or_default().contains("title"); + if !is_title && style.justify_content == Some(taffy::JustifyContent::FlexEnd) { + style.flex_grow = Some(1.0); + } + } + } } } } @@ -234,7 +247,28 @@ fn build( let _name = node.as_element()?; let style = styles.get(&id)?; - let taffy_style = to_taffy_style(style); + let mut taffy_style = to_taffy_style(style); + + // Emulate HTML table layout by giving the last td in a tr all remaining space + if _name.local.as_ref() == "td" || _name.local.as_ref() == "th" { + if let Some(parent_id) = node.parent { + if let Some(parent) = tree.get_node(parent_id) { + if parent.as_element().map_or(false, |e| e.local.as_ref() == "tr") { + let mut last_td = None; + for cid in tree.children(parent_id) { + if let Some(child) = tree.get_node(cid) { + if child.as_element().map_or(false, |e| e.local.as_ref() == "td" || e.local.as_ref() == "th") { + last_td = Some(cid); + } + } + } + if last_td == Some(id) { + taffy_style.flex_grow = 1.0; + } + } + } + } + } let child_ids: Vec = tree .children(id) diff --git a/crates/obscura-render/src/lib.rs b/crates/obscura-render/src/lib.rs index ad9458a..675dc53 100644 --- a/crates/obscura-render/src/lib.rs +++ b/crates/obscura-render/src/lib.rs @@ -79,6 +79,8 @@ pub struct LayoutStyle { pub font_size: Option, pub align_items: Option, pub flex_direction: Option, + pub justify_content: Option, + pub flex_grow: Option, } /// A node in the input layout tree. `text` is carried for the paint phase; it @@ -159,10 +161,13 @@ pub(crate) fn to_taffy_style(style: &LayoutStyle) -> Style { }; if let Some(fd) = style.flex_direction { s.flex_direction = fd; + s.flex_wrap = taffy::FlexWrap::NoWrap; } else if style.display == Display::Inline { s.flex_direction = taffy::FlexDirection::Row; + s.flex_wrap = taffy::FlexWrap::Wrap; + } else { + s.flex_wrap = taffy::FlexWrap::NoWrap; } - s.flex_wrap = taffy::FlexWrap::Wrap; s.size = taffy::Size { width: dimension(style.width), height: dimension(style.height), @@ -170,6 +175,12 @@ pub(crate) fn to_taffy_style(style: &LayoutStyle) -> Style { if let Some(ai) = style.align_items { s.align_items = Some(ai); } + if let Some(jc) = style.justify_content { + s.justify_content = Some(jc); + } + if let Some(fg) = style.flex_grow { + s.flex_grow = fg; + } s.margin = rect_auto(style.margin); s.padding = rect_lp(style.padding); s.border = rect_lp(style.border); diff --git a/crates/obscura-render/src/paint.rs b/crates/obscura-render/src/paint.rs index ae0dd58..77d0fac 100644 --- a/crates/obscura-render/src/paint.rs +++ b/crates/obscura-render/src/paint.rs @@ -127,12 +127,15 @@ fn paint_text_node( ) -> Option<()> { let node = tree.get_node(nid)?; let text = match &node.data { - obscura_dom::tree::NodeData::Text { contents } => contents.trim(), + obscura_dom::tree::NodeData::Text { contents } => { + if contents.trim().is_empty() { + " " + } else { + contents + } + }, _ => return None, }; - if text.is_empty() { - return None; - } let parent = node.parent?; let style = laid.styles.get(&parent)?; diff --git a/crates/obscura-render/src/style.rs b/crates/obscura-render/src/style.rs index d799763..b4717e6 100644 --- a/crates/obscura-render/src/style.rs +++ b/crates/obscura-render/src/style.rs @@ -35,6 +35,15 @@ pub fn ua_style(tag: &str) -> LayoutStyle { style.align_items = Some(taffy::AlignItems::Center); } else if tag == "body" { style.margin = Edges { top: 8.0, right: 8.0, bottom: 8.0, left: 8.0 }; + } else if tag == "table" || tag == "tbody" { + style.display = Display::Flex; + style.flex_direction = Some(taffy::FlexDirection::Column); + style.align_items = Some(taffy::AlignItems::Stretch); // stretch rows to fill table width + } else if tag == "td" || tag == "th" { + style.display = Display::Flex; + style.flex_direction = Some(taffy::FlexDirection::Column); + style.align_items = Some(taffy::AlignItems::FlexStart); + style.padding = Edges { top: 0.0, right: 5.0, bottom: 0.0, left: 0.0 }; } style } @@ -88,6 +97,13 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) { "color" => style.color = parse_color(value), "border-color" => style.border_color = parse_color(value), "font-size" => style.font_size = px(value), + "text-align" => { + if value == "right" { + style.justify_content = Some(taffy::JustifyContent::FlexEnd); + } else if value == "center" { + style.justify_content = Some(taffy::JustifyContent::Center); + } + }, _ => {} } }