package examples import "core:os" import sdl "vendor:sdl3" import "../../draw" import "../../vendor/clay" import cyber "../cybersteel" // Clay border debug example. // // Lays out a grid of bordered Clay elements that exercise every code path in // `clay_emit_partial_border` and `try_dispatch_clay_rect_border_pair`: // // 1. Uniform borders (fast path) — sharp, rounded, and the border-thicker-than-radius // edge case (inner corner clamps to 0). // 2. Background + border combinations — opaque bg + opaque uniform border MERGES into one // SDF primitive; translucent border DECLINES the merge to preserve blend fidelity; // non-uniform border declines and falls through to the slow path; translucent bg with // opaque border still merges (bg alpha doesn't affect merge correctness). // 3. Single-side borders — top / right / bottom / left individually. // 4. Two-side borders — parallel pairs (no corners drawn) and adjacent pairs (one corner // rounds, others stay square). // 5. Three-side borders + asymmetric widths. // 6. Layout correctness — a vertical list with bottom-border separators (each border // lives inside its own item, no bleed between siblings) and a row of adjacent fully // bordered siblings (no border overlap, each in its own bounds). clay_borders :: proc() { if !sdl.Init({.VIDEO}) do os.exit(1) window := sdl.CreateWindow("Clay Borders Debug", 1200, 900, {.HIGH_PIXEL_DENSITY}) gpu := sdl.CreateGPUDevice(draw.PLATFORM_SHADER_FORMAT, true, nil) if !sdl.ClaimWindowForGPUDevice(gpu, window) do os.exit(1) if !draw.init(gpu, window) do os.exit(1) PLEX_SANS_REGULAR = draw.register_font(cyber.SANS_REGULAR_RAW) // Distinct colors so the fill, border, and translucent variants are visually unambiguous. BG_PAGE :: draw.Color{25, 25, 30, 255} FILL_OPAQUE :: draw.Color{80, 120, 200, 255} FILL_TRANSLUCENT :: draw.Color{80, 120, 200, 128} BORDER_OPAQUE :: draw.Color{255, 200, 100, 255} BORDER_TRANSLUCENT :: draw.Color{255, 200, 100, 128} label_config := clay.TextElementConfig { fontId = PLEX_SANS_REGULAR, fontSize = 12, textColor = {220, 220, 220, 255}, } header_config := clay.TextElementConfig { fontId = PLEX_SANS_REGULAR, fontSize = 16, textColor = {255, 255, 255, 255}, } title_config := clay.TextElementConfig { fontId = PLEX_SANS_REGULAR, fontSize = 22, textColor = {255, 255, 255, 255}, } for { defer free_all(context.temp_allocator) ev: sdl.Event for sdl.PollEvent(&ev) { if ev.type == .QUIT do return } base_layer := draw.begin({width = 1200, height = 900}) clay.SetLayoutDimensions({width = base_layer.bounds.width, height = base_layer.bounds.height}) clay.BeginLayout() if clay.UI(clay.ID("borders_page"))( { layout = { sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, padding = clay.PaddingAll(20), childGap = 14, layoutDirection = .TopToBottom, }, backgroundColor = clay_color(BG_PAGE), }, ) { clay.Text("Clay Borders Debug", title_config) //----- Section 1: Uniform borders (fast path) ----------------------------------- clay.Text("Uniform borders (fast path)", header_config) if clay.UI(clay.ID("row_uniform"))(border_row_layout()) { border_test_card( "1px sharp", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 1, right = 1, top = 1, bottom = 1}, {}, ) border_test_card( "2px, radius 8", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 2, right = 2, top = 2, bottom = 2}, {topLeft = 8, topRight = 8, bottomRight = 8, bottomLeft = 8}, ) border_test_card( "8px, radius 20", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 8, right = 8, top = 8, bottom = 8}, {topLeft = 20, topRight = 20, bottomRight = 20, bottomLeft = 20}, ) border_test_card( "10px > radius 5 (inner clamps)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 10, right = 10, top = 10, bottom = 10}, {topLeft = 5, topRight = 5, bottomRight = 5, bottomLeft = 5}, ) } //----- Section 2: Background + border (merge optimization) ---------------------- clay.Text("Background + border (merge optimization)", header_config) if clay.UI(clay.ID("row_bg_border"))(border_row_layout()) { border_test_card( "opaque bg + opaque (MERGES: 1 prim)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 2, right = 2, top = 2, bottom = 2}, {topLeft = 6, topRight = 6, bottomRight = 6, bottomLeft = 6}, ) border_test_card( "translucent bg + opaque (MERGES)", label_config, FILL_TRANSLUCENT, BORDER_OPAQUE, {left = 3, right = 3, top = 3, bottom = 3}, {topLeft = 6, topRight = 6, bottomRight = 6, bottomLeft = 6}, ) border_test_card( "opaque bg + translucent (NO merge)", label_config, FILL_OPAQUE, BORDER_TRANSLUCENT, {left = 4, right = 4, top = 4, bottom = 4}, {topLeft = 8, topRight = 8, bottomRight = 8, bottomLeft = 8}, ) border_test_card( "opaque bg + non-uniform (NO merge)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 1, right = 4, top = 2, bottom = 3}, {topLeft = 6, topRight = 6, bottomRight = 6, bottomLeft = 6}, ) } //----- Section 3: Single side borders ------------------------------------------- clay.Text("Single side", header_config) if clay.UI(clay.ID("row_single_side"))(border_row_layout()) { border_test_card("top only (4px)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {top = 4}, {}) border_test_card("right only (4px)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {right = 4}, {}) border_test_card( "bottom only (4px, divider)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {bottom = 4}, {}, ) border_test_card("left only (4px)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 4}, {}) } //----- Section 4: Two side borders ---------------------------------------------- clay.Text("Two sides", header_config) if clay.UI(clay.ID("row_two_sides"))(border_row_layout()) { border_test_card( "T+B parallel (no corners)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {top = 3, bottom = 3}, {topLeft = 8, topRight = 8, bottomRight = 8, bottomLeft = 8}, ) border_test_card( "L+R parallel (no corners)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {left = 3, right = 3}, {topLeft = 8, topRight = 8, bottomRight = 8, bottomLeft = 8}, ) border_test_card( "T+L adjacent (TL rounds)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {top = 3, left = 3}, {topLeft = 12, topRight = 12, bottomRight = 12, bottomLeft = 12}, ) border_test_card( "B+R adjacent (BR rounds)", label_config, FILL_OPAQUE, BORDER_OPAQUE, {bottom = 3, right = 3}, {topLeft = 12, topRight = 12, bottomRight = 12, bottomLeft = 12}, ) } //----- Section 5: Three sides + asymmetric widths ------------------------------- clay.Text("Three sides + asymmetric widths", header_config) if clay.UI(clay.ID("row_advanced"))(border_row_layout()) { border_test_card( "T+R+B (no L), rounded", label_config, FILL_OPAQUE, BORDER_OPAQUE, {top = 3, right = 3, bottom = 3}, {topLeft = 8, topRight = 8, bottomRight = 8, bottomLeft = 8}, ) border_test_card( "T+L+R (no B), rounded", label_config, FILL_OPAQUE, BORDER_OPAQUE, {top = 3, left = 3, right = 3}, {topLeft = 8, topRight = 8, bottomRight = 8, bottomLeft = 8}, ) border_test_card( "asym 1/2/3/4 T/R/B/L", label_config, FILL_OPAQUE, BORDER_OPAQUE, {top = 1, right = 2, bottom = 3, left = 4}, {}, ) border_test_card( "asym + rounded", label_config, FILL_OPAQUE, BORDER_OPAQUE, {top = 2, right = 4, bottom = 2, left = 4}, {topLeft = 10, topRight = 10, bottomRight = 10, bottomLeft = 10}, ) } //----- Section 6: Layout correctness -------------------------------------------- clay.Text("Layout correctness", header_config) if clay.UI(clay.ID("row_correctness"))( {layout = {sizing = {clay.SizingGrow({}), clay.SizingFit({})}, childGap = 14}}, ) { // 6a: vertical list with per-item bottom-border separator. Each item's // border draws INSIDE its own bounds, so adjacent items don't bleed. if clay.UI(clay.ID("list_demo"))( { layout = { sizing = {clay.SizingFixed(300), clay.SizingFit({})}, padding = clay.PaddingAll(6), childGap = 6, layoutDirection = .TopToBottom, }, }, ) { clay.Text("List with bottom-border separators", label_config) if clay.UI(clay.ID("list_outer"))( { layout = {sizing = {clay.SizingGrow({}), clay.SizingFit({})}, layoutDirection = .TopToBottom}, backgroundColor = clay_color(FILL_OPAQUE), }, ) { for index in 0 ..< 5 { if clay.UI(clay.ID("list_item", u32(index)))( { layout = {sizing = {clay.SizingGrow({}), clay.SizingFixed(28)}, padding = clay.PaddingAll(6)}, border = {color = clay_color(BORDER_OPAQUE), width = {bottom = 1}}, }, ) { clay.Text("Item", label_config) } } } } // 6b: row of adjacent fully bordered siblings. With borders rendered // INSIDE each element's bounds, the boundary between two siblings shows // the natural 2*width sum (no overlap, no bleed). if clay.UI(clay.ID("adj_demo"))( { layout = { sizing = {clay.SizingFixed(380), clay.SizingFit({})}, padding = clay.PaddingAll(6), childGap = 6, layoutDirection = .TopToBottom, }, }, ) { clay.Text("Adjacent bordered siblings (no gap)", label_config) if clay.UI(clay.ID("adj_row"))({layout = {sizing = {clay.SizingGrow({}), clay.SizingFit({})}}}) { for index in 0 ..< 4 { if clay.UI(clay.ID("adj_item", u32(index)))( { layout = {sizing = {clay.SizingFixed(80), clay.SizingFixed(60)}}, backgroundColor = clay_color(FILL_OPAQUE), border = {color = clay_color(BORDER_OPAQUE), width = {left = 2, right = 2, top = 2, bottom = 2}}, }, ) {} } } } } } clay_batch := draw.ClayBatch { bounds = base_layer.bounds, cmds = clay.EndLayout(0), } draw.prepare_clay_batch(base_layer, &clay_batch) draw.end(gpu, window) } } // Helper: convert a draw.Color (RGBA u8) to clay.Color (RGBA float in 0-255 range). clay_color :: proc(c: draw.Color) -> clay.Color { return clay.Color{f32(c[0]), f32(c[1]), f32(c[2]), f32(c[3])} } // Helper: shared row container declaration for the test sections. border_row_layout :: proc() -> clay.ElementDeclaration { return clay.ElementDeclaration{layout = {sizing = {clay.SizingGrow({}), clay.SizingFit({})}, childGap = 12}} } // One labeled test card: a fixed-width column with a caption above and a sample bordered // rectangle below. Uses `clay.ID_LOCAL` for the inner element so each card gets a unique // child ID without the caller passing one explicitly. border_test_card :: proc( label: string, label_config: clay.TextElementConfig, fill_color: draw.Color, border_color: draw.Color, border_width: clay.BorderWidth, corner_radii: clay.CornerRadius, ) { if clay.UI(clay.ID(label))( { layout = { sizing = {clay.SizingFixed(275), clay.SizingFit({})}, padding = clay.PaddingAll(4), childGap = 6, layoutDirection = .TopToBottom, }, }, ) { clay.Text(label, label_config) if clay.UI(clay.ID_LOCAL("test_inner"))( { layout = {sizing = {clay.SizingGrow({}), clay.SizingFixed(64)}}, backgroundColor = clay_color(fill_color), border = clay.BorderElementConfig{color = clay_color(border_color), width = border_width}, cornerRadius = corner_radii, }, ) {} } }