Skip to content

feat(examples): sokol + imgui smoke test example - #39

Merged
apotema merged 1 commit into
mainfrom
examples/sokol-imgui-smoke
Apr 14, 2026
Merged

feat(examples): sokol + imgui smoke test example#39
apotema merged 1 commit into
mainfrom
examples/sokol-imgui-smoke

Conversation

@apotema

@apotema apotema commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds `examples/sokol_imgui/` — a minimal end-to-end test that exercises the full sokol + Dear ImGui + labelle-imgui bridge stack. If this example builds and runs with a clickable button, the entire chain is wired correctly:

  • sokol backend with `with_sokol_imgui = true` (provided by the labelle-imgui sokol bridge that owns its own sokol-with-imgui dep)
  • cimgui linked once into the final exe
  • `sokol_imgui_bridge` artifact resolving `simgui_*` symbols
  • generated `sokolEvent` forwarding sapp events to `imgui_bridge_handle_event` so simgui's IO state actually receives mouse / keyboard input

Diff against `examples/raylib`

The example is a near-copy of `examples/raylib` (same scenes, prefabs, components, gizmos, scene switcher, player movement) so the side-by-side diff between the two `project.labelle` files is the minimum surface area needed to add ImGui to a sokol project:

```zon
.backend = .sokol,
.gui = .{ .plugin = "imgui" },
.plugins = .{
.{ .name = "imgui", .repo = "local:../../../labelle-imgui" },
},
```

scripts/imgui_demo.zig

Opens a "Sokol + ImGui Bridge" window each frame and renders three widgets that each exercise a different piece of the IO path:

  • label — proves the render side works
  • button — proves `mouse_pos` + `mouse_button` events flow through
  • slider — proves drag deltas accumulate via the same event channel

Dependencies

Test plan

  • `labelle build` against latest assembler main + labelle-imgui main
  • Click counter increments on mouse click inside the ImGui button
  • Slider drags and updates value
  • Window can be dragged by its title bar

🤖 Generated with Claude Code

Adds examples/sokol_imgui/ — a minimal end-to-end test that exercises
the full sokol + Dear ImGui + labelle-imgui bridge stack. If this
example builds and runs with a clickable button, the entire chain is
wired correctly:

- sokol backend with `with_sokol_imgui = true` (provided by the
  labelle-imgui sokol bridge that owns its own sokol-with-imgui dep)
- cimgui linked once into the final exe
- sokol_imgui_bridge artifact resolving simgui_* symbols
- generated sokolEvent forwarding sapp events to imgui_bridge_handle_event
  so simgui's IO state actually receives mouse / keyboard input

The example is a near-copy of examples/raylib (same scenes, prefabs,
components, gizmos, scene switcher, player movement) so the side-by-side
diff between the two project.labelle files is the minimum surface area
needed to add ImGui to a sokol project:

- backend = .sokol
- gui = .{ .plugin = "imgui" }
- plugins includes labelle-imgui

scripts/imgui_demo.zig opens a "Sokol + ImGui Bridge" window each frame
and renders a label, a click counter button, and a slider — three
widgets that each exercise a different piece of the IO path:

- label proves the render side works
- button proves mouse_pos + mouse_button events flow through
- slider proves drag deltas accumulate via the same event channel

Depends on #38 (sokol event forwarding,
already merged) for the click/slider widgets to actually respond.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Apr 14, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Adds a new self-contained example project and assets without modifying engine/runtime code paths; risk is limited to example build/config integration with the local labelle-imgui plugin.

Overview
Adds a new examples/sokol_imgui demo project that runs on the Sokol backend and enables the imgui GUI plugin, intended as an end-to-end smoke test for the Sokol+Dear ImGui bridge.

The example includes minimal ECS components, prefabs, scenes (with include-based scene composition), and state-scoped scripts for menu/playing/paused flow, hot-reload (F5), and camera/player movement.

Adds scripts/imgui_demo.zig which renders an ImGui window each frame (label/button/slider + frame counter) to validate widget rendering and input event forwarding through the bridge.

Reviewed by Cursor Bugbot for commit a0a7d0b. Bugbot is set up for automated code reviews on this repo. Configure here.

@apotema
apotema merged commit a5486b7 into main Apr 14, 2026
4 checks passed
@apotema
apotema deleted the examples/sokol-imgui-smoke branch April 14, 2026 17:19

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new example project, sokol_imgui, which demonstrates the integration of Sokol and Dear ImGui within the engine. It includes a full suite of components, prefabs, scenes, and scripts to showcase features like player movement, state management, and UI rendering. However, several compilation issues were identified in the imgui_demo.zig script, specifically regarding a call to an undefined supportsWidgets function and type mismatches in catch expressions when formatting strings for GUI labels.


pub fn drawGui(game: anytype) void {
const Gui = @TypeOf(game.*).Gui;
if (!Gui.supportsWidgets()) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The supportsWidgets() function is called on the Gui adapter, but it is not defined in either plugins/imgui/src/adapter.zig or gui/sokol-imgui/src/adapter.zig. This will cause a compilation error. If this is a required part of the GuiInterface contract, it should be implemented in the adapters.

Gui.separator();

var fps_buf: [64]u8 = undefined;
Gui.label(std.fmt.bufPrintZ(&fps_buf, "frame: {d}", .{frame_counter}) catch "frame: ?");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In Zig, the catch expression requires both branches to have the same type or coerce to a common type. std.fmt.bufPrintZ returns a sentinel-terminated slice [:0]u8, while the string literal "frame: ?" is a pointer to an array (*const [8:0]u8). This will result in a compilation error. Slicing the literal to [:0]const u8 resolves the type mismatch.

    Gui.label(std.fmt.bufPrintZ(&fps_buf, "frame: {d}", .{frame_counter}) catch "frame: ?"[0..:0]);

if (Gui.button("Click me")) click_count += 1;
Gui.sameLine();
var click_buf: [32]u8 = undefined;
Gui.label(std.fmt.bufPrintZ(&click_buf, "clicks: {d}", .{click_count}) catch "clicks: ?");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to the frame counter label, this catch block will fail to compile due to a type mismatch between the slice returned by bufPrintZ and the string literal. Slicing the literal to [:0]const u8 ensures compatibility with the expected [*:0]const u8 parameter of Gui.label.

    Gui.label(std.fmt.bufPrintZ(&click_buf, "clicks: {d}", .{click_count}) catch "clicks: ?"[0..:0]);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant