johan0A/clay-zig-bindings
Zig bindings for the library clay: A high performance UI layout library in C.
IMPORTANT
Zig 0.14.0 or higher is required. (tested with zig 0.14.0-dev.3062+ff551374a)
NOTE
This project is currently in beta.
This repository contains Zig bindings for the clay UI layout library, as well as an example implementation of the clay website in Zig.
This README is abbreviated and applies to using clay in Zig specifically: 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.
Some differences between the C API and the Zig bindings include:
.fixed()
or .layout()
.grow
In C:
CLAY({ // C macro for creating a scope
.id = CLAY_ID("SideBar"),
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) },
.padding = CLAY_PADDING_ALL(16),
.childAlignment = .{ .x = CLAY_ALIGN_X_CENTER , .y = .CLAY_ALIGN_Y_TOP },
.childGap = 16
},
.backgroundColor = COLOR_LIGHT
}){
// Child elements here
}
In Zig:
clay.UI()(.{ // function call for creating a scope
.id = .ID("SideBar"),
.layout = .{
.direction = .top_to_bottom,
.sizing = .{ .w = .fixed(300), .h = .grow },
.padding = .all(16),
.child_alignment = .{ .x = .center, .y = .top },
.child_gap = 16,
},
.background_color = light_grey,
})({
// Child elements here
});
zclay
to the dependency list in build.zig.zon
:zig fetch --save https://github.com/johan0A/clay-zig-bindings/archive/<commit sha>.tar.gz
build.zig
:...
const zclay_dep = b.dependency("zclay", .{
.target = target,
.optimize = optimize,
});
compile_step.root_module.addImport("zclay", zclay_dep.module("zclay"));
...
const min_memory_size: u32 = clay.minMemorySize();
const memory = try allocator.alloc(u8, min_memory_size);
defer allocator.free(memory);
const arena: clay.Arena = clay.createArenaWithCapacityAndMemory(memory);
_ = clay.initialize(arena, .{ .h = 1000, .w = 1000 }, .{});
clay.setMeasureTextFunction(renderer.measureText);
measureText(text, config)
function with clay.setMeasureTextFunction(function) so that clay can measure and wrap text.// Example measure text function
pub fn measureText(clay_text: []const u8, config: *clay.TextElementConfig, user_data: void) 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)
// Update internal pointer position for handling mouseover / click / touch events
clay.setPointerState(.{
.x = mouse_position_x,
.y = mouse_position_y,
}, is_left_mouse_button_down);
const light_grey: clay.Color = .{ 224, 215, 210, 255 };
const red: clay.Color = .{ 168, 66, 28, 255 };
const orange: clay.Color = .{ 225, 138, 50, 255 };
const white: clay.Color = .{ 250, 250, 255, 255 };
const sidebar_item_layout: clay.LayoutConfig = .{ .sizing = .{ .w = .grow, .h = .fixed(50) } };
// Re-useable components are just normal functions
fn sidebarItemComponent(index: u32) void {
clay.UI()(.{
.id = .IDI("SidebarBlob", index),
.layout = sidebar_item_layout,
.background_color = orange,
})({});
}
// An example function to begin the "root" of your layout tree
fn createLayout(profile_picture: *const rl.Texture2D) clay.ClayArray(clay.RenderCommand) {
clay.beginLayout();
clay.UI()(.{
.id = .ID("OuterContainer"),
.layout = .{ .direction = .left_to_right, .sizing = .grow, .padding = .all(16), .child_gap = 16 },
.background_color = white,
})({
clay.UI()(.{
.id = .ID("SideBar"),
.layout = .{
.direction = .top_to_bottom,
.sizing = .{ .h = .grow, .w = .fixed(300) },
.padding = .all(16),
.child_alignment = .{ .x = .center, .y = .top },
.child_gap = 16,
},
.background_color = light_grey,
})({
clay.UI()(.{
.id = .ID("ProfilePictureOuter"),
.layout = .{ .sizing = .{ .w = .grow }, .padding = .all(16), .child_alignment = .{ .x = .left, .y = .center }, .child_gap = 16 },
.background_color = red,
})({
clay.UI()(.{
.id = .ID("ProfilePicture"),
.layout = .{ .sizing = .{ .h = .fixed(60), .w = .fixed(60) } },
.image = .{ .source_dimensions = .{ .h = 60, .w = 60 }, .image_data = @ptrCast(profile_picture) },
})({});
clay.text("Clay - UI Library", .{ .font_size = 24, .color = light_grey });
});
for (0..5) |i| sidebarItemComponent(@intCast(i));
});
clay.UI()(.{
.id = .ID("MainContent"),
.layout = .{ .sizing = .grow },
.background_color = light_grey,
})({
//...
});
});
return clay.endLayout();
}
pub fn clayRaylibRender(render_commands: *clay.ClayArray(clay.RenderCommand), allocator: std.mem.Allocator) void {
var i: usize = 0;
while (i < render_commands.length) : (i += 1) {
const render_command = clay.renderCommandArrayGet(render_commands, @intCast(i));
const bounding_box = render_command.bounding_box;
switch (render_command.command_type) {
.none => {},
.text => {
...
Please see the full C documentation for clay for API details and the example folder in this repo. All public C functions and Macros have Zig binding equivalents, generally of the form Clay_BeginLayout
(C) -> clay.beginLayout
(zig)