From 740caaea06bde4c4d79a19ef3c13086e6b1905a5 Mon Sep 17 00:00:00 2001 From: SGavrl <277843151+SGavrl@users.noreply.github.com> Date: Thu, 2 Jul 2026 22:12:03 +0000 Subject: [PATCH] Fix layout text width estimates and alignment for hn header --- crates/obscura-render/src/dom.rs | 31 +++++++++++++++++++++------ crates/obscura-render/src/paint.rs | 34 ++++++++++++++++++++++++++++-- crates/obscura-render/src/style.rs | 2 ++ 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/crates/obscura-render/src/dom.rs b/crates/obscura-render/src/dom.rs index 62263c1..ac68ca0 100644 --- a/crates/obscura-render/src/dom.rs +++ b/crates/obscura-render/src/dom.rs @@ -224,16 +224,33 @@ fn build( let node = tree.get_node(id)?; if let obscura_dom::tree::NodeData::Text { contents } = &node.data { - let text = contents.trim(); - if text.is_empty() { - return None; + let mut display_text = String::new(); + let mut in_space = false; + for c in contents.chars() { + if c.is_whitespace() { + if !in_space { + display_text.push(' '); + in_space = true; + } + } else { + display_text.push(c); + in_space = false; + } + } + if display_text == " " { + display_text = " ".to_string(); // Keep single space } - let parent = node.parent?; - let style = styles.get(&parent)?; - let fsize = style.font_size.unwrap_or(16.0); + let mut fsize = 16.0; + let mut is_bold = false; + if let Some(parent_id) = node.parent { + if let Some(p_style) = styles.get(&parent_id) { + fsize = p_style.font_size.unwrap_or(16.0); + is_bold = p_style.font_weight.as_deref() == Some("bold"); + } + } - let width = text.chars().count() as f32 * (fsize * 0.55); // rough width estimate + let width = crate::paint::measure_text(&display_text, fsize, is_bold); let height = fsize * 1.2; let taffy_style = taffy::Style { diff --git a/crates/obscura-render/src/paint.rs b/crates/obscura-render/src/paint.rs index eea6105..2496211 100644 --- a/crates/obscura-render/src/paint.rs +++ b/crates/obscura-render/src/paint.rs @@ -143,12 +143,25 @@ fn paint_text_node( pixmap: &mut Pixmap, ) -> Option<()> { let node = tree.get_node(nid)?; + let mut text_buf = String::new(); let text = match &node.data { obscura_dom::tree::NodeData::Text { contents } => { - if contents.trim().is_empty() { + let mut in_space = false; + for c in contents.chars() { + if c.is_whitespace() { + if !in_space { + text_buf.push(' '); + in_space = true; + } + } else { + text_buf.push(c); + in_space = false; + } + } + if text_buf.is_empty() { " " } else { - contents + &text_buf } }, _ => return None, @@ -167,11 +180,28 @@ fn paint_text_node( None => return None, }; + if text.contains("zachtronics") || text.contains("Exapunks") { + println!("DEBUG text: {:?} rect: {:?}", text, rect); + } + // Paint text at rect.x, rect.y draw_text(pixmap, text, rect.x, rect.y, color, fsize, is_bold); Some(()) } +pub fn measure_text(text: &str, size: f32, is_bold: bool) -> f32 { + let font = FontRef::try_from_slice(FONT_BYTES).unwrap(); + let scale = PxScale::from(size); + let scaled_font = font.as_scaled(scale); + let mut width = 0.0; + for c in text.chars() { + if c.is_control() { continue; } + width += scaled_font.h_advance(font.glyph_id(c)); + } + if is_bold { width += text.chars().filter(|c| !c.is_control()).count() as f32; } + width +} + fn draw_text(pixmap: &mut Pixmap, text: &str, x: f32, y: f32, color: [u8; 4], size: f32, is_bold: bool) { let font = FontRef::try_from_slice(FONT_BYTES).unwrap(); let scale = PxScale::from(size); diff --git a/crates/obscura-render/src/style.rs b/crates/obscura-render/src/style.rs index 98e657f..9247ea1 100644 --- a/crates/obscura-render/src/style.rs +++ b/crates/obscura-render/src/style.rs @@ -114,8 +114,10 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) { "text-align" => { if value == "right" { style.justify_content = Some(taffy::JustifyContent::FlexEnd); + style.align_items = Some(taffy::AlignItems::FlexEnd); } else if value == "center" { style.justify_content = Some(taffy::JustifyContent::Center); + style.align_items = Some(taffy::AlignItems::Center); } }, "align-items" => {