A library that contains Zig bindings for RmlUi, C++ user interface library based on the HTML and CSS standards.
Option A. Install with package manager
zig fetch --save https://github.com/silbinarywolf/rmlui/archive/REPLACE_WITH_WANTED_COMMIT.tar.gz"Option B. Copy-paste the dependency into your project directly and put in a third-party folder. This is recommended if you want to easily hack on it or tweak it.
.{
.name = .yourzigproject,
.dependencies = .{
.rmlui = .{
.path = "third-party/rmlui",
},
},
}- SDL3: An example of how to get setup with SDL3 and how bindings work
I've handwritten a C-API wrapper that call out to the C++ code, similar to how cimgui works with ImGui.
However because the binding logic for RmlUi relies on C++ templating (generics), I've had to spend some time understanding how those types are registered with RmlUi and then write specific Zig code that works similarly, such as generating a type id dynamically once when a value is bound here.
An example of what the setup and application loop looks like when consuming this library, based on the provided SDL3 example.
// This is an overly simplified example to give you the gist
// of how this library works
const rml = @import("rml");
const rmldebug = @import("rmldebug");
const rmlsdl = @import("rmlsdl");
pub fn main() !void {
// Setup platform-specific interfaces (ie. SDL3)
rml.setSystemInterface(...);
rml.setRenderInterface(...);
rml.setFileInterface(...);
try rml.initialise();
defer rml.shutdown();
const context = try rml.createContext("main", 1280, 720, .default);
defer context.destroy();
try rmldebug.initialise(context);
const lato_font_family_name = "Lato";
try rml.loadFontFaceFromMemory(@embedFile("data/Lato-Regular.ttf"), lato_font_family_name, .{});
// Example of data binding setup, based on the tutorial here:
// https://mikke89.github.io/RmlUiDoc/pages/data_bindings/examples.html
const MyData = struct {
// FixedString is a Zig-API provided type with a fixed capacity, for two-way binding
title: rml.FixedString(256) = .initComptime("Hello World!"),
animal: rml.FixedString(128) = .initComptime("dog"),
// []const u8 strings are readonly and cannot be updated in an <input> field, but you can bind and update them via "dirtyVariable"
readonly_text: []const u8 = "the cat is meowing",
show_text: bool = true,
};
var my_data: MyData = .{};
const my_model = modelblk: {
var dmc = try context.createDataModel("my_model", .default);
try dmc.bind("title", &my_data.title);
try dmc.bind("animal", &my_data.animal);
try dmc.bind("readonly_text", &my_data.readonly_text);
try dmc.bind("show_text", &my_data.show_text);
break :modelblk dmc.getModelHandle();
};
// Load Document
const document = try context.loadDocumentFromMemory(@embedFile("data/main.rml"), "data/main.rml");
document.show(.default);
while (!has_quit) {
// Event Loop
while (SDL_PollEvent(...)) {
if (!rmlsdl.inputEventHandler(context, window, &sdl_event)) {
// If false, then RmlUi consumed/processed that event, don't process it ourselves
continue;
}
}
// Update
if (my_model.isVariableDirty("animal")) {
my_data.title = try .format("Hello {s}!", .{my_data.animal.string()});
my_model.dirtyVariable("title");
my_data.other_text = "the cats meowing has ceased...";
my_model.dirtyVariable("other_text");
}
_ = context.update(); // Run RmlUi input handling logic, etc
// Draw
sdl.SDL_SetRenderDrawColor(renderer, 20, 20, 20, 0);
sdl.SDL_RenderClear(renderer);
render_interface.beginFrame();
_ = context.render(); // Render RmlUi
render_interface.endFrame();
sdl.SDL_RenderPresent(renderer)
}
}- mikke89 for maintaining this awesome library.