gum-property-assignment

Reference guide for how Gum instantiates runtime objects from save data and applies variables to renderables. Load this when working on ToGraphicalUiElement, SetGraphicalUiElement, ApplyState, SetProperty, SetVariablesRecursively, CustomSetPropertyOnRenderable, font loading, IsAllLayoutSuspended, or isFontDirty.

447 stars

Best use case

gum-property-assignment is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Reference guide for how Gum instantiates runtime objects from save data and applies variables to renderables. Load this when working on ToGraphicalUiElement, SetGraphicalUiElement, ApplyState, SetProperty, SetVariablesRecursively, CustomSetPropertyOnRenderable, font loading, IsAllLayoutSuspended, or isFontDirty.

Teams using gum-property-assignment should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/gum-property-assignment/SKILL.md --create-dirs "https://raw.githubusercontent.com/vchelaru/Gum/main/.claude/skills/gum-property-assignment/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/gum-property-assignment/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How gum-property-assignment Compares

Feature / Agentgum-property-assignmentStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Reference guide for how Gum instantiates runtime objects from save data and applies variables to renderables. Load this when working on ToGraphicalUiElement, SetGraphicalUiElement, ApplyState, SetProperty, SetVariablesRecursively, CustomSetPropertyOnRenderable, font loading, IsAllLayoutSuspended, or isFontDirty.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Gum Property Assignment Reference

## Save-to-Runtime Instantiation

When a game loads a Gum project, `ElementSave.ToGraphicalUiElement()` is the entry point that
creates a live `GraphicalUiElement` tree from a save element (screen, component, or standard).
Without a loaded Gum project, `ElementSave` classes are not used at runtime — but `StateSave`,
`StateSaveCategory`, and `VariableSave` are always used (they power the state system on GUE).

### ToGraphicalUiElement → SetGraphicalUiElement pipeline

`ToGraphicalUiElement` (in `ElementSaveExtensions.GumRuntime.cs`) creates a GUE and delegates
to `SetGraphicalUiElement`, which does the heavy lifting in this order:

1. **AddStatesAndCategoriesRecursivelyToGue** — walks the inheritance chain (via `BaseType`)
   and copies `StateSaveCategory` and `StateSave` entries onto the GUE.
2. **CreateGraphicalComponent** — creates the underlying renderable (`IRenderable`) via
   `CustomCreateGraphicalComponentFunc` and assigns it with `SetContainedObject`.
3. **AddExposedVariablesRecursively** — registers exposed variable bindings.
4. **CreateChildrenRecursively** — iterates `elementSave.Instances`, calls
   `instance.ToGraphicalUiElement()` for each, and parents the child GUE. For screens,
   children are not parented directly (they use `ElementGueContainingThis` instead).
5. **SetInitialState** — applies the default state's variables via `ApplyState`.
6. **Animations** — if the project has `ElementAnimations`, wires them up.

After this, the GUE tree is fully created and has its default state applied. The GUE's `Tag`
and `ElementSave` properties point back to the source `ElementSave`.

For a deep dive into the full variable lifecycle including Forms state updates and
`RefreshStyles`, see the **gum-variable-deep-dive** skill.

---

## Two Paths for Setting Properties on a GUE

**Direct property setters** (e.g. `textRuntime.Font = "Arial"`) — these are typed C# properties
on `TextRuntime` or `GraphicalUiElement` that immediately call helpers like `UpdateToFontValues()`.

**String-based path** (e.g. `SetProperty("Font", "Arial")`) — used by `ApplyState` and
`SetVariablesRecursively` when processing state variables. This path goes through:

```
ApplyState / SetVariablesRecursively
  → GraphicalUiElement.SetProperty(string, object)
  → CustomSetPropertyOnRenderable.SetPropertyOnRenderable(...)
      → TrySetPropertyOnText / TrySetPropertyOnSprite / etc.
          → modifies the underlying renderable directly
```

`CustomSetPropertyOnRenderable` is the bridge between the string-based variable system and the
actual renderable objects. It lives in `Gum/Wireframe/CustomSetPropertyOnRenderable.cs` (with
parallel copies in `Runtimes/SkiaGum/` and `Runtimes/RaylibGum/`).

The delegate `GraphicalUiElement.UpdateFontFromProperties` is wired to the static
`CustomSetPropertyOnRenderable.UpdateToFontValues(IText, GUE)` at startup. This is how the
string path and the instance method path both ultimately call the same font loading logic.

All `CustomSetPropertyOnRenderable` statics (including `FontService`) are wired by
`EditorTabPlugin_XNA.StartUp()` in the Gum tool. The class itself must not reference DI
containers or service locators. Game runtimes assign `FontService` directly.

---

## Font Deferred-Loading System

Loading a `.fnt` file is expensive. A text element has ~6 font-related properties (Font,
FontSize, IsBold, IsItalic, UseFontSmoothing, OutlineThickness), so without deferral each
property assignment during a screen load triggers a separate disk read.

### The Flag: `isFontDirty`

`GraphicalUiElement.isFontDirty` is set to `true` instead of loading the font when layout is
suspended. It is consumed (font loaded, flag cleared) by `UpdateFontRecursive()`.

### Where deferral happens

| Path | Defers for `IsAllLayoutSuspended`? | Defers for `IsLayoutSuspended`? |
|------|------------------------------------|---------------------------------|
| `GUE.UpdateToFontValues()` (direct setters) | Yes | Yes |
| `CustomSetPropertyOnRenderable.UpdateToFontValues` (string path) | Yes | **No** |

The string path deliberately does **not** defer for instance-level `IsLayoutSuspended`. Doing so
would cause cascading parent layout calls when `UpdateFontRecursive` later assigns the `BitmapFont`
to a `Text` with `RelativeToChildren` dimensions inside `ResumeLayoutUpdateIfDirtyRecursive`. See
the long comment at the top of that static method for the full explanation.

### Where fonts actually load

Two code paths consume `isFontDirty`:

1. **`WireframeObjectManager`** (Gum tool screen load): after `IsAllLayoutSuspended = false`,
   calls `RootGue.UpdateFontRecursive()` then `RootGue.UpdateLayout()`. At this point all
   elements have `mIsLayoutSuspended = false` (because `ApplyState` skips `SuspendLayout` when
   the global flag is set), so every dirty text element loads its font in one pass.

2. **`ResumeLayoutUpdateIfDirtyRecursive`** (instance-level suspension): sets
   `mIsLayoutSuspended = false` on the current element *before* calling `UpdateFontRecursive()`,
   then recurses to children. This ordering is critical — if `mIsLayoutSuspended` were still true
   when `UpdateFontRecursive` runs, that element's font load would be skipped.

### Known gap

When fonts are set via the string path (`SetProperty`) during an `ApplyState` that uses
instance-level `SuspendLayout` (not `IsAllLayoutSuspended`), fonts still load immediately —
one disk read per property assignment. This is the `MonoGame ApplyState` path; fixing it
requires solving the cascading layout problem described in `CustomSetPropertyOnRenderable`.

---

## Key Files

| File | Role |
|------|------|
| `GumRuntime/GraphicalUiElement.cs` | `SetProperty`, `ApplyState`, `UpdateToFontValues` (instance), `UpdateFontRecursive`, `isFontDirty` |
| `Gum/Wireframe/CustomSetPropertyOnRenderable.cs` | String-path dispatch + static `UpdateToFontValues(IText, GUE)` |
| `GumRuntime/ElementSaveExtensions.cs` | `SetVariablesRecursively` — iterates state variables and calls `ApplyState` |
| `Gum/Wireframe/WireframeObjectManager.cs` | Sets `IsAllLayoutSuspended` around screen load, calls `UpdateFontRecursive` after |
| `Tool/EditorTabPlugin_XNA/MainEditorTabPlugin.cs` | Wires all `CustomSetPropertyOnRenderable` statics in `StartUp()` |

Related Skills

validate-code-changes

447
from vchelaru/Gum

Validate all code changes on the current branch. Spawns QA and refactoring agents in parallel to review for correctness, edge cases, code quality, and pattern adherence. Use when ready to review branch changes before merging.

skills-writer

447
from vchelaru/Gum

Creates and updates skill files (.claude/skills/*/SKILL.md) by reading source code and condensing knowledge into concise reference guides. Use when asked to create a new skill, update an existing skill, or document a subsystem for Claude Code agent context.

gum-variable-deep-dive

447
from vchelaru/Gum

Deep dive into the full variable lifecycle — from VariableSave on ElementSave through runtime application on GraphicalUiElement and Forms controls. Load this when working on styling, theming, RefreshStyles, or when you need to understand how variable values flow from save data to live visuals.

gum-unit-tests

447
from vchelaru/Gum

Reference guide for writing unit tests in the Gum repository. Load this when writing or modifying tests in Gum.ProjectServices.Tests, Gum.Cli.Tests, or any other Gum test project.

gum-tool-viewmodels

447
from vchelaru/Gum

Reference guide for Gum tool ViewModel conventions. Load this when working on ViewModels, XAML views, data binding, DependsOn, or visibility properties in the Gum tool.

gum-tool-variable-references

447
from vchelaru/Gum

Reference guide for Gum's variable reference system — Excel-like cross-instance/cross-element variable binding using Roslyn-parsed assignment syntax. Load this when working on VariableReferenceLogic, EvaluatedSyntax, ApplyVariableReferences, VariableChangedThroughReference, or the VariableReferences VariableListSave.

gum-tool-variable-grid

447
from vchelaru/Gum

Reference guide for Gum's Variables tab and DataUiGrid system. Load this when working on the Variables tab, DataUiGrid control, MemberCategory, InstanceMember, category population, property grid refresh, or category expansion state persistence.

gum-tool-undo

447
from vchelaru/Gum

Reference guide for Gum's undo/redo system. Load this when working on undo/redo behavior, the History tab, UndoManager, UndoPlugin, UndoSnapshot, or stale reference issues after undo.

gum-tool-selection

447
from vchelaru/Gum

Reference guide for Gum's editor selection system. Load this when working on click/drag selection, the rectangle/marquee selector, input handlers (move, resize, rotate, polygon points), the IsActive flag, locked instance behavior, SelectionManager coordination, or the selection event cascade (plugin events, forced default state, tree view sync).

gum-tool-save-classes

447
from vchelaru/Gum

Reference guide for Gum's save/load data model. Load this when working with GumProjectSave, ScreenSave, ComponentSave, StandardElementSave, ElementSave, StateSave, VariableSave, InstanceSave, BehaviorSave, or any serialization/deserialization of Gum project files.

gum-tool-plugins

447
from vchelaru/Gum

Reference guide for the Gum tool's plugin system, including visualization plugins (EditorTabPlugin_XNA, TextureCoordinateSelectionPlugin). Load this when working on plugin registration, PluginBase, InternalPlugin, PluginManager, plugin events, visualization/rendering concerns, or finding which internal plugin owns a feature.

gum-tool-output

447
from vchelaru/Gum

Reference guide for Gum's Output tab system. Load this when working on the Output tab, IOutputManager, MainOutputViewModel, GuiCommands.PrintOutput, or adding output/error messages to the tool.