justgook/smgui

★ 1Forks 0OdinGitHub ↗Compare

README

SMGUI for Odin

An idiomatic Odin rewrite of the state-mode graphical user interface toolkit SMGUI.

The original ANSI C project is pinned as the reference-c git submodule. It remains the behavioral reference for API, event, and framebuffer parity tests.

Bootstrap

Install Nix and direnv, then clone and enter the repository:

git clone --recurse-submodules <repository-url>
cd smgui
direnv allow
make check
make run

Direnv loads the pinned Nix development shell from flake.nix, including Odin, the C toolchain, GLFW, and native graphics dependencies. After changing flake.nix or flake.lock, approve it again with direnv allow.

Use make help to list Odin and reference-C build targets. make run launches the Odin migration target derived from reference-c/docs/screen1.png; make c-run launches the original C widgets example behind that screenshot.

Layout

  • examples — visual/manual migration examples (make run)
  • smgui/ui.odin — backend-independent interface and implementation target
  • smgui/{sdl2,sdl3,glfw,raylib,sokol} — presentation and event adapters
  • smgui/widgets — optional custom widgets
  • psf2, ssfn, spritesheet — fixed, scalable, and Aseprite sprite-sheet font packages
  • scripts/aseprite-theme, themes — Aseprite theme conversion and generated SMGUI theme bundles
  • reference-c — pinned original implementation
  • tests/parity — behavioral and framebuffer parity harness

The Odin interface intentionally uses slices, enums, bit sets, and typed errors rather than preserving C source compatibility.

The default commands show the migration target and its original reference:

make run                         # Odin target with Raylib
make c-run                       # upstream reference-c/examples/widgets.c

This target stays intentionally visible while parity work proceeds, making cross-form layout gaps and missing controls obvious. For exact paired form trees used by framebuffer comparison, run:

make smoke-odin
make smoke-c

Other examples and backends remain selectable explicitly:

make run EXAMPLE=basic BACKEND=raylib
make run BACKEND=sokol              # same smoke forms via Sokol
make c-example-run C_EXAMPLE=helloworld

Both make run commands render the same examples/smoke form tree. The Sokol adapter uses sokol-odin and owns the platform loop, so Sokol applications call smgui/sokol.run with their context, forms, and optional init/frame hooks. make sokol-libs builds its native libraries; normal make check and Sokol builds do this automatically.

Set SMGUI_SKIN to apply a packed PNG skin to the smoke application with either backend. The bundled reference skin is a ready-to-use example:

SMGUI_SKIN=reference-c/examples/skin.png make run
SMGUI_SKIN=reference-c/examples/skin.png make run BACKEND=sokol

The smoke example also offers the bundled proportional Aseprite fonts without changing the PSF2 parity default:

SMGUI_FONT=aseprite make run
SMGUI_FONT=aseprite-mini make run

Both are rendered at nearest-neighbour scale 2. Their assets are licensed under CC BY 4.0; see spritesheet/fonts/LICENSE.txt.

The generated Catppuccin Mocha colors and skin can be selected together:

SMGUI_THEME=catppuccin-mocha SMGUI_FONT=aseprite make run

A native .Panel Form renders named Panel_Style nine-slices in normal layout order and clips its children. panel_padding defines independent content insets, panel_background fills beneath the nine-slice center without affecting layout, and panel_content = .Transparent clears the full nine-slice center for a host-rendered canvas, independently of child padding. Buttons retain the default 4px horizontal content padding unless button_horizontal_padding is set. Set button_fixed_size to preserve explicit button dimensions when oversized icon bounds should not drive layout. Set button_style to a normal/focused Panel_Style pair to replace the fixed-skin button chrome with named nine-slices; hover, pressed, and selected states use the focused slice. Adding .Focused selects the focused slice without turning the Panel into a popup or overlay. Generated theme bundles expose style_pair for constructing these styles from Aseprite part names.

set_png_skin(ctx, png, scale) optionally scales packed skin graphics by a positive integer at load time using nearest-neighbor sampling; scale defaults to 1. Generated bundles provide matching scale parameters for their skin, style atlas, and icon atlas.

See scripts/aseprite-theme for converting other Aseprite themes into separate skin, icon-atlas, nine-slice style-atlas, and color/attribute outputs.

The skin file must use SMGUI's packed skin-atlas format: one sprite per skin image in the documented order, with its rectangles stored in the PNG comment metadata. An unset or empty SMGUI_SKIN keeps the default color theme.

Run completed small framebuffer fixtures, or generate the large smoke fixture:

make parity                        # expected-green element/state fixtures
make parity-case CASE=empty
make skin-parity                   # representative C/Odin fixtures with the bundled skin
make parity-fuzz FUZZ_SEED=1 FUZZ_CASES=20
make smoke-images
make smoke-compare                 # non-zero until smoke parity is reached

See tests/manual/README.md for covered controls and the comparison procedure. BACKEND is part of the build interface now; additional values will become available as their adapters are migrated.

Contributors

justgook

Issues