clay/bindings/odin
2024-08-30 21:58:08 +12:00
..
clay-odin Odin Bindings - First Pass (#5) 2024-08-30 21:58:08 +12:00
examples/clay-official-website Odin Bindings - First Pass (#5) 2024-08-30 21:58:08 +12:00
.gitignore Odin Bindings - First Pass (#5) 2024-08-30 21:58:08 +12:00
build-clay-lib.sh Odin Bindings - First Pass (#5) 2024-08-30 21:58:08 +12:00
odinfmt.json Odin Bindings - First Pass (#5) 2024-08-30 21:58:08 +12:00
ols.json Odin Bindings - First Pass (#5) 2024-08-30 21:58:08 +12:00
README.md Odin Bindings - First Pass (#5) 2024-08-30 21:58:08 +12:00

Odin Language Bindings

This directory contains bindings for the Odin programming language, as well as an example implementation of the clay website in Odin.

If you haven't taken a look at the full documentation for clay, 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.

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:

Special thanks to

// C form of element macros
CLAY_RECTANGLE(CLAY_ID("SideBar"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW() }, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = COLOR_LIGHT), {
	// Child elements here
});
// Odin form of element macros
if clay.Rectangle(clay.ID("SideBar"), clay.Layout({ layoutDirection = .TOP_TO_BOTTOM, sizing = { width = clay.SizingFixed(300), height = clay.SizingGrow({}) }, padding = {16, 16} }), clay.RectangleConfig({ color = COLOR_LIGHT })) {
	// Child elements here
}

Quick Start

  1. Download the [clay-odin](link todo) directory and copy it into your project.
import clay "clay-odin"
  1. Ask clay for how much static memory it needs using clay.MinMemorySize(), create an Arena for it to use with clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory), and initialize it with clay.Initialize(arena).
minMemorySize: c.uint32_t = clay.MinMemorySize()
memory := make([^]u8, minMemorySize)
arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory)
  1. Provide a measureText(text, config) proc "c" with clay.SetMeasureTextFunction(function) so that clay can measure and wrap text.
// Example measure text function
measureText :: proc "c" (text: ^clay.String, config: ^clay.TextElementConfig) -> clay.Dimensions {
    // clay.TextElementConfig contains members such as fontId, fontSize, letterSpacing etc
    // Note: clay.String->chars is not guaranteed to be null terminated
}

// Tell clay how to measure text
clay.SetMeasureTextFunction(measureText)
  1. Optional - Call clay.SetPointerPosition(pointerPosition) if you want to use mouse interactions.
// Update internal pointer position for handling mouseover / click / touch events
clay.SetPointerPosition(clay.Vector2{ mousePositionX, mousePositionY })
  1. Call clay.BeginLayout(screenWidth, screenHeight) and declare your layout using the provided macros.
COLOR_LIGHT :: clay.Color{224, 215, 210, 255}
COLOR_RED :: clay.Color{168, 66, 28, 255}
COLOR_ORANGE :: clay.Color{225, 138, 50, 255}

// Layout config is just a struct that can be declared statically, or inline
sidebarItemLayout := clay.LayoutConfig {
    sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(50)},
}

// Re-useable components are just normal functions
SidebarItemComponent :: proc(index: u32) {
    if clay.Rectangle(clay.IDI("SidebarBlob", index), &sidebarItemLayout, clay.RectangleConfig({color = COLOR_ORANGE})) {}
}

// An example function to begin the "root" of your layout tree
CreateLayout :: proc() -> clay.ClayArray(clay.RenderCommand) {
    clay.BeginLayout(windowWidth, windowHeight)

    // 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.Rectangle(
        clay.ID("OuterContainer"),
        clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, padding = {16, 16}, childGap = 16}),
        clay.RectangleConfig({color = {250, 250, 255, 255}}),
    ) {
        if clay.Rectangle(
            clay.ID("SideBar"),
            clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingFixed(300), height = clay.SizingGrow({})}, padding = {16, 16}, childGap = 16}),
            clay.RectangleConfig({color = COLOR_LIGHT}),
        ) {
            if clay.Rectangle(
                clay.ID("ProfilePictureOuter"),
                clay.Layout({sizing = {width = clay.SizingGrow({})}, padding = {16, 16}, childGap = 16, childAlignment = {y = .CENTER}}),
                clay.RectangleConfig({color = COLOR_RED}),
            ) {
                if clay.Image(
                    clay.ID("ProfilePicture"),
                    clay.Layout({sizing = {width = clay.SizingFixed(60), height = clay.SizingFixed(60)}}),
                    clay.ImageConfig({imageData = &profilePicture, sourceDimensions = {height = 60, width = 60}}),
                ) {}
                clay.Text(clay.ID("ProfileTitle"), clay.MakeString("Clay - UI Library"), clay.TextConfig({fontSize = 24, textColor = {255, 255, 255, 255}}))
            }

            // Standard Odin code like loops etc work inside components
            for i: u32 = 0; i < 10; i += 1 {
                SidebarItemComponent(i)
            }
        }

        if clay.Rectangle(
            clay.ID("MainContent"),
            clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}}),
            clay.RectangleConfig({color = COLOR_LIGHT}),
        ) {}
    }
    // ...
}
  1. Call clay.EndLayout(screenWidth, screenHeight) and process the resulting clay.RenderCommandArray in your choice of renderer.
renderCommands: clay.ClayArray(clay.RenderCommand) = clay.EndLayout(windowWidth, windowHeight)

for i: u32 = 0; i < renderCommands.length; i += 1 {
	renderCommand := clay.RenderCommandArray_Get(&renderCommands, cast(i32)i)

	switch renderCommand.commandType {
	case .Rectangle:
		{
			DrawRectangle(renderCommand.boundingBox, renderCommand.config.rectangleElementConfig.color)
		}
	// ... Implement handling of other command types
	}
}

Please see the full C documentation for clay for API details. All public C functions and Macros have Odin binding equivalents, generally of the form CLAY_RECTANGLE (C) -> clay.Rectangle (Odin)