Skip to content

Commit

Permalink
Debug Tools (nicbarker#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicbarker authored Sep 16, 2024
1 parent b3d768c commit a4f90a2
Show file tree
Hide file tree
Showing 21 changed files with 1,374 additions and 366 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ The supported directives are:
- `CLAY_DISABLE_CULLING` - Disables [Visibility Culling](#visibility-culling) of render commands.
- `CLAY_WASM` - Required when targeting Web Assembly.
- `CLAY_OVERFLOW_TRAP` - By default, clay will continue to allow function calls without crashing even when it exhausts all its available pre-allocated memory. This can produce erroneous layout results that are difficult to interpret. If `CLAY_OVERFLOW_TRAP` is defined, clay will raise a `SIGTRAP` signal that will be caught by your debugger. Relies on `signal.h` being available in your environment.
- `CLAY_DEBUG` - Used for debugging clay's internal implementation. Useful if you want to modify or debug clay, or learn how things work. It enables a number of debug features such as preserving source strings for has IDs to make debugging easier.
- `CLAY_DEBUG` - Used for debugging clay's internal implementation. Useful if you want to modify or debug clay, or learn how things work. It enables a number of debug features such as preserving source strings for hash IDs to make debugging easier.
- `CLAY_EXTEND_CONFIG_RECTANGLE` - Provide additional struct members to `CLAY_RECTANGLE_CONFIG` that will be passed through with output render commands.
- `CLAY_EXTEND_CONFIG_TEXT` - Provide additional struct members to `CLAY_TEXT_CONFIG` that will be passed through with output render commands.
- `CLAY_EXTEND_CONFIG_IMAGE` - Provide additional struct members to `CLAY_IMAGE_CONFIG` that will be passed through with output render commands.
Expand All @@ -384,6 +384,20 @@ There are also supported bindings for other languages, including:

- [Odin Bindings](https://github.com/nicbarker/clay/tree/main/bindings/odin)

### Debug Tools

Clay includes built-in UI debugging tools, similar to the "inspector" in browsers such as Chrome or Firefox. These tools are included in `clay.h`, and work by injecting additional render commands into the output [Clay_RenderCommandArray](#clay_rendercommandarray).

As long as the renderer that you're using works correctly, no additional setup or configuration is required to use the debug tools.

To enable the debug tools, use the function `Clay_SetDebugModeEnabled(bool enabled)`. This boolean is persistent and does not need to be set every frame.

The debug tooling by default will render as a panel to the right side of the screen, compressing your layout by its width. The default width is 400 and is currently configurable via the direct mutation of the internal variable `Clay__debugViewWidth`, however this is an internal API and is potentially subject to change.

<img width="1506" alt="Screenshot 2024-09-12 at 12 54 03 PM" src="https://github.com/user-attachments/assets/2d122658-3305-4e27-88d6-44f08c0cb4e6">

_The official Clay website with debug tooling visible_

# API

### Naming Conventions
Expand Down Expand Up @@ -1422,13 +1436,13 @@ switch (renderCommand->commandType) {

`uint32_t CLAY_ID(char *label)`

Generates a `uint32_t` string hash from the provided `char *label`. Used both to generate ids when defining element macros, as well as for referencing ids later when using utility functions such as [Clay_PointerOver](#clay-pointerover)
Generates a `uint32_t` string id from the provided `char *label`. Used both to generate ids when defining element macros, as well as for referencing ids later when using utility functions such as [Clay_PointerOver](#clay-pointerover)

### CLAY_IDI()

`uint32_t CLAY_IDI(char *label, int index)`

Generates a `uint32_t` string hash from the provided `char *label`, combined with the `int index`. Used for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime.
Generates a `uint32_t` string id from the provided `char *label`, combined with the `int index`. Used for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime.

## Data Structures & Definitions

Expand Down
64 changes: 40 additions & 24 deletions bindings/odin/clay-odin/clay.odin
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ BorderData :: struct {
color: Color,
}

ElementId :: struct {
id: u32,
offset: u32,
baseId: u32,
stringId: String,
}

when ODIN_OS == .Windows {
EnumBackingType :: u32
} else {
Expand All @@ -79,12 +86,19 @@ RectangleElementConfig :: struct {
cornerRadius: CornerRadius,
}

TextWrapMode :: enum EnumBackingType {
Words,
Newlines,
None,
}

TextElementConfig :: struct {
textColor: Color,
fontId: u16,
fontSize: u16,
letterSpacing: u16,
lineSpacing: u16,
wrapMode: TextWrapMode,
}

ImageElementConfig :: struct {
Expand Down Expand Up @@ -234,15 +248,17 @@ ClayArray :: struct($type: typeid) {
foreign Clay {
MinMemorySize :: proc() -> u32 ---
CreateArenaWithCapacityAndMemory :: proc(capacity: u32, offset: [^]u8) -> Arena ---
SetPointerPosition :: proc(position: Vector2) ---
Initialize :: proc(arena: Arena) ---
SetPointerState :: proc(position: Vector2, pointerDown: bool) ---
Initialize :: proc(arena: Arena, layoutDimensions: Dimensions) ---
UpdateScrollContainers :: proc(isPointerActive: bool, scrollDelta: Vector2, deltaTime: c.float) ---
BeginLayout :: proc(screenWidth: c.int, screenHeight: c.int) ---
EndLayout :: proc(screenWidth: c.int, screenHeight: c.int) -> ClayArray(RenderCommand) ---
PointerOver :: proc(id: u32) -> bool ---
GetScrollContainerData :: proc(id: u32) -> ScrollContainerData ---
SetLayoutDimensions :: proc(dimensions: Dimensions) ---
BeginLayout :: proc() ---
EndLayout :: proc() -> ClayArray(RenderCommand) ---
PointerOver :: proc(id: ElementId) -> bool ---
GetScrollContainerData :: proc(id: ElementId) -> ScrollContainerData ---
SetMeasureTextFunction :: proc(measureTextFunction: proc "c" (text: ^String, config: ^TextElementConfig) -> Dimensions) ---
RenderCommandArray_Get :: proc(array: ^ClayArray(RenderCommand), index: i32) -> ^RenderCommand ---
SetDebugModeEnabled :: proc(enabled: bool) ---
}

@(private, link_prefix = "Clay_", default_calling_convention = "c")
Expand All @@ -259,14 +275,14 @@ foreign _ {

@(link_prefix = "Clay_", default_calling_convention = "c", private)
foreign Clay {
_OpenContainerElement :: proc(id: u32, layoutConfig: ^LayoutConfig) ---
_OpenRectangleElement :: proc(id: u32, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) ---
_OpenTextElement :: proc(id: u32, text: String, textConfig: ^TextElementConfig) ---
_OpenImageElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) ---
_OpenScrollElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^ScrollElementConfig) -> rawptr ---
_OpenFloatingElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^FloatingElementConfig) -> rawptr ---
_OpenBorderElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^BorderElementConfig) ---
_OpenCustomElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^CustomElementConfig) ---
_OpenContainerElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig) ---
_OpenRectangleElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) ---
_OpenTextElement :: proc(id: ElementId, text: String, textConfig: ^TextElementConfig) ---
_OpenImageElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) ---
_OpenScrollElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^ScrollElementConfig) ---
_OpenFloatingElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^FloatingElementConfig) ---
_OpenBorderElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^BorderElementConfig) ---
_OpenCustomElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^CustomElementConfig) ---
_CloseElementWithChildren :: proc() ---
_CloseScrollElement :: proc() ---
_CloseFloatingElement :: proc() ---
Expand All @@ -278,52 +294,52 @@ foreign Clay {
_CustomElementConfigArray_Add :: proc(array: ^ClayArray(CustomElementConfig), config: CustomElementConfig) -> ^CustomElementConfig ---
_ScrollElementConfigArray_Add :: proc(array: ^ClayArray(ScrollElementConfig), config: ScrollElementConfig) -> ^ScrollElementConfig ---
_BorderElementConfigArray_Add :: proc(array: ^ClayArray(BorderElementConfig), config: BorderElementConfig) -> ^BorderElementConfig ---
_HashString :: proc(toHash: String, index: u32) -> u32 ---
_HashString :: proc(toHash: String, index: u32) -> ElementId ---
}

@(require_results, deferred_none = _CloseElementWithChildren)
Container :: proc(id: u32, layoutConfig: ^LayoutConfig) -> bool {
Container :: proc(id: ElementId, layoutConfig: ^LayoutConfig) -> bool {
_OpenContainerElement(id, layoutConfig)
return true
}

@(require_results, deferred_none = _CloseElementWithChildren)
Rectangle :: proc(id: u32, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) -> bool {
Rectangle :: proc(id: ElementId, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) -> bool {
_OpenRectangleElement(id, layoutConfig, rectangleConfig)
return true
}

Text :: proc(id: u32, text: string, textConfig: ^TextElementConfig) -> bool {
Text :: proc(id: ElementId, text: string, textConfig: ^TextElementConfig) -> bool {
_OpenTextElement(id, MakeString(text), textConfig)
return true
}

@(require_results, deferred_none = _CloseElementWithChildren)
Image :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) -> bool {
Image :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) -> bool {
_OpenImageElement(id, layoutConfig, imageConfig)
return true
}

@(require_results, deferred_none = _CloseScrollElement)
Scroll :: proc(id: u32, layoutConfig: ^LayoutConfig, scrollConfig: ^ScrollElementConfig) -> bool {
Scroll :: proc(id: ElementId, layoutConfig: ^LayoutConfig, scrollConfig: ^ScrollElementConfig) -> bool {
_OpenScrollElement(id, layoutConfig, scrollConfig)
return true
}

@(require_results, deferred_none = _CloseFloatingElement)
Floating :: proc(id: u32, layoutConfig: ^LayoutConfig, floatingConfig: ^FloatingElementConfig) -> bool {
Floating :: proc(id: ElementId, layoutConfig: ^LayoutConfig, floatingConfig: ^FloatingElementConfig) -> bool {
_OpenFloatingElement(id, layoutConfig, floatingConfig)
return true
}

@(require_results, deferred_none = _CloseElementWithChildren)
Border :: proc(id: u32, layoutConfig: ^LayoutConfig, borderConfig: ^BorderElementConfig) -> bool {
Border :: proc(id: ElementId, layoutConfig: ^LayoutConfig, borderConfig: ^BorderElementConfig) -> bool {
_OpenBorderElement(id, layoutConfig, borderConfig)
return true
}

@(require_results, deferred_none = _CloseElementWithChildren)
Custom :: proc(id: u32, layoutConfig: ^LayoutConfig, customConfig: ^CustomElementConfig) -> bool {
Custom :: proc(id: ElementId, layoutConfig: ^LayoutConfig, customConfig: ^CustomElementConfig) -> bool {
_OpenCustomElement(id, layoutConfig, customConfig)
return true
}
Expand Down Expand Up @@ -412,6 +428,6 @@ MakeString :: proc(label: string) -> String {
return String{chars = raw_data(label), length = cast(c.int)len(label)}
}

ID :: proc(label: string, index: u32 = 0) -> u32 {
ID :: proc(label: string, index: u32 = 0) -> ElementId {
return _HashString(MakeString(label), index)
}
Binary file modified bindings/odin/clay-odin/linux/clay.a
Binary file not shown.
Binary file modified bindings/odin/clay-odin/macos-arm64/clay.a
Binary file not shown.
Binary file modified bindings/odin/clay-odin/macos/clay.a
Binary file not shown.
Binary file modified bindings/odin/clay-odin/wasm/clay.o
Binary file not shown.
Binary file modified bindings/odin/clay-odin/windows/clay.lib
Binary file not shown.
Binary file added bindings/odin/clay-official-website
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ checkImage3: raylib.Texture2D = {}
checkImage4: raylib.Texture2D = {}
checkImage5: raylib.Texture2D = {}

FONT_ID_TITLE_56 :: 0
FONT_ID_BODY_16 :: 0
FONT_ID_TITLE_56 :: 9
FONT_ID_TITLE_52 :: 1
FONT_ID_TITLE_48 :: 2
FONT_ID_TITLE_36 :: 3
Expand All @@ -24,7 +25,6 @@ FONT_ID_BODY_36 :: 5
FONT_ID_BODY_30 :: 6
FONT_ID_BODY_28 :: 7
FONT_ID_BODY_24 :: 8
FONT_ID_BODY_16 :: 9

COLOR_LIGHT :: clay.Color{244, 235, 230, 255}
COLOR_LIGHT_HOVER :: clay.Color{224, 215, 210, 255}
Expand Down Expand Up @@ -112,13 +112,15 @@ LandingPageDesktop :: proc() {
LandingPageMobile :: proc() {
if clay.Container(
clay.ID("LandingPage1Mobile"),
clay.Layout({
layoutDirection = .TOP_TO_BOTTOM,
sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = cast(f32)windowHeight - 70})},
childAlignment = {x = .CENTER, y = .CENTER},
padding = {16, 32},
childGap = 32,
}),
clay.Layout(
{
layoutDirection = .TOP_TO_BOTTOM,
sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = cast(f32)windowHeight - 70})},
childAlignment = {x = .CENTER, y = .CENTER},
padding = {16, 32},
childGap = 32,
},
),
) {
if clay.Container(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingGrow({})}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) {
clay.Text(
Expand Down Expand Up @@ -331,7 +333,7 @@ HighPerformancePageMobile :: proc(lerpValue: f32) {
}
}

RendererButtonActive :: proc(id: u32, index: i32, text: string) {
RendererButtonActive :: proc(id: clay.ElementId, index: i32, text: string) {
if clay.Rectangle(
id,
clay.Layout({sizing = {width = clay.SizingFixed(300)}, padding = {16, 16}}),
Expand All @@ -341,7 +343,7 @@ RendererButtonActive :: proc(id: u32, index: i32, text: string) {
}
}

RendererButtonInactive :: proc(id: u32, index: u32, text: string) {
RendererButtonInactive :: proc(id: clay.ElementId, index: u32, text: string) {
if clay.Border(id, clay.Layout({}), clay.BorderConfigOutsideRadius({2, COLOR_RED}, 10)) {
if clay.Rectangle(
clay.ID("RendererButtonInactiveInner", index),
Expand Down Expand Up @@ -377,11 +379,7 @@ RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.
clay.ID("RendererRightText"),
clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}, layoutDirection = .TOP_TO_BOTTOM, childGap = 16}),
) {
clay.Text(
clay.ID("RendererTextRightTitle"),
"Try changing renderer!",
clay.TextConfig({fontSize = 36, fontId = FONT_ID_BODY_36, textColor = COLOR_ORANGE}),
)
clay.Text(clay.ID("RendererTextRightTitle"), "Try changing renderer!", clay.TextConfig({fontSize = 36, fontId = FONT_ID_BODY_36, textColor = COLOR_ORANGE}))
if clay.Container(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 32})}})) {}
RendererButtonActive(clay.ID("RendererSelectButtonActive", 0), 0, "Raylib Renderer")
}
Expand All @@ -405,13 +403,15 @@ RendererPageDesktop :: proc() {
RendererPageMobile :: proc() {
if clay.Rectangle(
clay.ID("RendererMobile"),
clay.Layout({
layoutDirection = .TOP_TO_BOTTOM,
sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})},
childAlignment = {x = .CENTER, y = .CENTER},
padding = {x = 16, y = 32},
childGap = 32,
}),
clay.Layout(
{
layoutDirection = .TOP_TO_BOTTOM,
sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})},
childAlignment = {x = .CENTER, y = .CENTER},
padding = {x = 16, y = 32},
childGap = 32,
},
),
clay.RectangleConfig({color = COLOR_LIGHT}),
) {
RendererPage({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}, clay.SizingGrow({}))
Expand All @@ -429,7 +429,7 @@ animationLerpValue: f32 = -1.0

createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
mobileScreen := windowWidth < 750
clay.BeginLayout(windowWidth, windowHeight)
clay.BeginLayout()
if clay.Rectangle(
clay.ID("OuterContainer"),
clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {clay.SizingGrow({}), clay.SizingGrow({})}}),
Expand All @@ -450,7 +450,7 @@ createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
clay.Text(clay.ID("LinkDocsText"), "Docs", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}}))
}
}
githubButtonId: u32 = clay.ID("HeaderButtonGithub")
githubButtonId: clay.ElementId = clay.ID("HeaderButtonGithub")
if clay.Border(clay.ID("LinkGithubOuter"), clay.Layout({}), clay.BorderConfigOutsideRadius({2, COLOR_RED}, 10)) {
if clay.Rectangle(
githubButtonId,
Expand Down Expand Up @@ -494,7 +494,7 @@ createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
}
}
}
return clay.EndLayout(windowWidth, windowHeight)
return clay.EndLayout()
}

loadFont :: proc(fontId: u16, fontSize: u16, path: cstring) {
Expand All @@ -510,7 +510,7 @@ main :: proc() {
memory := make([^]u8, minMemorySize)
arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory)
clay.SetMeasureTextFunction(measureText)
clay.Initialize(arena)
clay.Initialize(arena, {cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()})

raylib.SetConfigFlags({.VSYNC_HINT, .WINDOW_RESIZABLE, .WINDOW_HIGHDPI, .MSAA_4X_HINT})
raylib.InitWindow(windowWidth, windowHeight, "Raylib Odin Example")
Expand All @@ -533,17 +533,24 @@ main :: proc() {
checkImage4 = raylib.LoadTextureFromImage(raylib.LoadImage("resources/check_4.png"))
checkImage5 = raylib.LoadTextureFromImage(raylib.LoadImage("resources/check_5.png"))

debugModeEnabled: bool = false

for !raylib.WindowShouldClose() {
defer free_all(context.temp_allocator)

animationLerpValue += raylib.GetFrameTime()
if animationLerpValue > 1 {
animationLerpValue = animationLerpValue - 2
}
windowWidth = raylib.GetScreenWidth()
windowHeight = raylib.GetScreenHeight()
clay.SetPointerPosition(transmute(clay.Vector2)raylib.GetMousePosition())
if (raylib.IsKeyPressed(.D)) {
debugModeEnabled = !debugModeEnabled
clay.SetDebugModeEnabled(debugModeEnabled)
}
clay.SetPointerState(transmute(clay.Vector2)raylib.GetMousePosition(), raylib.IsMouseButtonDown(raylib.MouseButton.LEFT))
clay.UpdateScrollContainers(false, transmute(clay.Vector2)raylib.GetMouseWheelMoveV(), raylib.GetFrameTime())
clay.SetLayoutDimensions({cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()})
renderCommands: clay.ClayArray(clay.RenderCommand) = createLayout(animationLerpValue < 0 ? (animationLerpValue + 1) : (1 - animationLerpValue))
raylib.BeginDrawing()
clayRaylibRender(&renderCommands)
Expand Down
Loading

0 comments on commit a4f90a2

Please sign in to comment.