Zig build package and bindings for Dear Imgui & extras.
Easy to use, hand-crafted API with default arguments, named parameters and Zig style text formatting. Here is a simple sample application, and here is a full one.
- Most public dear imgui API exposed
- All memory allocations go through user provided Zig allocator
- DrawList API for vector graphics, text rendering and custom widgets
- Test engine API for automatic testing
- Plot API for advanced data visualizations
- Gizmo API for gizmo
- Node editor API for node based stuff
- ImGui
1.91.0-docking
- ImGui test engine
1.91.0
- ImPlot
O.17
- ImGuizmo
1.89 WIP
- ImGuiNodeEditor
O.9.3
Example build.zig
:
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{ ... });
const zgui = b.dependency("zgui", .{
.shared = false,
.with_implot = true,
});
exe.root_module.addImport("zgui", zgui.module("root"));
exe.linkLibrary(zgui.artifact("imgui"));
{ // Needed for glfw/wgpu rendering backend
const zglfw = b.dependency("zglfw", .{});
exe.root_module.addImport("zglfw", zglfw.module("root"));
exe.linkLibrary(zglfw.artifact("glfw"));
const zpool = b.dependency("zpool", .{});
exe.root_module.addImport("zpool", zpool.module("root"));
const zgpu = b.dependency("zgpu", .{});
exe.root_module.addImport("zgpu", zgpu.module("root"));
exe.linkLibrary(zgpu.artifact("zdawn"));
}
}
Now in your code you may import and use zgui
:
const zgui = @import("zgui");
zgui.init(allocator);
_ = zgui.io.addFontFromFile(content_dir ++ "Roboto-Medium.ttf", 16.0);
zgui.backend.init(
window,
demo.gctx.device,
@enumToInt(swapchain_format),
@enumToInt(depth_format),
);
// Main loop
while (...) {
zgui.backend.newFrame(framebuffer_width, framebuffer_height);
zgui.bulletText(
"Average : {d:.3} ms/frame ({d:.1} fps)",
.{ demo.gctx.stats.average_cpu_time, demo.gctx.stats.fps },
);
zgui.bulletText("W, A, S, D : move camera", .{});
zgui.spacing();
if (zgui.button("Setup Scene", .{})) {
// Button pressed.
}
if (zgui.dragFloat("Drag 1", .{ .v = &value0 })) {
// value0 has changed
}
if (zgui.dragFloat("Drag 2", .{ .v = &value0, .min = -1.0, .max = 1.0 })) {
// value1 has changed
}
// Setup wgpu render pass here
zgui.backend.draw(pass);
}
If your project spans multiple zig modules that both use ImGui, such as an exe paired with a dll, you may want to build the zgui
dependencies (zgui_pkg.zgui_c_cpp
) as a shared library. This can be enabled with the shared
build option. Then, in build.zig
, use zgui_pkg.link
to link zgui
to all the modules that use ImGui.
When built this way, the ImGui context will be located in the shared library. However, the zgui
zig code (which is compiled separately into each module) requires its own memory buffer which has to be initialized separately with initNoContext
.
In your executable:
const zgui = @import("zgui");
zgui.init(allocator);
defer zgui.deinit();
In your shared library:
const zgui = @import("zgui");
zgui.initNoContext(allocator);
defer zgui.deinitNoContxt();
draw_list.addQuad(.{
.p1 = .{ 170, 420 },
.p2 = .{ 270, 420 },
.p3 = .{ 220, 520 },
.p4 = .{ 120, 520 },
.col = 0xff_00_00_ff,
.thickness = 3.0,
});
draw_list.addText(.{ 130, 130 }, 0xff_00_00_ff, "The number is: {}", .{7});
draw_list.addCircleFilled(.{ .p = .{ 200, 600 }, .r = 50, .col = 0xff_ff_ff_ff });
draw_list.addCircle(.{ .p = .{ 200, 600 }, .r = 30, .col = 0xff_00_00_ff, .thickness = 11 });
draw_list.addPolyline(
&.{ .{ 100, 700 }, .{ 200, 600 }, .{ 300, 700 }, .{ 400, 600 } },
.{ .col = 0xff_00_aa_11, .thickness = 7 },
);
Zig wraper for ImGUI test engine.
var check_b = false;
var _te: *zgui.te.TestEngine = zgui.te.getTestEngine().?;
fn registerTests() void {
_ = _te.registerTest(
"Awesome",
"should_do_some_another_magic",
@src(),
struct {
pub fn gui(ctx: *zgui.te.TestContext) !void {
_ = ctx; // autofix
_ = zgui.begin("Test Window", .{ .flags = .{ .no_saved_settings = true } });
defer zgui.end();
zgui.text("Hello, automation world", .{});
_ = zgui.button("Click Me", .{});
if (zgui.treeNode("Node")) {
defer zgui.treePop();
_ = zgui.checkbox("Checkbox", .{ .v = &check_b });
}
}
pub fn run(ctx: *zgui.te.TestContext) !void {
ctx.setRef("/Test Window");
ctx.windowFocus("");
ctx.itemAction(.click, "Click Me", .{}, null);
ctx.itemAction(.open, "Node", .{}, null);
ctx.itemAction(.check, "Node/Checkbox", .{}, null);
ctx.itemAction(.uncheck, "Node/Checkbox", .{}, null);
std.testing.expect(true) catch |err| {
zgui.te.checkTestError(@src(), err);
return;
};
}
},
);
}
if (zgui.plot.beginPlot("Line Plot", .{ .h = -1.0 })) {
zgui.plot.setupAxis(.x1, .{ .label = "xaxis" });
zgui.plot.setupAxisLimits(.x1, .{ .min = 0, .max = 5 });
zgui.plot.setupLegend(.{ .south = true, .west = true }, .{});
zgui.plot.setupFinish();
zgui.plot.plotLineValues("y data", i32, .{ .v = &.{ 0, 1, 0, 1, 0, 1 } });
zgui.plot.plotLine("xy data", f32, .{
.xv = &.{ 0.1, 0.2, 0.5, 2.5 },
.yv = &.{ 0.1, 0.3, 0.5, 0.9 },
});
zgui.plot.endPlot();
}
Zig wraper for ImGuizmo.
Zig wraper for ImGuiNodeEditor.
var node_editor = zgui.node_editor.EditorContext.create(.{ .enable_smooth_zoom = true }),
zgui.node_editor.setCurrentEditor(node_editor);
defer zgui.node_editor.setCurrentEditor(null);
{
zgui.node_editor.begin("NodeEditor", .{ 0, 0 });
defer zgui.node_editor.end();
zgui.node_editor.beginNode(1);
{
defer zgui.node_editor.endNode();
zgui.textUnformatted("Node A");
zgui.node_editor.beginPin(1, .input);
{
defer zgui.node_editor.endPin();
zgui.textUnformatted("-> In");
}
zgui.sameLine(.{});
zgui.node_editor.beginPin(2, .output);
{
defer zgui.node_editor.endPin();
zgui.textUnformatted("Out ->");
}
}
}