#import "modules/std.sx"; #import "ui/types.sx"; RenderNodeType :: enum { rect; rounded_rect; text; image; clip_push; clip_pop; opacity_push; opacity_pop; } RenderNode :: struct { type: RenderNodeType; frame: Frame; // Rect / rounded_rect fill_color: Color; stroke_color: Color; stroke_width: f32; corner_radius: f32; // Text text: string; font_size: f32; text_color: Color; // Image texture_id: u32; // Opacity opacity: f32; depth: s64; } RenderTree :: struct { nodes: List(RenderNode); generation: s64; init :: () -> RenderTree { RenderTree.{ generation = 0 }; } clear :: (self: *RenderTree) { self.nodes.len = 0; self.generation += 1; } add :: (self: *RenderTree, node: RenderNode) -> s64 { idx := self.nodes.len; self.nodes.append(node); idx; } } // Stateful builder — views use this to emit render nodes RenderContext :: struct { tree: *RenderTree; clip_depth: s64; opacity: f32; depth: s64; init :: (tree: *RenderTree) -> RenderContext { RenderContext.{ tree = tree, clip_depth = 0, opacity = 1.0, depth = 0 }; } add_rect :: (self: *RenderContext, frame: Frame, fill: Color) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .rect; node.frame = frame; node.fill_color = fill; node.opacity = self.opacity; node.depth = self.depth; self.tree.add(node); self.depth += 1; } add_rounded_rect :: (self: *RenderContext, frame: Frame, fill: Color, radius: f32) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .rounded_rect; node.frame = frame; node.fill_color = fill; node.corner_radius = radius; node.opacity = self.opacity; node.depth = self.depth; self.tree.add(node); self.depth += 1; } add_stroked_rect :: (self: *RenderContext, frame: Frame, fill: Color, stroke: Color, stroke_w: f32, radius: f32) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .rounded_rect; node.frame = frame; node.fill_color = fill; node.stroke_color = stroke; node.stroke_width = stroke_w; node.corner_radius = radius; node.opacity = self.opacity; node.depth = self.depth; self.tree.add(node); self.depth += 1; } add_text :: (self: *RenderContext, frame: Frame, text: string, font_size: f32, color: Color) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .text; node.frame = frame; node.text = text; node.font_size = font_size; node.text_color = color; node.opacity = self.opacity; node.depth = self.depth; self.tree.add(node); self.depth += 1; } add_image :: (self: *RenderContext, frame: Frame, texture_id: u32) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .image; node.frame = frame; node.texture_id = texture_id; node.opacity = self.opacity; node.depth = self.depth; self.tree.add(node); self.depth += 1; } push_clip :: (self: *RenderContext, frame: Frame) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .clip_push; node.frame = frame; node.depth = self.depth; self.tree.add(node); self.clip_depth += 1; } pop_clip :: (self: *RenderContext) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .clip_pop; node.depth = self.depth; self.tree.add(node); self.clip_depth -= 1; } push_opacity :: (self: *RenderContext, alpha: f32) { prev := self.opacity; self.opacity = prev * alpha; node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .opacity_push; node.opacity = self.opacity; node.depth = self.depth; self.tree.add(node); } pop_opacity :: (self: *RenderContext, prev_opacity: f32) { node : RenderNode = ---; memset(@node, 0, size_of(RenderNode)); node.type = .opacity_pop; node.depth = self.depth; self.tree.add(node); self.opacity = prev_opacity; } }