2024-08-30 09:58:08 +00:00
### Odin Language Bindings
2025-02-20 22:10:00 +00:00
This directory contains bindings for the [Odin ](odin-lang.org ) programming language, as well as an example implementation of the [Clay website ](https://nicbarker.com/clay ) in Odin.
2024-08-30 09:58:08 +00:00
Special thanks to
- [laytan ](https://github.com/laytan )
- [Dudejoe870 ](https://github.com/Dudejoe870 )
- MrStevns from the Odin Discord server
2025-02-20 22:10:00 +00:00
If you haven't taken a look at the [full documentation for Clay ](https://github.com/nicbarker/clay/blob/main/README.md ), it's recommended that you take a look there first to familiarise yourself with the general concepts. This README is abbreviated and applies to using Clay in Odin specifically.
2024-08-30 10:03:26 +00:00
2025-02-20 22:10:00 +00:00
The **most notable difference** between the C API and the Odin bindings is the use of `if` statements to create the scope for declaring child elements, when using the equivalent of the [Element Macros ](https://github.com/nicbarker/clay/blob/main/README.md#element-macros ):
2024-08-30 09:58:08 +00:00
```C
// C form of element macros
2025-02-20 22:10:00 +00:00
// Define an element with 16px of x and y padding
CLAY({ .id = CLAY_ID("Outer"), .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
// Child elements here
}
2024-08-30 09:58:08 +00:00
```
```Odin
// Odin form of element macros
2025-02-20 22:10:00 +00:00
if clay.UI({ id = clay.ID("Outer"), layout = { padding = clay.PaddingAll(16) }}) {
2025-02-21 16:59:31 +00:00
// Child elements here
2024-08-30 09:58:08 +00:00
}
```
### Quick Start
2024-08-30 10:04:20 +00:00
1. Download the [clay-odin ](https://github.com/nicbarker/clay/tree/main/bindings/odin/clay-odin ) directory and copy it into your project.
2024-08-30 09:58:08 +00:00
```Odin
import clay "clay-odin"
```
2025-02-20 22:10:00 +00:00
2. Ask Clay for how much static memory it needs using [clay.MinMemorySize() ](https://github.com/nicbarker/clay/blob/main/README.md#clay_minmemorysize ), create an Arena for it to use with [clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory) ](https://github.com/nicbarker/clay/blob/main/README.md#clay_createarenawithcapacityandmemory ), and initialize it with [clay.Initialize(arena) ](https://github.com/nicbarker/clay/blob/main/README.md#clay_initialize ).
2024-08-30 09:58:08 +00:00
```Odin
2025-02-20 22:10:00 +00:00
min_memory_size: u32 = clay.MinMemorySize()
memory := make([^]u8, min_memory_size)
arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(min_memory_size, memory)
2024-08-30 10:05:09 +00:00
clay.Initialize(arena)
2024-08-30 09:58:08 +00:00
```
2025-02-20 22:10:00 +00:00
3. Provide a `measure_text(text, config)` proc "c" with [clay.SetMeasureTextFunction(function) ](https://github.com/nicbarker/clay/blob/main/README.md#clay_setmeasuretextfunction ) so that Clay can measure and wrap text.
2024-08-30 09:58:08 +00:00
```Odin
// Example measure text function
2025-02-20 22:10:00 +00:00
measure_text :: proc "c" (
2025-02-21 16:59:31 +00:00
text: clay.StringSlice,
config: ^clay.TextElementConfig,
userData: rawptr,
2025-02-20 22:10:00 +00:00
) -> clay.Dimensions {
// clay.TextElementConfig contains members such as fontId, fontSize, letterSpacing, etc..
2024-08-30 09:58:08 +00:00
// Note: clay.String->chars is not guaranteed to be null terminated
2025-02-20 22:10:00 +00:00
return {
2025-02-21 16:59:31 +00:00
width = f32(text.length * i32(config.fontSize)),
height = f32(config.fontSize),
2025-02-20 22:10:00 +00:00
}
2024-08-30 09:58:08 +00:00
}
2025-02-20 22:10:00 +00:00
2024-08-30 09:58:08 +00:00
// Tell clay how to measure text
2025-02-20 22:10:00 +00:00
clay.SetMeasureTextFunction(measure_text, nil)
2025-02-21 16:59:31 +00:00
```
2024-08-30 09:58:08 +00:00
2025-02-20 22:10:00 +00:00
4. **Optional** - Call [clay.SetPointerState(pointerPosition, isPointerDown) ](https://github.com/nicbarker/clay/blob/main/README.md#clay_setpointerstate ) if you want to use mouse interactions.
2024-08-30 09:58:08 +00:00
```Odin
// Update internal pointer position for handling mouseover / click / touch events
2025-02-20 22:10:00 +00:00
clay.SetPointerState(
clay.Vector2 { mouse_pos_x, mouse_pos_y },
is_mouse_down,
)
2024-08-30 09:58:08 +00:00
```
2025-02-20 22:10:00 +00:00
5. Call [clay.BeginLayout() ](https://github.com/nicbarker/clay/blob/main/README.md#clay_beginlayout ) and declare your layout using the provided macros.
2024-08-30 09:58:08 +00:00
```Odin
2025-02-20 22:10:00 +00:00
// Define some colors.
2024-08-30 09:58:08 +00:00
COLOR_LIGHT :: clay.Color{224, 215, 210, 255}
COLOR_RED :: clay.Color{168, 66, 28, 255}
COLOR_ORANGE :: clay.Color{225, 138, 50, 255}
2025-02-20 22:10:00 +00:00
COLOR_BLACK :: clay.Color{0, 0, 0, 255}
2024-08-30 09:58:08 +00:00
// Layout config is just a struct that can be declared statically, or inline
2025-02-20 22:10:00 +00:00
sidebar_item_layout := clay.LayoutConfig {
sizing = {
2025-02-21 16:59:31 +00:00
width = clay.SizingGrow({}),
height = clay.SizingFixed(50)
2025-02-20 22:10:00 +00:00
},
2024-08-30 09:58:08 +00:00
}
2025-02-20 22:10:00 +00:00
// Re-useable components are just normal procs.
sidebar_item_component :: proc(index: u32) {
if clay.UI()({
2025-02-21 16:59:31 +00:00
id = clay.ID("SidebarBlob", index),
layout = sidebar_item_layout,
backgroundColor = COLOR_ORANGE,
2025-02-20 22:10:00 +00:00
}) {}
2024-08-30 09:58:08 +00:00
}
2025-02-20 22:10:00 +00:00
// An example function to create your layout tree
create_layout :: proc() -> clay.ClayArray(clay.RenderCommand) {
// Begin constructing the layout.
clay.BeginLayout()
// An example of laying out a UI with a fixed-width sidebar and flexible-width main content
// NOTE: To create a scope for child components, the Odin API uses `if` with components that have children
if clay.UI()({
id = clay.ID("OuterContainer"),
layout = {
2025-02-21 16:59:31 +00:00
sizing = { width = clay.SizingGrow({}), height = clay.SizingGrow({}) },
padding = { 16, 16, 16, 16 },
childGap = 16,
2025-02-20 22:10:00 +00:00
},
backgroundColor = { 250, 250, 255, 255 },
}) {
if clay.UI()({
id = clay.ID("SideBar"),
layout = {
2025-02-21 16:59:31 +00:00
layoutDirection = .TopToBottom,
sizing = { width = clay.SizingFixed(300), height = clay.SizingGrow({}) },
padding = { 16, 16, 16, 16 },
childGap = 16,
2025-02-20 22:10:00 +00:00
},
backgroundColor = COLOR_LIGHT,
}) {
if clay.UI()({
id = clay.ID("ProfilePictureOuter"),
2025-02-21 16:59:31 +00:00
layout = {
sizing = { width = clay.SizingGrow({}) },
padding = { 16, 16, 16, 16 },
childGap = 16,
childAlignment = { y = .Center },
},
2025-02-20 22:10:00 +00:00
backgroundColor = COLOR_RED,
2025-02-21 16:59:31 +00:00
cornerRadius = { 6, 6, 6, 6 },
2025-02-20 22:10:00 +00:00
}) {
if clay.UI()({
id = clay.ID("ProfilePicture"),
layout = {
2025-02-21 16:59:31 +00:00
sizing = { width = clay.SizingFixed(60), height = clay.SizingFixed(60) },
2025-02-20 22:10:00 +00:00
},
image = {
2025-02-21 16:59:31 +00:00
imageData = & profile_picture,
sourceDimensions = {
width = 60,
height = 60,
},
2025-02-20 22:10:00 +00:00
},
}) {}
2025-02-21 16:59:31 +00:00
clay.Text(
"Clay - UI Library",
clay.TextConfig({ textColor = COLOR_BLACK, fontSize = 16 }),
)
2024-08-30 09:58:08 +00:00
}
2025-02-20 22:10:00 +00:00
// Standard Odin code like loops, etc. work inside components.
// Here we render 5 sidebar items.
for i in u32(0)..< 5 {
sidebar_item_component(i)
2024-08-30 09:58:08 +00:00
}
}
2025-02-20 22:10:00 +00:00
if clay.UI()({
id = clay.ID("MainContent"),
layout = {
2025-02-21 16:59:31 +00:00
sizing = { width = clay.SizingGrow({}), height = clay.SizingGrow({}) },
2025-02-20 22:10:00 +00:00
},
backgroundColor = COLOR_LIGHT,
}) {}
2024-08-30 09:58:08 +00:00
}
2025-02-20 22:10:00 +00:00
// Returns a list of render commands
2025-02-21 16:59:31 +00:00
render_commands: clay.ClayArray(clay.RenderCommand) = clay.EndLayout()
2025-02-20 22:10:00 +00:00
return render_commands
2024-08-30 09:58:08 +00:00
}
```
2025-02-20 22:10:00 +00:00
6. Call your layout proc and process the resulting [clay.ClayArray(clay.RenderCommand) ](https://github.com/nicbarker/clay/blob/main/README.md#clay_rendercommandarray ) in your choice of renderer.
2024-08-30 09:58:08 +00:00
```Odin
2025-02-20 22:10:00 +00:00
render_commands = create_layout()
2024-08-30 09:58:08 +00:00
2025-02-20 22:10:00 +00:00
for i in 0 ..< int ( render_commands . length ) {
render_command := clay.RenderCommandArray_Get(render_commands, cast(i32)i)
2024-08-30 10:06:33 +00:00
2025-02-20 22:10:00 +00:00
switch render_command.commandType {
2024-08-30 10:06:33 +00:00
case .Rectangle:
2025-02-20 22:10:00 +00:00
DrawRectangle(render_command.boundingBox, render_command.config.rectangleElementConfig.color)
2024-08-30 10:06:33 +00:00
// ... Implement handling of other command types
}
2024-08-30 09:58:08 +00:00
}
```
2025-02-20 22:10:00 +00:00
Please see the [full C documentation for Clay ](https://github.com/nicbarker/clay/blob/main/README.md ) for API details. All public C functions and Macros have Odin binding equivalents, generally of the form `CLAY_ID` (C) -> `clay.ID` (Odin).