From 89c083991f72ac7980231c47ffdb24bbb393b03a Mon Sep 17 00:00:00 2001 From: Aleksander Date: Sun, 5 Oct 2025 17:23:27 +0200 Subject: [PATCH] wgui: pretty-print tree --- uidev/src/main.rs | 9 ++++-- wgui/src/drawing.rs | 15 ++++++++++ wgui/src/i18n.rs | 4 +-- wgui/src/layout.rs | 51 +++++++++++++++++++++++++++++++- wgui/src/renderer_vk/text/mod.rs | 10 ++----- wgui/src/widget/div.rs | 8 +++++ wgui/src/widget/label.rs | 16 +++++++++- wgui/src/widget/mod.rs | 20 +++++++++++++ wgui/src/widget/rectangle.rs | 15 +++++++++- wgui/src/widget/sprite.rs | 8 +++++ 10 files changed, 142 insertions(+), 14 deletions(-) diff --git a/uidev/src/main.rs b/uidev/src/main.rs index f98b3db..0a7e548 100644 --- a/uidev/src/main.rs +++ b/uidev/src/main.rs @@ -77,7 +77,8 @@ fn load_testbed( fn main() -> Result<(), Box> { init_logging(); - let (gfx, event_loop, window, surface) = init_window("[-/=]: gui scale, F10: debug draw")?; + let (gfx, event_loop, window, surface) = + init_window("[-/=]: gui scale, F10: debug draw, F11: print tree")?; let inner_size = window.inner_size(); let mut swapchain_size = [inner_size.width, inner_size.height]; @@ -230,6 +231,10 @@ fn main() -> Result<(), Box> { testbed.layout().borrow_mut().mark_redraw(); } + if event.physical_key == PhysicalKey::Code(KeyCode::F11) { + testbed.layout().borrow_mut().print_tree(); + } + if event.physical_key == PhysicalKey::Code(KeyCode::Equal) { scale *= 1.25; render_context @@ -309,7 +314,7 @@ fn main() -> Result<(), Box> { log::trace!("drawing frame {frame_index}"); frame_index += 1; - limiter.start(0); // max 120 fps + limiter.start(120); // max 120 fps profiler.start(); { diff --git a/wgui/src/drawing.rs b/wgui/src/drawing.rs index be15d5f..75b20f6 100644 --- a/wgui/src/drawing.rs +++ b/wgui/src/drawing.rs @@ -75,6 +75,9 @@ pub struct Color { pub a: f32, } +pub const ANSI_RESET_CODE: &str = "\x1b[39m\x1b[49m"; +pub const ANSI_BOLD_CODE: &str = "\x1b[1m"; + impl Color { pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { Self { r, g, b, a } @@ -109,6 +112,18 @@ impl Color { a: self.a * (1.0 - n) + other.a * n, } } + + pub fn debug_ansi_format(&self) -> String { + let r = (self.r * 255.0).clamp(0.0, 255.0) as u8; + let g = (self.g * 255.0).clamp(0.0, 255.0) as u8; + let b = (self.b * 255.0).clamp(0.0, 255.0) as u8; + format!("\x1b[38;2;{r};{g};{b}m") + } + + // pretty-print ansi escape code color + pub fn debug_ansi_block(&self) -> String { + format!("{}███{}", self.debug_ansi_format(), ANSI_RESET_CODE) + } } impl Default for Color { diff --git a/wgui/src/i18n.rs b/wgui/src/i18n.rs index eb22b63..b95193a 100644 --- a/wgui/src/i18n.rs +++ b/wgui/src/i18n.rs @@ -7,8 +7,8 @@ use crate::assets::AssetProvider; // for now it's just a simple string container #[derive(Debug, Default)] pub struct Translation { - text: Rc, - translated: bool, // if true, `text` is a translation key + pub text: Rc, + pub translated: bool, // if true, `text` is a translation key } impl PartialEq for Translation { diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index d113cc6..44a1e24 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -1,12 +1,13 @@ use std::{ cell::{RefCell, RefMut}, + io::Write, rc::{Rc, Weak}, }; use crate::{ animation::Animations, components::{Component, InitData}, - drawing::{Boundary, push_scissor_stack, push_transform_stack}, + drawing::{self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, push_scissor_stack, push_transform_stack}, event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection}, globals::WguiGlobals, widget::{self, EventParams, WidgetObj, WidgetState, div::WidgetDiv}, @@ -546,6 +547,54 @@ impl Layout { Ok(()) } + + pub fn print_tree(&self) { + let mut buf = Vec::::new(); + self.print_tree_recur(&mut buf, 0, self.tree_root_node); + let str = format!("\n{}", unsafe { str::from_utf8_unchecked(&buf) }); + std::io::stdout().write_all(str.as_bytes()).unwrap(); + } + + fn print_tree_recur(&self, buf: &mut Vec, depth: u32, node_id: taffy::NodeId) { + // indent + for _ in 0..depth { + buf.push(b'|'); + buf.push(b' '); + } + + let widget_id = self.state.tree.get_node_context(node_id).unwrap(); + let layout = self.state.tree.layout(node_id).unwrap(); + + let widget = self.state.widgets.get(*widget_id).unwrap(); + + let state = widget.state(); + + let type_color = match state.obj.get_type() { + widget::WidgetType::Div => drawing::Color::new(1.0, 1.0, 1.0, 1.0), + widget::WidgetType::Label => drawing::Color::new(0.4, 1.0, 0.0, 1.0), + widget::WidgetType::Sprite => drawing::Color::new(0.0, 0.8, 1.0, 1.0), + widget::WidgetType::Rectangle => drawing::Color::new(1.0, 0.5, 0.2, 1.0), + }; + + let line = format!( + "{}{}{}{}: [pos: {}x{}][size: {}x{}]{}\n", + ANSI_BOLD_CODE, + type_color.debug_ansi_format(), + state.obj.get_type().as_str(), + ANSI_RESET_CODE, + layout.location.x, + layout.location.y, + layout.content_size.width, + layout.content_size.height, + state.obj.debug_print() + ); + + buf.append(&mut line.into_bytes()); + + for child_id in self.state.tree.child_ids(node_id) { + self.print_tree_recur(buf, depth + 1, child_id); + } + } } impl LayoutState { diff --git a/wgui/src/renderer_vk/text/mod.rs b/wgui/src/renderer_vk/text/mod.rs index 4d2ea33..96b587c 100644 --- a/wgui/src/renderer_vk/text/mod.rs +++ b/wgui/src/renderer_vk/text/mod.rs @@ -25,7 +25,7 @@ const DEFAULT_LINE_HEIGHT_RATIO: f32 = 1.43; pub(crate) const DEFAULT_METRICS: Metrics = Metrics::new(DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE * DEFAULT_LINE_HEIGHT_RATIO); -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct TextShadow { pub y: f32, pub x: f32, @@ -42,7 +42,7 @@ impl Default for TextShadow { } } -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone)] pub struct TextStyle { pub size: Option, pub line_height: Option, @@ -78,11 +78,7 @@ impl From<&TextStyle> for Metrics { impl From<&TextStyle> for Wrap { fn from(value: &TextStyle) -> Self { - if value.wrap { - Self::WordOrGlyph - } else { - Self::None - } + if value.wrap { Self::WordOrGlyph } else { Self::None } } } diff --git a/wgui/src/widget/div.rs b/wgui/src/widget/div.rs index 8a146ff..55a636e 100644 --- a/wgui/src/widget/div.rs +++ b/wgui/src/widget/div.rs @@ -26,4 +26,12 @@ impl WidgetObj for WidgetDiv { fn set_id(&mut self, id: WidgetID) { self.id = id; } + + fn get_type(&self) -> super::WidgetType { + super::WidgetType::Div + } + + fn debug_print(&self) -> String { + String::default() + } } diff --git a/wgui/src/widget/label.rs b/wgui/src/widget/label.rs index 79ca803..eb6a2ad 100644 --- a/wgui/src/widget/label.rs +++ b/wgui/src/widget/label.rs @@ -15,7 +15,7 @@ use crate::{ use super::{WidgetObj, WidgetState}; -#[derive(Default)] +#[derive(Debug, Default)] pub struct WidgetLabelParams { pub content: Translation, pub style: TextStyle, @@ -160,4 +160,18 @@ impl WidgetObj for WidgetLabel { fn set_id(&mut self, id: WidgetID) { self.id = id; } + + fn get_type(&self) -> super::WidgetType { + super::WidgetType::Label + } + + fn debug_print(&self) -> String { + let color = if let Some(color) = self.params.style.color { + format!("[color: {}]", color.debug_ansi_block()) + } else { + String::default() + }; + + format!("[text: \"{}\"]{}", self.params.content.text, color) + } } diff --git a/wgui/src/widget/mod.rs b/wgui/src/widget/mod.rs index 0ece8a2..7dbdb5a 100644 --- a/wgui/src/widget/mod.rs +++ b/wgui/src/widget/mod.rs @@ -115,10 +115,30 @@ pub struct DrawParams<'a> { pub taffy_layout: &'a taffy::Layout, } +pub enum WidgetType { + Div, + Label, + Sprite, + Rectangle, +} + +impl WidgetType { + pub const fn as_str(&self) -> &str { + match self { + WidgetType::Div => "div", + WidgetType::Label => "label", + WidgetType::Sprite => "sprite", + WidgetType::Rectangle => "rectangle", + } + } +} + pub trait WidgetObj: AnyTrait { // every widget stores their of id for convenience reasons fn get_id(&self) -> WidgetID; fn set_id(&mut self, id: WidgetID); // always set at insertion + fn get_type(&self) -> WidgetType; + fn debug_print(&self) -> String; fn draw(&mut self, state: &mut DrawState, params: &DrawParams); diff --git a/wgui/src/widget/rectangle.rs b/wgui/src/widget/rectangle.rs index 9a98e5e..6d462f0 100644 --- a/wgui/src/widget/rectangle.rs +++ b/wgui/src/widget/rectangle.rs @@ -8,7 +8,7 @@ use crate::{ use super::{WidgetObj, WidgetState}; -#[derive(Default)] +#[derive(Debug, Default)] pub struct WidgetRectangleParams { pub color: drawing::Color, pub color2: drawing::Color, @@ -66,4 +66,17 @@ impl WidgetObj for WidgetRectangle { fn set_id(&mut self, id: WidgetID) { self.id = id; } + + fn get_type(&self) -> super::WidgetType { + super::WidgetType::Rectangle + } + + fn debug_print(&self) -> String { + format!( + "[color: {}][color2: {}][gradient: {:?}]", + self.params.color.debug_ansi_block(), + self.params.color2.debug_ansi_block(), + self.params.gradient, + ) + } } diff --git a/wgui/src/widget/sprite.rs b/wgui/src/widget/sprite.rs index af7d34a..9b44749 100644 --- a/wgui/src/widget/sprite.rs +++ b/wgui/src/widget/sprite.rs @@ -101,4 +101,12 @@ impl WidgetObj for WidgetSprite { fn set_id(&mut self, id: WidgetID) { self.id = id; } + + fn get_type(&self) -> super::WidgetType { + super::WidgetType::Sprite + } + + fn debug_print(&self) -> String { + String::default() + } }