gum-layout-engine
Deep reference for maintainers of the Gum layout engine — the UpdateLayout call chain, UpdateChildren ordering, stacking position pipeline, dirty state tracking, and performance optimizations inside GraphicalUiElement. Load when debugging layout performance, optimizing UpdateLayout/UpdateChildren, working on RefreshParentRowColumnDimensionForThis, GetWhatToStackAfter, MakeDirty, ResumeLayoutUpdateIfDirtyRecursive, or _cachedSiblingIndex.
Best use case
gum-layout-engine is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Deep reference for maintainers of the Gum layout engine — the UpdateLayout call chain, UpdateChildren ordering, stacking position pipeline, dirty state tracking, and performance optimizations inside GraphicalUiElement. Load when debugging layout performance, optimizing UpdateLayout/UpdateChildren, working on RefreshParentRowColumnDimensionForThis, GetWhatToStackAfter, MakeDirty, ResumeLayoutUpdateIfDirtyRecursive, or _cachedSiblingIndex.
Teams using gum-layout-engine 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
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/gum-layout-engine/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How gum-layout-engine Compares
| Feature / Agent | gum-layout-engine | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Deep reference for maintainers of the Gum layout engine — the UpdateLayout call chain, UpdateChildren ordering, stacking position pipeline, dirty state tracking, and performance optimizations inside GraphicalUiElement. Load when debugging layout performance, optimizing UpdateLayout/UpdateChildren, working on RefreshParentRowColumnDimensionForThis, GetWhatToStackAfter, MakeDirty, ResumeLayoutUpdateIfDirtyRecursive, or _cachedSiblingIndex.
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 Layout Engine Internals
For user-facing layout concepts (units, stacking, wrapping, Anchor/Dock), see
the **gum-layout** skill. This skill is for people debugging, optimizing, or
extending the engine itself.
All layout logic lives in `GumRuntime/GraphicalUiElement.cs`.
## UpdateLayout Call Chain
Entry point: `UpdateLayout(ParentUpdateType, int childrenUpdateDepth, XOrY?)`
### Flow (in order)
1. **Resolve `updateParent`** — evaluate `ParentUpdateType` flags against actual
parent state (stacks? depends on children? has ratio children?).
2. **Early out — suspended or invisible** — if layout is suspended
(`mIsLayoutSuspended` or `IsAllLayoutSuspended`) OR the element is invisible
and not needed for parent update, call `MakeDirty()` and return. Invisible
elements also exit if parent is invisible (unless render target).
3. **Early out — propagate to parent** — if `updateParent` is true AND
`GetIfShouldCallUpdateOnParent()` is true, call `parent.UpdateLayout()` with
`childrenUpdateDepth + 1` and **return**. The parent's layout will update
this element as a child. This is how child changes bubble up.
4. **Clear dirty state** — `currentDirtyState = null`. This is critical: it
prevents double-updates during `ResumeLayoutUpdateIfDirtyRecursive` (see
below).
5. **Pre-children dimensions** — update dimensions that do NOT depend on
children (Absolute, PercentageOfParent, etc.) so children have correct parent
sizes when they lay out.
6. **First children pass (if dimensions depend on children)** — update children
with absolute layout types so the parent can measure them. With
`UseFixedStackChildrenSize`, only the first child is updated here (O(1)
instead of O(n)).
7. **Post-children dimensions** — update RelativeToChildren / RelativeToMaxParentOrChildren
dimensions now that children have been measured.
8. **Wrapped children pass** — if `WrapsChildren`, update `StackedWrapped`
children and re-measure dimensions with wrapping considered.
9. **UpdatePosition** — calculate X/Y based on units, origin, parent stacking.
For stacked children, this calls `GetWhatToStackAfter` to find position
relative to the previous sibling.
10. **RefreshParentRowColumnDimensionForThis** — if parent stacks, update the
per-row/column max dimension.
11. **Full children pass** — `UpdateChildren(depth, ChildType.All, ...)` updates
all children. Children already updated in step 6 are skipped via
`alreadyUpdated` set.
12. **Post-layout dimension check** — if size changed and parent depends on
children, re-update dimensions. If still changed, update parent.
## UpdateChildren Internals
### Two-pass ordering for Ratio dependencies
When some children use `Ratio` width/height and siblings use complex units
(RelativeToChildren, PercentageOfOtherDimension, MaintainFileAspectRatio,
ScreenPixel, RelativeToMaxParentOrChildren), those complex-unit siblings must be
updated **first**. Ratio children need sibling sizes to compute remaining space.
Pass 1 (conditional): update children with `DoesDimensionNeedUpdateFirstForRatio`
units. Pass 2: update all remaining children.
### shouldFlagAsUpdated
For `Regular` layout, children are flagged as updated to avoid redundant work.
For stacked layouts, children are **not** flagged — they need a second pass to
update positions in order (stacking depends on sibling order).
### _cachedSiblingIndex
Set on each child (`child._cachedSiblingIndex = i`) in the iteration loop
before calling `UpdateLayout`. Used by `GetWhatToStackAfter` to avoid an
O(n) `IndexOf` call. Falls back to `IndexOf` if the cache is stale (element
not at expected position).
## Stacking Position Pipeline
`UpdatePosition` → `TryAdjustOffsetsByParentLayoutType` → `GetWhatToStackAfter`
### GetWhatToStackAfter
Finds the previous visible sibling and computes the offset to stack after it.
1. **Find sibling index** — uses `_cachedSiblingIndex` (O(1)) with `IndexOf`
fallback (O(n)). The cache is valid during `UpdateChildren` but may be stale
for individual property-change-triggered layouts.
2. **Find previous visible sibling** — walks backward from `thisIndex` skipping
invisible elements.
3. **Determine wrap** — if wrapping, increments `StackedRowOrColumnIndex` and
sums `StackedRowOrColumnDimensions` for all previous rows/columns.
4. **Compute offset** — for non-wrapping: previous sibling's position + size +
`StackSpacing`. For wrapping: row/column dimension sum.
### RefreshParentRowColumnDimensionForThis
Maintains `parent.StackedRowOrColumnDimensions[rowOrColumnIndex]` — the max
cross-axis dimension for each row (LeftToRight) or column (TopToBottom).
**O(1) fast path**: if this child's dimension >= stored max, just set it.
This is the common case during sequential layout (e.g., populating a ListBox).
**O(n) fallback**: if this child's dimension < stored max, it may have been the
max-holder and shrunk. Must rescan all siblings in the same row/column to find
the true max.
## Dirty State and Suspension
### MakeDirty
Called when `UpdateLayout` is invoked on a suspended or invisible element.
Accumulates into `currentDirtyState`:
- `ParentUpdateType` — OR'd together across multiple calls
- `ChildrenUpdateDepth` — max of all calls
- `XOrY` — set to null if different axes were dirtied (means update both)
### ResumeLayoutUpdateIfDirtyRecursive
Called when layout is resumed after suspension. Walks the tree:
1. Clear `mIsLayoutSuspended`
2. If `currentDirtyState != null`, call `UpdateLayout` with accumulated state
3. Recurse into children
**No double-update**: the parent's `UpdateLayout` (step 2) calls
`UpdateChildren`, which calls each child's `UpdateLayout`, which clears that
child's `currentDirtyState`. When recursion (step 3) reaches that child, its
dirty state is already null — it skips the `UpdateLayout` call.
### EffectiveDirtyStateParentUpdateType
Combines `currentDirtyState.ParentUpdateType` with runtime checks:
`GetIfParentHasRatioChildren()` and `GetIfParentStacks()`. These are checked
at resume time, not when dirtied, so they reflect current state.
## Upward Propagation
### GetIfShouldCallUpdateOnParent
Returns true if:
- Parent dimensions depend on children (`GetIfDimensionsDependOnChildren`)
- Parent stacks children (any non-Regular `ChildrenLayout`)
- Any sibling uses `Ratio` width/height
When true, `UpdateLayout` delegates to the parent (step 3 above) instead of
laying out the element directly. The parent will re-lay out all children.
## Performance Patterns
| Optimization | What it avoids | Where |
|---|---|---|
| `_cachedSiblingIndex` | O(n) `IndexOf` per child → O(n²) total | `GetWhatToStackAfter` |
| `RefreshParentRowColumn` fast path | O(n) rescan per child → O(n²) total | `RefreshParentRowColumnDimensionForThis` |
| `UseFixedStackChildrenSize` | Iterating all children in `GetMaxCellHeight` | `UpdateLayout` step 6, `UpdateHeight` |
| `xOrY` parameter | Recalculating unchanged axis | Throughout |
| `childrenUpdateDepth` | Unbounded recursion | `UpdateChildren` decrements per level |
| `alreadyUpdated` set | Re-updating children measured in pre-pass | `UpdateChildren` |
### Diagnostic Counters
- `UpdateLayoutCallCount` — total layout calls (incremented after parent propagation check)
- `ChildrenUpdatingParentLayoutCalls` — times a child triggered parent relayoutRelated Skills
gum-layout
Reference guide for Gum's layout system — dimension units, position units, children layout modes, layout calculation flow, and layout suspension. Load when working on Width/HeightUnits, XUnits/YUnits, stacking, wrapping, auto-sizing, Anchor/Dock, or GraphicalUiElement layout logic. For deep engine internals (debugging/optimizing UpdateLayout, UpdateChildren, dirty state), see the gum-layout-engine skill instead.
validate-code-changes
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
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
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
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
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
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
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
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
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
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
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.