diff --git a/bindings/odin/examples/shared_layouts/clay_video_demo.odin b/bindings/odin/examples/shared_layouts/clay_video_demo.odin index 1486c6a..496d4dd 100644 --- a/bindings/odin/examples/shared_layouts/clay_video_demo.odin +++ b/bindings/odin/examples/shared_layouts/clay_video_demo.odin @@ -100,7 +100,7 @@ video_demo_layout :: proc(data: ^Video_Demo_Data) -> clay.ClayArray(clay.RenderC // Child elements go inside braces if clay.UI(clay.ID("HeaderBar"))( { - layout = {sizing = {height = clay.SizingFixed(60), width = clay.SizingFixed(0)}, padding = {16, 16, 0, 0}, childGap = 16, childAlignment = {y = .Center}}, + layout = {sizing = {height = clay.SizingFixed(60), width = clay.SizingGrow()}, padding = {16, 16, 0, 0}, childGap = 16, childAlignment = {y = .Center}}, backgroundColor = contentBackgroundColor, cornerRadius = clay.CornerRadiusAll(8), }, @@ -154,7 +154,7 @@ video_demo_layout :: proc(data: ^Video_Demo_Data) -> clay.ClayArray(clay.RenderC clay.TextDynamic(document.title, clay.TextConfig({fontId = VIDEO_DEMO_FONT_ID_BODY, fontSize = 20, textColor = {255, 255, 255, 255}})) } } else { - clickData := new_clone((Sidebar_Click_Data) { + clickData := new_clone(Sidebar_Click_Data { requestedDocumentIndex = i, selectedDocumentIndex = &data.selectedDocumentIndex, }, context.temp_allocator) diff --git a/bindings/odin/examples/video_demo_sdl/README.md b/bindings/odin/examples/video_demo_sdl/README.md index 03ebf4f..cb2e18c 100644 --- a/bindings/odin/examples/video_demo_sdl/README.md +++ b/bindings/odin/examples/video_demo_sdl/README.md @@ -1,4 +1,4 @@ -Odin doesn't directly package SDL, so on Windows, you'll want to copy `SDL3.dll` and `SDL3_ttf.dll` into this directory. On Linux and Mac, you should install SDL3 via your package manager +Odin doesn't directly package SDL, so on Windows, you'll want to copy `SDL3.dll` and `SDL3_ttf.dll` into this directory from odin's `vendor/sdl3` directory located next to the compiler. You can use `odin root` to figure out where your compiler is installed. On Linux and Mac, you should install SDL3 via your package manager ## Running on Windows In an embdedded termninal, missing dependencies will fail silently on Windows, so if you have any unexplainable crashes, make sure the DLLs are in your current working directory (the path you run the command from). It's easiest to just copy them next to this file, and run from here as well. \ No newline at end of file diff --git a/bindings/odin/examples/video_demo_sdl/main.odin b/bindings/odin/examples/video_demo_sdl/main.odin index 5e8f687..380aa9b 100644 --- a/bindings/odin/examples/video_demo_sdl/main.odin +++ b/bindings/odin/examples/video_demo_sdl/main.odin @@ -10,7 +10,7 @@ import "vendor:sdl3/ttf" App_State :: struct { window: ^sdl.Window, - rendererData: Clay_SDL3RendererData, + rendererData: Clay_SDL_Render_Data, } state: App_State @@ -22,7 +22,7 @@ errorHandler :: proc "c" (errorData: clay.ErrorData) { } load_font :: proc(data: []byte, size: f32, id: u16, fonts: ^[dynamic]^ttf.Font) { - font_stream := sdl.IOFromConstMem(raw_data(ROBOTO), uint(len(ROBOTO))) + font_stream := sdl.IOFromConstMem(raw_data(data), uint(len(data))) font := ttf.OpenFontIO(font_stream, true, size * 2) assign_at(fonts, int(id), font) } @@ -38,7 +38,7 @@ measure_text :: proc "c" (text: clay.StringSlice, config: ^clay.TextElementConfi sdl.LogError(i32(sdl.LogCategory.ERROR), "Failed to measure text: %s", sdl.GetError()) } - return clay.Dimensions{f32(width), f32(height)} + return {f32(width), f32(height)} } // Load at compile time, directly into the binary ROBOTO := #load("./Roboto-Regular.ttf") @@ -58,9 +58,9 @@ main :: proc() { state.window = window state.rendererData.renderer = renderer - state.rendererData.textEngine = ttf.CreateRendererTextEngine(renderer) + state.rendererData.text_engine = ttf.CreateRendererTextEngine(renderer) - minMemorySize := (uint)(clay.MinMemorySize()) + minMemorySize := uint(clay.MinMemorySize()) arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, make([^]u8, minMemorySize)) clay.Initialize(arena, {1280, 720}, {handler = errorHandler}) clay.SetMeasureTextFunction(measure_text, &state.rendererData.fonts) @@ -78,12 +78,10 @@ main :: proc() { window_width, window_height: i32 for !done { - defer free_all(context.temp_allocator) - scrollDelta: clay.Vector2 - for (sdl.PollEvent(&event)) { - if (event.type == .QUIT) { + for sdl.PollEvent(&event) { + if event.type == .QUIT { done = true } else if event.type == .MOUSE_WHEEL { scrollDelta.x = event.wheel.x @@ -99,10 +97,8 @@ main :: proc() { clay.SetLayoutDimensions({width = f32(window_width), height = f32(window_height)}) - mouseX: f32 = 0 - mouseY: f32 = 0 - mouseState := sdl.GetMouseState(&mouseX, &mouseY) - mousePosition := (clay.Vector2){mouseX, mouseY} + mousePosition : clay.Vector2 + mouseState := sdl.GetMouseState(&mousePosition.x, &mousePosition.y) clay.SetPointerState(mousePosition, .LEFT in mouseState) clay.UpdateScrollContainers(false, scrollDelta, f32(deltaTime)) @@ -112,7 +108,7 @@ main :: proc() { sdl.SetRenderDrawColor(renderer, 0, 0, 0, 255) sdl.RenderClear(renderer) - sdl_Clay_RenderClayCommands(&state.rendererData, &commands) + sdl_render_clay_commands(&state.rendererData, &commands) sdl.RenderPresent(renderer) } diff --git a/bindings/odin/examples/video_demo_sdl/renderer.odin b/bindings/odin/examples/video_demo_sdl/renderer.odin index 0c4c6b4..4537de2 100644 --- a/bindings/odin/examples/video_demo_sdl/renderer.odin +++ b/bindings/odin/examples/video_demo_sdl/renderer.odin @@ -3,16 +3,17 @@ package video_demo_sdl import clay "../../clay-odin" import "core:math" +import "core:math/linalg" import sdl "vendor:sdl3" import "vendor:sdl3/ttf" -Clay_SDL3RendererData :: struct { - renderer: ^sdl.Renderer, - textEngine: ^ttf.TextEngine, - fonts: [dynamic]^ttf.Font, +Clay_SDL_Render_Data :: struct { + renderer: ^sdl.Renderer, + text_engine: ^ttf.TextEngine, + fonts: [dynamic]^ttf.Font, } -// SDL_ttf works in pts, but clay expects pixels. +// SDL_ttf works in pts, but clay expects pixels. // 0.85 looks correct from what I've seen, but this calculation is probably incorrect. px_to_pt :: proc "contextless" (pixels: f32) -> f32 { return pixels * 0.85 @@ -24,432 +25,287 @@ px_to_pt :: proc "contextless" (pixels: f32) -> f32 { NUM_CIRCLE_SEGMENTS :: 16 //all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles. -sdl_Clay_RenderFillRoundedRect :: proc( - rendererData: ^Clay_SDL3RendererData, - rect: sdl.FRect, - cornerRadius: f32, - _color: clay.Color, -) { - color := sdl.FColor(_color / 255) +@(private = "file") +fill_rounded_rect :: proc(rendererData: ^Clay_SDL_Render_Data, rect: sdl.FRect, cornerRadius: f32, _color: clay.Color) { + color := sdl.FColor(_color / 255) - indexCount: i32 = 0 - vertexCount: i32 = 0 + indexCount: i32 = 0 + vertexCount: i32 = 0 - minRadius := sdl.min(rect.w, rect.h) / 2 - clampedRadius := sdl.min(cornerRadius, minRadius) + minRadius := min(rect.w, rect.h) / 2 + clampedRadius := min(cornerRadius, minRadius) - numCircleSegments := sdl.max(NUM_CIRCLE_SEGMENTS, i32(clampedRadius * 0.5)) + numCircleSegments := max(NUM_CIRCLE_SEGMENTS, i32(clampedRadius * 0.5)) - totalVertices := 4 + (4 * (numCircleSegments * 2)) + 2 * 4 - totalIndices := 6 + (4 * (numCircleSegments * 3)) + 6 * 4 + totalVertices := 4 + (4 * (numCircleSegments * 2)) + 2 * 4 + totalIndices := 6 + (4 * (numCircleSegments * 3)) + 6 * 4 - // Maybe instrinsics.alloca these? - vertices := make([]sdl.Vertex, totalVertices, allocator = context.temp_allocator) - indices := make([]i32, totalIndices, allocator = context.temp_allocator) + // Maybe instrinsics.alloca these? + vertices := make([]sdl.Vertex, totalVertices, allocator = context.temp_allocator) + indices := make([]i32, totalIndices, allocator = context.temp_allocator) - //define center rectangle - vertices[vertexCount + 0] = (sdl.Vertex) { - {rect.x + clampedRadius, rect.y + clampedRadius}, - color, - {0, 0}, - } //0 center TL - vertices[vertexCount + 1] = (sdl.Vertex) { - {rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, - color, - {1, 0}, - } //1 center TR - vertices[vertexCount + 2] = (sdl.Vertex) { - {rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, - color, - {1, 1}, - } //2 center BR - vertices[vertexCount + 3] = (sdl.Vertex) { - {rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, - color, - {0, 1}, - } //3 center BL + //define center rectangle + vertices[vertexCount + 0] = {{rect.x + clampedRadius, rect.y + clampedRadius}, color, {0, 0}} //0 center TL + vertices[vertexCount + 1] = {{rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, color, {1, 0}} //1 center TR + vertices[vertexCount + 2] = {{rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1}} //2 center BR + vertices[vertexCount + 3] = {{rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1}} //3 center BL - vertexCount += 4 + vertexCount += 4 - indices[indexCount + 0] = 0 - indices[indexCount + 1] = 1 - indices[indexCount + 2] = 3 - indices[indexCount + 3] = 1 - indices[indexCount + 4] = 2 - indices[indexCount + 5] = 3 + indices[indexCount + 0] = 0 + indices[indexCount + 1] = 1 + indices[indexCount + 2] = 3 + indices[indexCount + 3] = 1 + indices[indexCount + 4] = 2 + indices[indexCount + 5] = 3 - indexCount += 6 + indexCount += 6 - //define rounded corners as triangle fans - step := (f32(math.PI) / 2) / f32(numCircleSegments) - for i in 0 ..< numCircleSegments { - angle1 := f32(i) * step - angle2 := (f32(i) + 1) * step + //define rounded corners as triangle fans + step := (f32(math.PI) / 2) / f32(numCircleSegments) + for i in 0 ..< numCircleSegments { + angle1 := f32(i) * step + angle2 := (f32(i) + 1) * step - for j in i32(0) ..< 4 { // Iterate over four corners - cx, cy, signX, signY: f32 + for j in i32(0) ..< 4 { // Iterate over four corners + cx, cy, signX, signY: f32 - switch (j) { - case 0: - cx = rect.x + clampedRadius - cy = rect.y + clampedRadius - signX = -1 - signY = -1 - // Top-left - case 1: - cx = rect.x + rect.w - clampedRadius - cy = rect.y + clampedRadius - signX = 1 - signY = -1 // Top-right - case 2: - cx = rect.x + rect.w - clampedRadius - cy = rect.y + rect.h - clampedRadius - signX = 1 - signY = 1 // Bottom-right - case 3: - cx = rect.x + clampedRadius - cy = rect.y + rect.h - clampedRadius - signX = -1 - signY = 1 // Bottom-left - case: - return - } + switch j { + case 0: + cx = rect.x + clampedRadius + cy = rect.y + clampedRadius + signX = -1 + signY = -1 + // Top-left + case 1: + cx = rect.x + rect.w - clampedRadius + cy = rect.y + clampedRadius + signX = 1 + signY = -1 // Top-right + case 2: + cx = rect.x + rect.w - clampedRadius + cy = rect.y + rect.h - clampedRadius + signX = 1 + signY = 1 // Bottom-right + case 3: + cx = rect.x + clampedRadius + cy = rect.y + rect.h - clampedRadius + signX = -1 + signY = 1 // Bottom-left + case: + return + } - vertices[vertexCount + 0] = (sdl.Vertex) { - { - cx + sdl.cosf(angle1) * clampedRadius * signX, - cy + sdl.sinf(angle1) * clampedRadius * signY, - }, - color, - {0, 0}, - } - vertices[vertexCount + 1] = (sdl.Vertex) { - { - cx + sdl.cosf(angle2) * clampedRadius * signX, - cy + sdl.sinf(angle2) * clampedRadius * signY, - }, - color, - {0, 0}, - } + vertices[vertexCount + 0] = {{cx + math.cos(angle1) * clampedRadius * signX, cy + math.sin(angle1) * clampedRadius * signY}, color, {0, 0}} + vertices[vertexCount + 1] = {{cx + math.cos(angle2) * clampedRadius * signX, cy + math.sin(angle2) * clampedRadius * signY}, color, {0, 0}} - vertexCount += 2 + vertexCount += 2 - indices[indexCount + 0] = j // Connect to corresponding central rectangle vertex - indices[indexCount + 1] = vertexCount - 2 - indices[indexCount + 2] = vertexCount - 1 - indexCount += 3 - } - } + indices[indexCount + 0] = j // Connect to corresponding central rectangle vertex + indices[indexCount + 1] = vertexCount - 2 + indices[indexCount + 2] = vertexCount - 1 + indexCount += 3 + } + } - //Define edge rectangles - // Top edge - vertices[vertexCount + 0] = (sdl.Vertex){{rect.x + clampedRadius, rect.y}, color, {0, 0}} //TL - vertices[vertexCount + 1] = (sdl.Vertex) { - {rect.x + rect.w - clampedRadius, rect.y}, - color, - {1, 0}, - } //TR + //Define edge rectangles + // Top edge + vertices[vertexCount + 0] = {{rect.x + clampedRadius, rect.y}, color, {0, 0}} //TL + vertices[vertexCount + 1] = {{rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0}} //TR - vertexCount += 2 + vertexCount += 2 - indices[indexCount + 0] = 0 - indices[indexCount + 1] = vertexCount - 2 //TL - indices[indexCount + 2] = vertexCount - 1 //TR - indices[indexCount + 3] = 1 - indices[indexCount + 4] = 0 - indices[indexCount + 5] = vertexCount - 1 //TR - indexCount += 6 - // Right edge - vertices[vertexCount + 0] = (sdl.Vertex) { - {rect.x + rect.w, rect.y + clampedRadius}, - color, - {1, 0}, - } //RT - vertices[vertexCount + 1] = (sdl.Vertex) { - {rect.x + rect.w, rect.y + rect.h - clampedRadius}, - color, - {1, 1}, - } //RB - vertexCount += 2 + indices[indexCount + 0] = 0 + indices[indexCount + 1] = vertexCount - 2 //TL + indices[indexCount + 2] = vertexCount - 1 //TR + indices[indexCount + 3] = 1 + indices[indexCount + 4] = 0 + indices[indexCount + 5] = vertexCount - 1 //TR + indexCount += 6 + // Right edge + vertices[vertexCount + 0] = {{rect.x + rect.w, rect.y + clampedRadius}, color, {1, 0}} //RT + vertices[vertexCount + 1] = {{rect.x + rect.w, rect.y + rect.h - clampedRadius}, color, {1, 1}} //RB + vertexCount += 2 - indices[indexCount + 0] = 1 - indices[indexCount + 1] = vertexCount - 2 //RT - indices[indexCount + 2] = vertexCount - 1 //RB - indices[indexCount + 3] = 2 - indices[indexCount + 4] = 1 - indices[indexCount + 5] = vertexCount - 1 //RB - indexCount += 6 + indices[indexCount + 0] = 1 + indices[indexCount + 1] = vertexCount - 2 //RT + indices[indexCount + 2] = vertexCount - 1 //RB + indices[indexCount + 3] = 2 + indices[indexCount + 4] = 1 + indices[indexCount + 5] = vertexCount - 1 //RB + indexCount += 6 - // Bottom edge - vertices[vertexCount + 0] = (sdl.Vertex) { - {rect.x + rect.w - clampedRadius, rect.y + rect.h}, - color, - {1, 1}, - } //BR - vertices[vertexCount + 1] = (sdl.Vertex) { - {rect.x + clampedRadius, rect.y + rect.h}, - color, - {0, 1}, - } //BL - vertexCount += 2 + // Bottom edge + vertices[vertexCount + 0] = {{rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1}} //BR + vertices[vertexCount + 1] = {{rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1}} //BL + vertexCount += 2 - indices[indexCount + 0] = 2 - indices[indexCount + 1] = vertexCount - 2 //BR - indices[indexCount + 2] = vertexCount - 1 //BL - indices[indexCount + 3] = 3 - indices[indexCount + 4] = 2 - indices[indexCount + 5] = vertexCount - 1 //BL - indexCount += 6 + indices[indexCount + 0] = 2 + indices[indexCount + 1] = vertexCount - 2 //BR + indices[indexCount + 2] = vertexCount - 1 //BL + indices[indexCount + 3] = 3 + indices[indexCount + 4] = 2 + indices[indexCount + 5] = vertexCount - 1 //BL + indexCount += 6 - // Left edge - vertices[vertexCount + 0] = (sdl.Vertex) { - {rect.x, rect.y + rect.h - clampedRadius}, - color, - {0, 1}, - } //LB - vertices[vertexCount + 1] = (sdl.Vertex){{rect.x, rect.y + clampedRadius}, color, {0, 0}} //LT - vertexCount += 2 + // Left edge + vertices[vertexCount + 0] = {{rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1}} //LB + vertices[vertexCount + 1] = {{rect.x, rect.y + clampedRadius}, color, {0, 0}} //LT + vertexCount += 2 - indices[indexCount + 0] = 3 - indices[indexCount + 1] = vertexCount - 2 //LB - indices[indexCount + 2] = vertexCount - 1 //LT - indices[indexCount + 3] = 0 - indices[indexCount + 4] = 3 - indices[indexCount + 5] = vertexCount - 1 //LT - indexCount += 6 + indices[indexCount + 0] = 3 + indices[indexCount + 1] = vertexCount - 2 //LB + indices[indexCount + 2] = vertexCount - 1 //LT + indices[indexCount + 3] = 0 + indices[indexCount + 4] = 3 + indices[indexCount + 5] = vertexCount - 1 //LT + indexCount += 6 - // Render everything - sdl.RenderGeometry( - rendererData.renderer, - nil, - raw_data(vertices), - vertexCount, - raw_data(indices), - indexCount, - ) + // Render everything + sdl.RenderGeometry(rendererData.renderer, nil, raw_data(vertices), vertexCount, raw_data(indices), indexCount) } -sdl_Clay_RenderArc :: proc( - rendererData: ^Clay_SDL3RendererData, - center: sdl.FPoint, - radius: f32, - startAngle: f32, - endAngle: f32, - thickness: f32, - color: clay.Color, -) { - sdl.SetRenderDrawColor( - rendererData.renderer, - u8(color.r), - u8(color.g), - u8(color.b), - u8(color.a), - ) +@(private = "file") +render_arc :: proc(rendererData: ^Clay_SDL_Render_Data, center: sdl.FPoint, radius: f32, startAngle: f32, endAngle: f32, thickness: f32, color: clay.Color) { + sdl.SetRenderDrawColor(rendererData.renderer, clay_to_sdl_color(color)) - radStart := startAngle * (math.PI / 180.0) - radEnd := endAngle * (math.PI / 180.0) + radStart := math.to_radians(startAngle) + radEnd := math.to_radians(endAngle) - numCircleSegments := sdl.max(NUM_CIRCLE_SEGMENTS, i32(radius * 1.5)) //increase circle segments for larger circles, 1.5 is arbitrary. + numCircleSegments := max(NUM_CIRCLE_SEGMENTS, i32(radius * 1.5)) //increase circle segments for larger circles, 1.5 is arbitrary. - angleStep := (radEnd - radStart) / f32(numCircleSegments) - thicknessStep: f32 = 0.4 //arbitrary value to avoid overlapping lines. Changing THICKNESS_STEP or numCircleSegments might cause artifacts. + angleStep := (radEnd - radStart) / f32(numCircleSegments) + thicknessStep: f32 = 0.4 //arbitrary value to avoid overlapping lines. Changing THICKNESS_STEP or numCircleSegments might cause artifacts. - for t := thicknessStep; t < thickness - thicknessStep; t += thicknessStep { - points := make([]sdl.FPoint, numCircleSegments + 1, allocator = context.temp_allocator) - clampedRadius := sdl.max(radius - t, 1) + for t := thicknessStep; t < thickness - thicknessStep; t += thicknessStep { + points := make([]sdl.FPoint, numCircleSegments + 1, allocator = context.temp_allocator) + clampedRadius := max(radius - t, 1) - for i in 0 ..= numCircleSegments { - angle := radStart + f32(i) * angleStep - points[i] = (sdl.FPoint) { - sdl.roundf(center.x + sdl.cosf(angle) * clampedRadius), - sdl.roundf(center.y + sdl.sinf(angle) * clampedRadius), - } - } - sdl.RenderLines(rendererData.renderer, raw_data(points), numCircleSegments + 1) - } + for i in 0 ..= numCircleSegments { + angle := radStart + f32(i) * angleStep + points[i] = sdl.FPoint{math.round(center.x + math.cos(angle) * clampedRadius), math.round(center.y + math.sin(angle) * clampedRadius)} + } + sdl.RenderLines(rendererData.renderer, raw_data(points), numCircleSegments + 1) + } } -currentClippingRectangle: sdl.Rect +@(private = "file") +current_clipping_rect: sdl.Rect -sdl_Clay_RenderClayCommands :: proc( - rendererData: ^Clay_SDL3RendererData, - rcommands: ^clay.ClayArray(clay.RenderCommand), -) { - for i in 0 ..< rcommands.length { - rcmd := clay.RenderCommandArray_Get(rcommands, i) - bounding_box := rcmd.boundingBox - rect := sdl.FRect{bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height} - - #partial switch (rcmd.commandType) { - case .Rectangle: - config := &rcmd.renderData.rectangle - sdl.SetRenderDrawBlendMode(rendererData.renderer, sdl.BLENDMODE_BLEND) - sdl.SetRenderDrawColor( - rendererData.renderer, - u8(config.backgroundColor.r), - u8(config.backgroundColor.g), - u8(config.backgroundColor.b), - u8(config.backgroundColor.a), - ) - if (config.cornerRadius.topLeft > 0) { - sdl_Clay_RenderFillRoundedRect( - rendererData, - rect, - config.cornerRadius.topLeft, - config.backgroundColor, - ) - } else { - sdl.RenderFillRect(rendererData.renderer, &rect) - } - - case .Text: - config := &rcmd.renderData.text - font := rendererData.fonts[config.fontId] - ttf.SetFontSize(font, px_to_pt(f32(config.fontSize))) - text := ttf.CreateText( - rendererData.textEngine, - font, - cstring(config.stringContents.chars), - uint(config.stringContents.length), - ) - ttf.SetTextColor( - text, - u8(config.textColor.r), - u8(config.textColor.g), - u8(config.textColor.b), - u8(config.textColor.a), - ) - ttf.DrawRendererText(text, rect.x, rect.y) - ttf.DestroyText(text) - - case .Border: - config := &rcmd.renderData.border - - minRadius := sdl.min(rect.w, rect.h) / 2 - clampedRadii := clay.CornerRadius { - topLeft = sdl.min(config.cornerRadius.topLeft, minRadius), - topRight = sdl.min(config.cornerRadius.topRight, minRadius), - bottomLeft = sdl.min(config.cornerRadius.bottomLeft, minRadius), - bottomRight = sdl.min(config.cornerRadius.bottomRight, minRadius), - } - //edges - sdl.SetRenderDrawColor( - rendererData.renderer, - u8(config.color.r), - u8(config.color.g), - u8(config.color.b), - u8(config.color.a), - ) - if (config.width.left > 0) { - starting_y := rect.y + clampedRadii.topLeft - length := rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft - line := sdl.FRect{rect.x - 1, starting_y, f32(config.width.left), length} - sdl.RenderFillRect(rendererData.renderer, &line) - } - if (config.width.right > 0) { - starting_x := rect.x + rect.w - f32(config.width.right) + 1 - starting_y := rect.y + clampedRadii.topRight - length := rect.h - clampedRadii.topRight - clampedRadii.bottomRight - line := sdl.FRect{starting_x, starting_y, f32(config.width.right), length} - sdl.RenderFillRect(rendererData.renderer, &line) - } - if (config.width.top > 0) { - starting_x := rect.x + clampedRadii.topLeft - length := rect.w - clampedRadii.topLeft - clampedRadii.topRight - line := sdl.FRect{starting_x, rect.y - 1, length, f32(config.width.top)} - sdl.RenderFillRect(rendererData.renderer, &line) - } - if (config.width.bottom > 0) { - starting_x := rect.x + clampedRadii.bottomLeft - starting_y := rect.y + rect.h - f32(config.width.bottom) + 1 - length := rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight - line := sdl.FRect{starting_x, starting_y, length, f32(config.width.bottom)} - sdl.SetRenderDrawColor( - rendererData.renderer, - u8(config.color.r), - u8(config.color.g), - u8(config.color.b), - u8(config.color.a), - ) - sdl.RenderFillRect(rendererData.renderer, &line) - } - //corners - if (config.cornerRadius.topLeft > 0) { - centerX := rect.x + clampedRadii.topLeft - 1 - centerY := rect.y + clampedRadii.topLeft - 1 - sdl_Clay_RenderArc( - rendererData, - (sdl.FPoint){centerX, centerY}, - clampedRadii.topLeft, - 180.0, - 270.0, - f32(config.width.top), - config.color, - ) - } - if (config.cornerRadius.topRight > 0) { - centerX := rect.x + rect.w - clampedRadii.topRight - centerY := rect.y + clampedRadii.topRight - 1 - sdl_Clay_RenderArc( - rendererData, - (sdl.FPoint){centerX, centerY}, - clampedRadii.topRight, - 270.0, - 360.0, - f32(config.width.top), - config.color, - ) - } - if (config.cornerRadius.bottomLeft > 0) { - centerX := rect.x + clampedRadii.bottomLeft - 1 - centerY := rect.y + rect.h - clampedRadii.bottomLeft - sdl_Clay_RenderArc( - rendererData, - (sdl.FPoint){centerX, centerY}, - clampedRadii.bottomLeft, - 90.0, - 180.0, - f32(config.width.bottom), - config.color, - ) - } - if (config.cornerRadius.bottomRight > 0) { - centerX := rect.x + rect.w - clampedRadii.bottomRight - centerY := rect.y + rect.h - clampedRadii.bottomRight - sdl_Clay_RenderArc( - rendererData, - (sdl.FPoint){centerX, centerY}, - clampedRadii.bottomRight, - 0.0, - 90.0, - f32(config.width.bottom), - config.color, - ) - } - - - case .ScissorStart: - boundingBox := rcmd.boundingBox - currentClippingRectangle = (sdl.Rect) { - x = i32(boundingBox.x), - y = i32(boundingBox.y), - w = i32(boundingBox.width), - h = i32(boundingBox.height), - } - sdl.SetRenderClipRect(rendererData.renderer, ¤tClippingRectangle) - - case .ScissorEnd: - sdl.SetRenderClipRect(rendererData.renderer, nil) - - - case .Image: - texture := (^sdl.Texture)(rcmd.renderData.image.imageData) - dest := sdl.FRect{rect.x, rect.y, rect.w, rect.h} - sdl.RenderTexture(rendererData.renderer, texture, nil, &dest) - - - case: - sdl.Log("Unknown render command type: %d", rcmd.commandType) - } - } +clay_to_sdl_color :: proc(color: clay.Color) -> (r, g, b, a: u8) { + return expand_values(linalg.array_cast(color, u8)) +} + +sdl_render_clay_commands :: proc(renderer_data: ^Clay_SDL_Render_Data, commands: ^clay.ClayArray(clay.RenderCommand)) { + for i in 0 ..< commands.length { + cmd := clay.RenderCommandArray_Get(commands, i) + bounding_box := cmd.boundingBox + rect := sdl.FRect{bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height} + + #partial switch cmd.commandType { + case .Rectangle: + config := &cmd.renderData.rectangle + sdl.SetRenderDrawBlendMode(renderer_data.renderer, sdl.BLENDMODE_BLEND) + sdl.SetRenderDrawColor(renderer_data.renderer, clay_to_sdl_color(config.backgroundColor)) + if config.cornerRadius.topLeft > 0 { + fill_rounded_rect(renderer_data, rect, config.cornerRadius.topLeft, config.backgroundColor) + } else { + sdl.RenderFillRect(renderer_data.renderer, &rect) + } + + case .Text: + config := &cmd.renderData.text + font := renderer_data.fonts[config.fontId] + ttf.SetFontSize(font, px_to_pt(f32(config.fontSize))) + text := ttf.CreateText(renderer_data.text_engine, font, cstring(config.stringContents.chars), uint(config.stringContents.length)) + ttf.SetTextColor(text, clay_to_sdl_color(config.textColor)) + ttf.DrawRendererText(text, rect.x, rect.y) + ttf.DestroyText(text) + + case .Border: + config := &cmd.renderData.border + + minRadius := min(rect.w, rect.h) / 2 + clampedRadii := clay.CornerRadius { + topLeft = min(config.cornerRadius.topLeft, minRadius), + topRight = min(config.cornerRadius.topRight, minRadius), + bottomLeft = min(config.cornerRadius.bottomLeft, minRadius), + bottomRight = min(config.cornerRadius.bottomRight, minRadius), + } + //edges + sdl.SetRenderDrawColor(renderer_data.renderer, clay_to_sdl_color(config.color)) + if config.width.left > 0 { + starting_y := rect.y + clampedRadii.topLeft + length := rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft + line := sdl.FRect{rect.x - 1, starting_y, f32(config.width.left), length} + sdl.RenderFillRect(renderer_data.renderer, &line) + } + if config.width.right > 0 { + starting_x := rect.x + rect.w - f32(config.width.right) + 1 + starting_y := rect.y + clampedRadii.topRight + length := rect.h - clampedRadii.topRight - clampedRadii.bottomRight + line := sdl.FRect{starting_x, starting_y, f32(config.width.right), length} + sdl.RenderFillRect(renderer_data.renderer, &line) + } + if config.width.top > 0 { + starting_x := rect.x + clampedRadii.topLeft + length := rect.w - clampedRadii.topLeft - clampedRadii.topRight + line := sdl.FRect{starting_x, rect.y - 1, length, f32(config.width.top)} + sdl.RenderFillRect(renderer_data.renderer, &line) + } + if config.width.bottom > 0 { + starting_x := rect.x + clampedRadii.bottomLeft + starting_y := rect.y + rect.h - f32(config.width.bottom) + 1 + length := rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight + line := sdl.FRect{starting_x, starting_y, length, f32(config.width.bottom)} + sdl.SetRenderDrawColor(renderer_data.renderer, clay_to_sdl_color(config.color)) + sdl.RenderFillRect(renderer_data.renderer, &line) + } + //corners + if config.cornerRadius.topLeft > 0 { + centerX := rect.x + clampedRadii.topLeft - 1 + centerY := rect.y + clampedRadii.topLeft - 1 + render_arc(renderer_data, {centerX, centerY}, clampedRadii.topLeft, 180, 270, f32(config.width.top), config.color) + } + if config.cornerRadius.topRight > 0 { + centerX := rect.x + rect.w - clampedRadii.topRight + centerY := rect.y + clampedRadii.topRight - 1 + render_arc(renderer_data, {centerX, centerY}, clampedRadii.topRight, 270, 360, f32(config.width.top), config.color) + } + if config.cornerRadius.bottomLeft > 0 { + centerX := rect.x + clampedRadii.bottomLeft - 1 + centerY := rect.y + rect.h - clampedRadii.bottomLeft + render_arc(renderer_data, {centerX, centerY}, clampedRadii.bottomLeft, 90, 180, f32(config.width.bottom), config.color) + } + if config.cornerRadius.bottomRight > 0 { + centerX := rect.x + rect.w - clampedRadii.bottomRight + centerY := rect.y + rect.h - clampedRadii.bottomRight + render_arc(renderer_data, {centerX, centerY}, clampedRadii.bottomRight, 0, 90, f32(config.width.bottom), config.color) + } + + + case .ScissorStart: + boundingBox := cmd.boundingBox + current_clipping_rect = sdl.Rect { + x = i32(boundingBox.x), + y = i32(boundingBox.y), + w = i32(boundingBox.width), + h = i32(boundingBox.height), + } + sdl.SetRenderClipRect(renderer_data.renderer, ¤t_clipping_rect) + + case .ScissorEnd: + sdl.SetRenderClipRect(renderer_data.renderer, nil) + + + case .Image: + texture := (^sdl.Texture)(cmd.renderData.image.imageData) + dest := sdl.FRect{rect.x, rect.y, rect.w, rect.h} + sdl.RenderTexture(renderer_data.renderer, texture, nil, &dest) + + + case: + sdl.Log("Unknown render command type: %d", cmd.commandType) + } + } }