diff --git a/examples/main.odin b/examples/main.odin index 236a3a8..988c959 100644 --- a/examples/main.odin +++ b/examples/main.odin @@ -18,9 +18,9 @@ device: ^sdl.GPUDevice debug_enabled := false body_text := clay.TextElementConfig { - fontId = renderer.JETBRAINS_MONO_REGULAR, - fontSize = 44, - textColor = { 1.0, 1.0, 1.0, 1.0 }, + fontId = renderer.JETBRAINS_MONO_REGULAR, + fontSize = 44, + textColor = {0.0, 0.0, 0.0, 255.0}, } main :: proc() { @@ -88,7 +88,7 @@ main :: proc() { log.error("Failed to initialize SDL:", sdl.GetError()) } - window = sdl.CreateWindow("System Controller", WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_FLAGS) + window = sdl.CreateWindow("Test", WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_FLAGS) if window == nil { log.error("Failed to create window:", sdl.GetError()) @@ -142,7 +142,9 @@ main :: proc() { os.exit(1) } - if update(cmd_buffer, frame_time - last_frame_time) { + should_quit := update(cmd_buffer, frame_time - last_frame_time) + + if should_quit { log.debug("User command to quit") break program } @@ -163,11 +165,8 @@ destroy :: proc() { update :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, delta_time: u64) -> bool { frame_time := f32(delta_time) / 1000.0 input := input() - - render_cmds: clay.ClayArray(clay.RenderCommand) = layout() - + render_cmds := layout() renderer.prepare(device, window, cmd_buffer, &render_cmds, input.mouse_delta, frame_time) - return input.should_quit } @@ -221,14 +220,32 @@ layout :: proc() -> clay.ClayArray(clay.RenderCommand) { layoutDirection = .TopToBottom, sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {x = .Center, y = .Center}, - childGap = 16, + childGap = 32, }, - backgroundColor = {0.2, 0.2, 0.2, 1.0}, + backgroundColor = {200.0, 200.0, 200.0, 255.0}, }, ) { - clay.Text("3D SCENE", &body_text) + if clay.UI()( + { + id = clay.ID("RoundedRect"), + backgroundColor = {255.0, 100.0, 100.0, 255.0}, + cornerRadius = clay.CornerRadius { + topLeft = 10, + topRight = 20, + bottomLeft = 40, + bottomRight = 0, + }, + border = clay.BorderElementConfig { + color = {0.0, 0.0, 0.0, 255.0}, + width = clay.BorderAll(5), + }, + layout = {sizing = {clay.SizingFixed(240), clay.SizingFixed(80)}}, + }, + ) { + } + + clay.Text("Test Text", &body_text) } return clay.EndLayout() } - diff --git a/library/clay/clay.odin b/library/clay/clay.odin index 9d7a73a..ad1e1ee 100644 --- a/library/clay/clay.odin +++ b/library/clay/clay.odin @@ -103,7 +103,7 @@ TextAlignment :: enum EnumBackingType { } TextElementConfig :: struct { - userData: rawptr, + userData: rawptr, textColor: Color, fontId: u16, fontSize: u16, @@ -113,9 +113,12 @@ TextElementConfig :: struct { textAlignment: TextAlignment, } +AspectRatioElementConfig :: struct { + aspectRatio: f32, +} + ImageElementConfig :: struct { imageData: rawptr, - sourceDimensions: Dimensions, } CustomElementConfig :: struct { @@ -135,9 +138,10 @@ BorderElementConfig :: struct { width: BorderWidth, } -ScrollElementConfig :: struct { - horizontal: bool, - vertical: bool, +ClipElementConfig :: struct { + horizontal: bool, // clip overflowing elements on the "X" axis + vertical: bool, // clip overflowing elements on the "Y" axis + childOffset: Vector2, // offsets the [X,Y] positions of all child elements, primarily for scrolling containers } FloatingAttachPointType :: enum EnumBackingType { @@ -169,6 +173,11 @@ FloatingAttachToElement :: enum EnumBackingType { Root, } +FloatingClipToElement :: enum EnumBackingType { + None, + AttachedParent, +} + FloatingElementConfig :: struct { offset: Vector2, expand: Dimensions, @@ -177,6 +186,7 @@ FloatingElementConfig :: struct { attachment: FloatingAttachPoints, pointerCaptureMode: PointerCaptureMode, attachTo: FloatingAttachToElement, + clipTo: FloatingClipToElement, } TextRenderData :: struct { @@ -196,7 +206,6 @@ RectangleRenderData :: struct { ImageRenderData :: struct { backgroundColor: Color, cornerRadius: CornerRadius, - sourceDimensions: Dimensions, imageData: rawptr, } @@ -235,7 +244,7 @@ ScrollContainerData :: struct { scrollPosition: ^Vector2, scrollContainerDimensions: Dimensions, contentDimensions: Dimensions, - config: ScrollElementConfig, + config: ClipElementConfig, // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. found: bool, } @@ -329,16 +338,17 @@ ClayArray :: struct($type: typeid) { } ElementDeclaration :: struct { - id: ElementId, - layout: LayoutConfig, + id: ElementId, + layout: LayoutConfig, backgroundColor: Color, - cornerRadius: CornerRadius, - image: ImageElementConfig, - floating: FloatingElementConfig, - custom: CustomElementConfig, - scroll: ScrollElementConfig, - border: BorderElementConfig, - userData: rawptr, + cornerRadius: CornerRadius, + aspectRatio: AspectRatioElementConfig, + image: ImageElementConfig, + floating: FloatingElementConfig, + custom: CustomElementConfig, + clip: ClipElementConfig, + border: BorderElementConfig, + userData: rawptr, } ErrorType :: enum EnumBackingType { @@ -385,6 +395,7 @@ foreign Clay { Hovered :: proc() -> bool --- OnHover :: proc(onHoverFunction: proc "c" (id: ElementId, pointerData: PointerData, userData: rawptr), userData: rawptr) --- PointerOver :: proc(id: ElementId) -> bool --- + GetScrollOffset :: proc() -> Vector2 --- GetScrollContainerData :: proc(id: ElementId) -> ScrollContainerData --- SetMeasureTextFunction :: proc(measureTextFunction: proc "c" (text: StringSlice, config: ^TextElementConfig, userData: rawptr) -> Dimensions, userData: rawptr) --- SetQueryScrollOffsetFunction :: proc(queryScrollOffsetFunction: proc "c" (elementId: u32, userData: rawptr) -> Vector2, userData: rawptr) --- @@ -437,6 +448,14 @@ PaddingAll :: proc(allPadding: u16) -> Padding { return { left = allPadding, right = allPadding, top = allPadding, bottom = allPadding } } +BorderOutside :: proc(width: u16) -> BorderWidth { + return {width, width, width, width, 0} +} + +BorderAll :: proc(width: u16) -> BorderWidth { + return {width, width, width, width, width} +} + CornerRadiusAll :: proc(radius: f32) -> CornerRadius { return CornerRadius{radius, radius, radius, radius} } @@ -467,4 +486,4 @@ ID :: proc(label: string, index: u32 = 0) -> ElementId { ID_LOCAL :: proc(label: string, index: u32 = 0) -> ElementId { return _HashString(MakeString(label), index, _GetParentElementId()) -} +} \ No newline at end of file diff --git a/library/clay/linux-arm64/clay.a b/library/clay/linux-arm64/clay.a index d40da7c..00879be 100644 Binary files a/library/clay/linux-arm64/clay.a and b/library/clay/linux-arm64/clay.a differ diff --git a/library/clay/linux/clay.a b/library/clay/linux/clay.a index 8352290..e524a3b 100644 Binary files a/library/clay/linux/clay.a and b/library/clay/linux/clay.a differ diff --git a/library/clay/macos-arm64/clay.a b/library/clay/macos-arm64/clay.a index 8c91161..b609cc7 100644 Binary files a/library/clay/macos-arm64/clay.a and b/library/clay/macos-arm64/clay.a differ diff --git a/library/clay/macos/clay.a b/library/clay/macos/clay.a index e56ff15..f614e4e 100644 Binary files a/library/clay/macos/clay.a and b/library/clay/macos/clay.a differ diff --git a/library/clay/wasm/clay.o b/library/clay/wasm/clay.o index fc3cf41..51a72dc 100644 Binary files a/library/clay/wasm/clay.o and b/library/clay/wasm/clay.o differ diff --git a/library/clay/windows/clay.lib b/library/clay/windows/clay.lib index 8bc2215..72a8cb3 100644 Binary files a/library/clay/windows/clay.lib and b/library/clay/windows/clay.lib differ diff --git a/library/sdl3_ttf/sdl3_ttf.odin b/library/sdl3_ttf/sdl3_ttf.odin deleted file mode 100644 index 71187fb..0000000 --- a/library/sdl3_ttf/sdl3_ttf.odin +++ /dev/null @@ -1,184 +0,0 @@ -package sdl3_ttf - -import sdl "vendor:sdl3" -import "core:c" - -foreign import lib "system:SDL3_ttf" - -Font :: struct {} - -Text :: struct { - text: cstring, - num_lines: c.int, - refcount: c.int, - internal: rawptr, -} - -TextEngine :: struct {} - -Direction :: enum c.int { - LTR = 0, - RTL, - TTB, - BTT, -} - -// Normal == empty -FontStyleFlag :: enum u32 { - BOLD = 0, - ITALIC = 1, - UNDERLINE = 2, - STRIKETHROUGH = 3, -} - -FontStyleFlags :: bit_set[FontStyleFlag;u32] -FONT_STYLE_NORMAL :: FontStyleFlags{} -FONT_STYLE_BOLD :: FontStyleFlags{.BOLD} -FONT_STYLE_ITALIC :: FontStyleFlags{.ITALIC} -FONT_STYLE_UNDERLINE :: FontStyleFlags{.UNDERLINE} -FONT_STYLE_STRIKETHROUGH :: FontStyleFlags{.STRIKETHROUGH} - -HintingFlags :: enum c.int { - NORMAL = 0, - LIGHT, - MONO, - NONE, - LIGHT_SUBPIXEL, -} - -TTF_PROP_FONT_OUTLINE_LINE_CAP_NUMBER :: "SDL_ttf.font.outline.line_cap" -TTF_PROP_FONT_OUTLINE_LINE_JOIN_NUMBER :: "SDL_ttf.font.outline.line_join" -TTF_PROP_FONT_OUTLINE_MITER_LIMIT_NUMBER :: "SDL_ttf.font.outline.miter_limit" - -HorizontalAlignment :: enum c.int { - INVALID = -1, - LEFT, - CENTER, - RIGHT, -} - -GPUAtlasDrawSequence :: struct { - atlas_texture: ^sdl.GPUTexture, - vertex_positions: [^]sdl.FPoint, - uvs: [^]sdl.FPoint, // Normalized - num_verticies: c.int, - indices: [^]c.int, - num_indices: c.int, - next: ^GPUAtlasDrawSequence, // If nil, this is the last text in the sequence -} - -GPUTextEngineWinding :: enum c.int { - INVALID = -1, - CLOCKWISE, - COUNTERCLOCKWISE, -} - -SubStringFlag :: enum u32 { - TEXT_START, - LINE_START, - LINE_END, - TEXT_END, -} - -SubString :: struct { - flags: SubStringFlag, - offset: c.int, - length: c.int, - line_index: c.int, - cluster_index: c.int, - rect: sdl.Rect, -} - -/// General -@(default_calling_convention = "c", link_prefix = "TTF_") -foreign lib { - Init :: proc() -> bool --- - CreateGPUTextEngine :: proc(device: ^sdl.GPUDevice) -> ^TextEngine --- - DestroyGPUTextEngine :: proc(engine: ^TextEngine) --- - Quit :: proc() --- -} - -/// Fonts -@(default_calling_convention = "c", link_prefix = "TTF_") -foreign lib { - CloseFont :: proc(font: ^Font) --- - FontHasGlyph :: proc(font: ^Font, glyph: u32) -> bool --- - FontIsFixedWidth :: proc(font: ^Font) -> bool --- - GetFontAscent :: proc(font: ^Font) -> c.int --- - GetFontDescent :: proc(font: ^Font) -> c.int --- - GetFontDirection :: proc(font: ^Font) -> Direction --- - GetFontDPI :: proc(font: ^Font, hdpi: ^c.int, vdpi: ^c.int) -> bool --- - GetFontFamilyName :: proc(font: ^Font) -> cstring --- - GetFontGeneration :: proc(font: ^Font) -> u32 --- - GetFontHeight :: proc(font: ^Font) -> c.int --- - GetFontHinting :: proc(font: ^Font) -> HintingFlags --- - GetFontKerning :: proc(font: ^Font) -> bool --- - /// Returns the font's recommended spacing - GetFontLineSkip :: proc(font: ^Font) -> c.int --- - GetFontOutline :: proc(font: ^Font) -> c.int --- - GetFontProperties :: proc(font: ^Font) -> sdl.PropertiesID --- - GetFontSize :: proc(font: ^Font) -> f32 --- - GetFontStyle :: proc(font: ^Font) -> FontStyleFlags --- - GetFontStyleName :: proc(font: ^Font) -> cstring --- - GetFontWrapAlignment :: proc(font: ^Font) -> HorizontalAlignment --- - GetFreeTypeVersion :: proc(major: ^c.int, minor: ^c.int, patch: ^c.int) --- - GetGlyphMetrics :: proc(font: ^Font, glyph: u32, min_x: ^c.int, max_x: ^c.int, min_y: ^c.int, max_y: ^c.int, advance: ^c.int) -> bool --- - GetGlyphScript :: proc(glyph: u32, script: ^c.char, script_size: c.size_t) -> bool --- - /// `stream`: A `sdl.IOStream` to provide a font's file data - /// `close_io`: Close src when the font is closed, false to leave it open - /// `point_size`: Font point size to use for the newly-opened font - OpenFontIO :: proc(stream: ^sdl.IOStream, close_io: bool, point_size: f32) -> ^Font --- - OpenFont :: proc(file: cstring, point_size: f32) -> ^Font --- - SetFontDirection :: proc(font: ^Font, direction: Direction) -> bool --- - SetFontHinting :: proc(font: ^Font, hinting_flags: HintingFlags) --- - SetFontKerning :: proc(font: ^Font, enabled: bool) --- - SetFontLineSkip :: proc(font: ^Font, lineskip: c.int) --- - SetFontOutline :: proc(font: ^Font, outline: c.int) -> bool --- - SetFontScript :: proc(font: ^Font, script: cstring) -> bool --- - SetFontSize :: proc(font: ^Font, pt_size: f32) -> bool --- - SetFontSizeDPI :: proc(font: ^Font, pt_size: f32, hdpi: c.int, vdpi: c.int) -> bool --- - SetFontStyle :: proc(font: ^Font, style: FontStyleFlags) --- - SetFontWrapAlignment :: proc(font: ^Font, horizontal_alignment: HorizontalAlignment) --- - SetGPUTextEngineWinding :: proc(engine: ^TextEngine, winding: GPUTextEngineWinding) --- -} - -/// Text -@(default_calling_convention = "c", link_prefix = "TTF_") -foreign lib { - AppendTextString :: proc(text: ^Text, str: cstring, length: c.size_t) -> bool --- - CreateText :: proc(engine: ^TextEngine, font: ^Font, text: cstring, length: c.size_t) -> ^Text --- - DeleteTextString :: proc(text: ^Text, offset: c.int, length: c.int) -> bool --- - DestroyText :: proc(text: ^Text) --- - GetGPUTextDrawData :: proc(text: ^Text) -> ^GPUAtlasDrawSequence --- - GetGPUTextEngineWinding :: proc(engine: ^TextEngine) -> GPUTextEngineWinding --- - GetNextTextSubString :: proc(text: ^Text, substring: ^SubString, next: ^SubString) -> bool --- - GetPreviousTextSubString :: proc(text: ^Text, substring: ^SubString, previous: ^SubString) -> bool --- - /// Calculate the dimensions of a rendered string of UTF-8 text. - GetStringSize :: proc(font: ^Font, text: cstring, length: c.size_t, w: ^c.int, h: ^c.int) -> bool --- - GetStringSizeWrapped :: proc(font: ^Font, text: cstring, length: c.size_t, wrap_width: c.int, w: ^c.int, h: ^c.int) -> bool --- - GetTextColor :: proc(text: ^Text, r: ^u8, g: ^u8, b: ^u8, a: ^u8) -> bool --- - GetTextColorFloat :: proc(text: ^Text, r: ^f32, g: ^f32, b: ^f32, a: ^f32) -> bool --- - GetTextEngine :: proc(text: ^Text) -> ^TextEngine --- - GetTextFont :: proc(text: ^Text) -> ^Font --- - GetTextPosition :: proc(text: ^Text, x: ^c.int, y: ^c.int) -> bool --- - GetTextProperties :: proc(text: ^Text) -> sdl.PropertiesID --- - GetTextSize :: proc(text: ^Text, width: ^c.int, height: ^c.int) -> bool --- - GetTextSubString :: proc(text: ^Text, offset: c.int, substring: ^SubString) -> bool --- - GetTextSubStringForLine :: proc(text: ^Text, line: c.int, substring: ^SubString) -> bool --- - GetTextSubStringForPoint :: proc(text: ^Text, x: c.int, y: c.int, substring: ^SubString) -> bool --- - GetTextSubStringsForRange :: proc(text: ^Text, offset: c.int, length: c.int, count: ^c.int) -> [^]^SubString --- - GetTextWrapping :: proc(text: ^Text, wrap_length: ^c.int) -> bool --- - GetTextWrapWidth :: proc(text: ^Text, wrap_width: ^c.int) -> bool --- - InsertTextString :: proc(text: ^Text, offset: c.int, str: cstring, length: c.size_t) -> bool --- - // Calculate how much of a UTF-8 string will fit in a given width. - MeasureString :: proc(font: ^Font, text: cstring, length: c.size_t, max_width: c.int, measured_width: ^c.int, measured_length: ^c.size_t) -> bool --- - SetTextColor :: proc(text: ^Text, r: u8, g: u8, b: u8, a: u8) -> bool --- - SetTextColorFloat :: proc(text: ^Text, r: f32, g: f32, b: f32, a: f32) -> bool --- - SetTextEngine :: proc(text: ^Text, engine: ^TextEngine) -> bool --- - SetTextFont :: proc(text: ^Text, font: ^Font) -> bool --- - SetTextPosition :: proc(text: ^Text, x: c.int, y: c.int) -> bool --- - SetTextString :: proc(text: ^Text, str: cstring, length: c.size_t) -> bool --- - SetTextWrapping :: proc(text: ^Text, wrap_length: c.int) -> bool --- - SetTextWrapWhitespaceVisible :: proc(text: ^Text, visible: bool) -> bool --- - SetTextWrapWidth :: proc(text: ^Text, wrap_width: c.int) -> bool --- -} diff --git a/renderer/renderer.odin b/renderer/renderer.odin index 6ab6b53..16a7937 100644 --- a/renderer/renderer.odin +++ b/renderer/renderer.odin @@ -6,8 +6,8 @@ import "core:log" import "core:os" import "core:strings" import clay "library:clay" -import sdl_ttf "library:sdl3_ttf" import sdl "vendor:sdl3" +import sdl_ttf "vendor:sdl3/ttf" when ODIN_OS == .Darwin { SHADER_TYPE :: sdl.GPUShaderFormat{.MSL} @@ -151,7 +151,7 @@ prepare :: proc( text_pipeline.cache[render_command.id] = sdl_text } else { // Update text with c_string - sdl_ttf.SetTextString(sdl_text, c_text, 0) + _ = sdl_ttf.SetTextString(sdl_text, c_text, 0) } data := sdl_ttf.GetGPUTextDrawData(sdl_text) @@ -164,7 +164,7 @@ prepare :: proc( Text{sdl_text, {bounds.x, bounds.y}, f32_color(render_data.textColor)}, ) layer.text_instance_len += 1 - layer.text_vertex_len += u32(data.num_verticies) + layer.text_vertex_len += u32(data.num_vertices) layer.text_index_len += u32(data.num_indices) scissor.text_len += 1 } @@ -194,7 +194,7 @@ prepare :: proc( cr := render_data.cornerRadius quad := Quad { position_scale = {bounds.x, bounds.y, bounds.width, bounds.height}, - corner_radii = {cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft}, + corner_radii = {cr.bottomRight, cr.topRight, cr.bottomLeft, cr.topLeft}, color = color, } append(&tmp_quads, quad) @@ -203,14 +203,11 @@ prepare :: proc( case clay.RenderCommandType.Border: render_data := render_command.renderData.border cr := render_data.cornerRadius + //TODO dedicated border pipeline quad := Quad { position_scale = {bounds.x, bounds.y, bounds.width, bounds.height}, - corner_radii = {cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft}, - //TODO: I was using a hack here to get the underlying color of the quad in the layout and then pass it into the - // right border color, but Clay got rid of multi color support for borders so I need to just make a dedicated border pipeline - color = f32_color( - clay.Color{render_data.color.r, render_data.color.g, render_data.color.b, 0.0}, - ), + corner_radii = {cr.bottomRight, cr.topRight, cr.bottomLeft, cr.topLeft}, + color = f32_color(clay.Color{0.0, 0.0, 0.0, 0.0}), border_color = f32_color(render_data.color), // We only support one border width at the moment border_width = f32(render_data.width.top), diff --git a/renderer/text.odin b/renderer/text.odin index 793528b..a744885 100644 --- a/renderer/text.odin +++ b/renderer/text.odin @@ -4,8 +4,8 @@ import "core:c" import "core:log" import "core:mem" import "core:os" -import sdl_ttf "library:sdl3_ttf" import sdl "vendor:sdl3" +import sdl_ttf "vendor:sdl3/ttf" JETBRAINS_MONO_REGULAR: u16 : 0 JETBRAINS_MONO_BOLD: u16 : 1 @@ -45,7 +45,7 @@ get_font :: proc(id: u16, size: u16) -> ^sdl_ttf.Font { os.exit(1) } font = f - sdl_ttf.SetFontSizeDPI(f, f32(size), 72 * i32(dpi_scaling), 72 * i32(dpi_scaling)) + _ = sdl_ttf.SetFontSizeDPI(f, f32(size), 72 * i32(dpi_scaling), 72 * i32(dpi_scaling)) text_pipeline.fonts[id][size] = f } @@ -205,7 +205,7 @@ create_text_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Tex log.error("Could not create text engine") os.exit(1) } - sdl_ttf.SetGPUTextEngineWinding(engine, .COUNTERCLOCKWISE) + sdl_ttf.SetGPUTextEngineWinding(engine, .COUNTER_CLOCKWISE) // Create buffers vertex_buffer := create_buffer( @@ -248,11 +248,10 @@ upload_text :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) { for &text, index in tmp_text { append(&instances, text.position) data := sdl_ttf.GetGPUTextDrawData(text.ref) - for data != nil { - for i in 0 ..< data.num_verticies { - pos := data.vertex_positions[i] - uv := data.uvs[i] + for i in 0 ..< data.num_vertices { + pos := data.xy[i] + uv := data.uv[i] color := text.color append(&vertices, TextVert{{pos.x, -pos.y, uv.x, uv.y}, color}) } @@ -421,7 +420,7 @@ draw_text :: proc( ) index_offset += u32(data.num_indices) - vertex_offset += data.num_verticies + vertex_offset += data.num_vertices data = data.next }