How the ArmorPaint UI Framework Is Implemented: Core Architecture and Immediate-Mode Design

ArmorPaint implements a lightweight, custom immediate-mode UI (IMGUI) framework written in C within the Iron engine, featuring theme-driven scaling, ratio-based layouts, and GPU-baked static assets.

The ArmorPaint UI framework provides the foundation for the application’s entire interface, from the node editor to the toolbar and sidebar. Built directly on the Iron rendering back-end rather than relying on external dependencies, this self-contained system handles everything from layout calculations to text editing and input processing. Understanding how this UI framework structures its context, drawing primitives, and widget state reveals why ArmorPaint maintains high performance across platforms while offering a fully customizable interface.

Core Architecture and State Management

At the heart of the ArmorPaint UI framework lies a minimalist state management approach that follows immediate-mode principles, where interface elements are redrawn every frame based on current application state.

The UI Context Singleton

The framework maintains global UI state through a singleton context structure. In base/sources/iron_ui.c, the ui_t *current pointer holds all transient data for the active frame, including the active window handle, input flags, theme configuration, and cursor position. Developers initialize the context with ui_set_current() and signal frame completion with ui_end_frame(), which clears per-frame flags and finalizes draw commands. This singleton pattern ensures that any part of the codebase can access UI state without passing context pointers through every function call.

Theme-Driven Scaling and Dimensions

Visual consistency relies on the ui_theme_t structure, which stores base measurements for element widths, heights, offsets, font sizes, and color palettes. Helper macros like UI_ELEMENT_W(), UI_ELEMENT_H(), and UI_SCALE() translate these base units to the current DPI scale, automatically handling high-resolution displays. When the UI is disabled, functions such as ui_fill(), ui_rect(), and ui_draw_shadow() automatically apply the theme’s fade-out alpha values to indicate inactive states.

Immediate-Mode Rendering Pipeline

Unlike retained-mode frameworks that maintain persistent widget objects, ArmorPaint’s UI framework reconstructs the interface every frame through explicit drawing commands.

Direct Drawing Primitives

Text rendering utilizes ui_draw_string(), which handles dynamic glyph loading, string truncation, and optional color-coding for syntax highlighting. Lower-level drawing wraps GPU calls through functions like draw_filled_rect and draw_rect, exposed via convenient wrappers that apply theme colors automatically. This direct approach eliminates the need for a separate UI scene graph, reducing memory overhead and simplifying synchronization with application state.

Layout System with Rows and Ratios

The layout engine uses a flexible ratio-based system defined in base/sources/iron_ui.c. The ui_row() function accepts an array of floating-point values where positive numbers indicate proportional column widths and negative values specify absolute pixel widths. Convenience macros—including ui_row2() through ui_row7()—provide predefined layouts for common configurations. The framework supports both UI_LAYOUT_VERTICAL and UI_LAYOUT_HORIZONTAL modes; ui_end_element_of_size() advances either the Y-coordinate (new line) or X-coordinate (same row) based on the current mode, enabling complex nested layouts without manual coordinate calculations.

Input Handling and Widget State

Processing user interaction requires careful state tracking across frames while maintaining the simplicity of immediate-mode design.

Input Processing Pipeline

All mouse, touch, and keyboard events flow through ui_end_input() in the core UI file. This function updates hover detection, pressed/released states, and key repeat timers. The framework translates touch-hold gestures into right-click events for mobile compatibility and resets per-frame input flags to ensure state doesn’t leak between frames. This centralized input handling ensures consistent behavior across different hardware configurations.

Widget Identification and Focus

To track focus and editing state across frames without retaining widget objects, the framework generates stable identifiers using ui_widget_id(). This function hashes the current window pointer, a kind identifier constant, and an optional user-provided seed to create unique IDs for each element. These IDs power the focus management system, tooltip tracking, and text editing state persistence, solving the classic immediate-mode problem of tracking which element is currently active.

Advanced Text and Asset Features

Beyond basic widgets, the ArmorPaint UI framework includes sophisticated systems for text manipulation and rendering optimization.

Text Editing Implementation

The framework provides a full-featured text field editor through ui_update_text_edit() and associated helper functions including ui_insert_char_at() and ui_remove_char_at(). This implementation handles cursor movement, text selection, clipboard operations, tab insertion, and Ctrl-based word navigation. By integrating directly into the immediate-mode loop, text fields support real-time validation and formatting without asynchronous callbacks.

Tooltips and Pop-ups

Delayed tooltips appear through ui_draw_tooltip(), which renders text or image tips after detecting a sustained hover state. The system resets the tooltip timer whenever input changes, preventing stale pop-ups from lingering during rapid interface interactions. This timing-based approach fits naturally within the per-frame update cycle.

GPU Baking for Static Assets

To optimize performance, ui_bake_elements() pre-renders common UI assets—such as checkboxes, radio buttons, and rounded corners—into GPU textures once per UI session. This caching strategy avoids recreating static geometry every frame while retaining the flexibility of immediate-mode API design. The baked textures persist across frames until the UI scale or theme changes, significantly reducing draw call overhead for complex interfaces.

Modular UI Components

Higher-level interface modules build upon these core primitives to create the complete ArmorPaint interface. The paint/sources/ui/ directory contains specialized implementations: ui_toolbar.c renders the top tool strip, ui_sidebar.c manages the left-hand layer panel, ui_menubar.c handles application menus, ui_nodes.c implements the visual node editor, and ui_statusbar.c displays cursor information at the bottom. Each module follows the same immediate-mode pattern, calling core functions like ui_button() and ui_text_input() to compose functional panels.

/* Begin a UI region for a window (x, y, width) */
ui_begin_region(ui, 0, 0, UI_ELEMENT_W() * 10);

/* Layout a row with three columns: 30% / 40% / remaining */
f32_array_t ratios = { .length = 3, .buffer = (float[]){0.3f, 0.4f, -1.0f} };
ui_row(&ratios);

/* A simple button */
if (ui_button("Paint", UI_ALIGN_LEFT, "")) {
    /* Button was pressed → toggle painting mode */
    paint_mode = !paint_mode;
}

/* Text input field */
char name[UI_TEXT_MAX] = "";
ui_text_input(&name, "Enter name", UI_ALIGN_LEFT, true, true);

/* End the region – automatically draws combo boxes and tooltips */
ui_end_frame();

Summary

The ArmorPaint UI framework demonstrates how immediate-mode principles can power professional-grade applications:

  • Singleton context architecture in base/sources/iron_ui.c manages per-frame state through ui_set_current() and ui_end_frame()
  • Theme-driven scaling via UI_ELEMENT_W(), UI_ELEMENT_H(), and UI_SCALE() ensures DPI-independent rendering
  • Ratio-based layout system using ui_row() and layout modes supports complex responsive designs without manual coordinate math
  • Stable widget identification through ui_widget_id() hashing enables focus management in immediate-mode context
  • GPU asset baking via ui_bake_elements() optimizes performance by caching static UI elements into textures
  • Modular component structure separates concerns across files like ui_toolbar.c, ui_sidebar.c, and ui_nodes.c

Frequently Asked Questions

What makes ArmorPaint's UI framework different from traditional retained-mode GUI libraries?

ArmorPaint uses an immediate-mode architecture where widgets are reconstructed and drawn every frame rather than maintaining persistent object hierarchies. This approach eliminates synchronization issues between application state and UI state, reduces memory allocation overhead, and allows the entire interface to scale dynamically with DPI changes through the ui_theme_t scaling system. Functions like ui_end_frame() clear all transient state, ensuring the UI always reflects current application data.

How does the UI framework handle high-DPI displays and different screen resolutions?

The framework implements automatic scaling through helper macros defined in base/sources/iron_ui.c. The UI_SCALE() macro calculates the ratio between base theme dimensions and actual display DPI, while UI_ELEMENT_W() and UI_ELEMENT_H() apply this scale to all measurements. Theme structures store base units, and all drawing operations multiply these values by the current scale factor, ensuring crisp rendering on both standard and high-resolution displays without manual coordinate adjustments.

Where is text editing functionality implemented in the ArmorPaint UI framework?

Full text editing capabilities reside in base/sources/iron_ui.c within functions like ui_update_text_edit(), ui_insert_char_at(), and ui_remove_char_at(). This system handles cursor navigation, selection ranges, clipboard operations, tab characters, and Ctrl-based word navigation. The immediate-mode design allows text fields to validate input in real-time, with state persisted across frames through the widget ID system rather than through retained text box objects.

What files should developers examine to understand the complete UI implementation?

Start with base/sources/iron_ui.c for the core engine and base/sources/iron_ui.h for the public API. For concrete usage examples, examine paint/sources/ui/ui_toolbar.c for toolbar buttons, paint/sources/ui/ui_nodes.c for the visual node editor, and paint/sources/ui/ui_sidebar.c for panel layouts. The header file defines all public structures including ui_t, ui_theme_t, and layout constants, while the source files demonstrate how higher-level components compose interfaces from primitive drawing functions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →