GG-UI — Foundations
What every author of GG-UI code needs: the pieces, how a build works, how UI stays current, and where each detail is documented. The reference pages (Core, Interaction, Styling, Rendering) go deeper; this page is the map.
The Pieces
| Piece | What it is | Where |
|---|---|---|
GGUI library | The UI: nodes, the builder, layout, bindings, input, styles. Depends only on TnxFoundation (enforced by CMake), runs headless | src/GGUI |
TnxFoundation | What the UI shares with the engine: allocation, handles, the lock-free ring, TnxName, JSON, Fixed32 | src/Foundation |
UIContext | One document: a tree of nodes, built by a root function, updated once a frame | UIContext.h |
UI | The builder passed to every UI function: ui.Row, ui.Label, ui.Button, ... | UI.h |
UIHost | The engine side: a document per window, input in, the published snapshot out to the renderer, the dock, styles, strings | src/Runtime/UI |
The engine talks to GGUI through three interfaces: platform events in, published snapshots out, and a binding reader for game data. Nothing else crosses, which is why the whole library can be tested without a window (Testbed/src/Tests/GGUI_*.cpp).
A Document
GGUI::UIContext context; context.Mount([](UI& ui) { if (auto page = ui.Column({ .Gap = 6, .Padding = GGEdges::All(8) })) { ui.Label("Game.Menu.Title"); ui.Button("Game.Menu.Play", [] { StartGame(); }); } }); context.Update(); // runs what needs running; with nothing changed, runs nothing context.UpdateLayout();
Under the engine, UIHost::Mount(window, root) does this for a window and updates it every frame. A UI function describes the tree; it doesn't draw. Running it again with the same result changes nothing.
Writing UI
Elements and props. Each element takes its subject, then props (a designated-initialiser struct), then what it shows, then what a change or click does. Props can be left out.
ui.Label("Editor.Stats.Live", { .Class = "stats-name" }); ui.Slider({ .Min = 0, .Max = 100 }, volume, [&](float value) { volume = value; }); ui.Button("Editor.Scene.Save", [editor] { ... });
Two forms for containers. A scope object builds children now; a lambda is content the container stores and can re-run on its own.
if (auto row = ui.Row({ .Gap = 8 })) // now: children follow in this function { ui.Label("Name"); ui.Spacer(); } ui.Column({}, [=](UI& ui) { ... }); // stored: captures by value
Plain C++ is the control flow. Branch with if, repeat with for. In a loop, give each element a key so it keeps its identity (focus, scroll, state) when the list changes: ui.Key(item.ID).
Shorthand. using namespace GGUI::StyleKeywords; allows .Width = Grow, .Padding = 8, .Align = Center, and a class where props would be: ui.Label("x", "muted").
Keeping It Current
Two mechanisms, each with one job.
| Re-run | Binding | |
|---|---|---|
| Use when | Data decides which elements exist | An element shows a value that changes |
| How | The component watches a GGRevision; its owner bumps it | The element is given a GGBinding |
| Cost | The function runs and its result is matched to the existing nodes | One read and compare per update, no function runs |
ui.Watch(selectionChanged); // re-run this component when it is bumped const TnxBinding health = TnxBind(player->Body, &CHealth<>::Value); ui.Label(health, { .Decimals = 0 }); // a bound number: updates with no re-run ui.Progress({}, health / TnxBind(player->Body, &CHealth<>::Max)); // a bound fill
- Components are the re-run boundaries. A stored lambda is one; so is
ui.Component(fn). An unchanged component (same function, equal captures or properties) is skipped when its parent re-runs. - Local state lives with the node:
auto open = ui.State(false);thenopen.Get()/open.Set(true). Setting it re-runs the component it belongs to. - Bindings read the published frame, never live objects. Game values are bound with
TnxBind(entity, &CHealth<>::Value); editor and tool values through their own targets (EngineStatsfor the stats panel). - Idle is free. With nothing watched bumped and no bound value changed, a document does no work.
Text, Looks, and Input
- Text is a string ID.
"Editor.Log.Title"is looked up in the string table (Trinyx/assets/strings/en.stringsfor the engine's own text);GGText::Verbatim(name)shows data as it is. Titles and tooltips always come from the table (Localisation). - Looks are not in code. Code names a class; a style sheet (
.ggss, hot reloaded) gives it colours, borders, and states (:hover,:focus,:disabled). The engine theme isTrinyx/assets/ui/trinyx.ggss(Styling). - Handlers are lambdas, taking the event or nothing. They run on the thread that updates the document, so they change UI state freely and hand anything else to its owner (in the editor,
EditorContext::).Do - Shortcuts are input actions, not keys in code:
ui.Command(action, handler), and menu items and tool buttons given the action show its chord (Input Actions).
What Is Built In
| Kind | Calls |
|---|---|
| Layout | Row, Column, Stack, Grid, Place, Spacer, Split, Tabs, Dock, SafeArea |
| Choosing | Switcher (one child by a value), Repeat (an item a number of times), Present (in layout or not) |
| Content | Label, Image, Swatch, Progress, Viewport, Canvas |
| Values | Button, Toggle (both may take content), Slider, Number, Vector, Dropdown, Carousel, Choice, Color, TextField, TextArea, KeyBinding |
| Data | List, Tree, Table, Tiles, Picker over a GGDataSource; Fields and Properties from reflection |
| Popups | Tooltip, Menu, ContextMenu, Popup, Dialog, Ask, notices |
| Tools | MenuBar, Toolbar, ToolButton, LogView, CommandLine, Graph |
| Assets | Use: a widget asset made as data (Widget Assets) |
A widget is a function: a new one needs no class and no registration. A widget that keeps its own state across frames (a text editor, a graph) is a struct with hooks (UIWidget.h).
Testing
A document runs headless: build it, read the nodes (Nodes(), Text(node), Type(node), Paint(node)), click (Click(node), Dispatch(event)), and update bindings against a fake reader. Authoring mistakes are reported through UIContext::SetDiagnostics, which a test can collect. The GGUI_* tests are the examples.
Next
- A first HUD, step by step: First Steps with GG-UI
- Hosting GG-UI yourself, and how the engine hosts it: Using GG-UI
- Building a panel for the editor: Building an Editor Panel
- The full authoring rules and data views: Core
- Focus, value widgets, popups, text, actions: Interaction
- Sheets, tokens, transitions: Styling