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).

447 stars

Best use case

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

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).

Teams using gum-tool-selection 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-tool-selection/SKILL.md --create-dirs "https://raw.githubusercontent.com/vchelaru/Gum/main/.claude/skills/gum-tool-selection/SKILL.md"

Manual Installation

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

How gum-tool-selection Compares

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

Frequently Asked Questions

What does this skill do?

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).

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 Editor Selection System Reference

## Overview

Selection in the wireframe (XNA) editor is coordinated by `SelectionManager`. It delegates specific interactions to a set of **input handlers**, each responsible for one type of gesture (move, resize, rotate, polygon point editing). A separate **rectangle selector** handles marquee/rubber-band multi-selection. Locking (`InstanceSave.Locked`) cuts across all of these.

## Input Handlers

**Base class:** `Tool/EditorTabPlugin_XNA/Editors/Handlers/InputHandlerBase.cs`

Each handler represents one interaction mode. Concrete handlers:

| Handler | File | Responsibility |
|---------|------|----------------|
| `MoveInputHandler` | `Handlers/MoveInputHandler.cs` | Drag-to-move selected instance(s) |
| `ResizeInputHandler` | `Handlers/ResizeInputHandler.cs` | Resize handle dragging |
| `RotationInputHandler` | `Handlers/RotationInputHandler.cs` | Rotation handle dragging |
| `PolygonPointInputHandler` | `Handlers/PolygonPointInputHandler.cs` | Polygon vertex select/move/add/delete |

### Handler Lifecycle

```
Mouse down  → HandlePush(x, y)  → returns true to claim gesture; sets IsActive = true
Mouse drag  → OnDrag()          → only meaningful when IsActive; applies transform
Mouse up    → OnRelease()       → cleans up; resets IsActive to false
```

`HandlePush` returns `bool`: `true` means this handler claims the gesture and sets `IsActive = true`; `false` passes to the next handler or the rectangle selector.

### `IsActive` Flag

`IsActive = true` signals that a handler owns the current drag gesture. It suppresses the rectangle selector — `SelectionManager` passes `isHandlerActive = true` to `RectangleSelector.HandleDrag`, which returns immediately. Must be set in `HandlePush` when claiming a gesture and reset in `OnRelease`.

The base class `HandlePush` automatically checks `Context.IsSelectionLocked()` and returns `false` if locked. Handlers that **override** `HandlePush` must replicate or explicitly call this check.

## Rectangle Selector (Marquee Selection)

**File:** `Tool/EditorTabPlugin_XNA/RectangleSelector.cs`

The rectangle selector activates on drag when no handler is active and the cursor is not over the element body (or Shift is held for additive selection), after a minimum drag distance is exceeded. `SelectionManager` passes `isHandlerActive` based on whether any handler's `IsActive` is `true`.

`GetElementsInRectangle()` finds visible elements whose bounds intersect the drag rectangle, skipping `ScreenSave` elements and instances where `Locked == true`. On release, it either replaces the selection or toggles additively (Shift held).

## Locking (`InstanceSave.Locked`)

`InstanceSave.Locked` is defined in `GumDataTypes/InstanceSave.cs`. The helper `EditorContext.IsSelectionLocked()` (in `Tool/EditorTabPlugin_XNA/Editors/EditorContext.cs`) returns `true` when the selected instance is locked.

### Where Locking Is Enforced

| Location | File | What It Prevents |
|----------|------|-----------------|
| `InputHandlerBase.HandlePush()` | `InputHandlerBase.cs` | Base lock check; handlers that don't override inherit this |
| `PolygonPointInputHandler.HandlePush()` | `PolygonPointInputHandler.cs` | Overrides base; manually checks lock before allowing vert select/add |
| `PolygonPointInputHandler.TryHandleDelete()` | `PolygonPointInputHandler.cs` | Prevents DEL key from deleting verts |
| `PolygonPointInputHandler.UpdateHover()` | `PolygonPointInputHandler.cs` | Hides the "add point" sprite on polygon edges |
| `ElementCommands.MoveSelectedObjectsBy()` | `Gum/ToolCommands/ElementCommands.cs` | Skips locked instances in multi-selection moves |
| `ResizeInputHandler.ApplySizeChange()` | `ResizeInputHandler.cs` | Skips locked instances during resize |
| `MoveInputHandler.ApplyAxisLockIfNeeded()` | `MoveInputHandler.cs` | Skips locked instances during axis-lock correction |
| `MoveInputHandler.ApplyAxisLockToSelectedState()` | `MoveInputHandler.cs` | Skips locked instances when writing axis-lock to state |
| `MoveInputHandler.SnapSelectedToUnitValues()` | `MoveInputHandler.cs` | Skips locked instances during snap-to-unit |
| `RectangleSelector.GetElementsInRectangle()` | `RectangleSelector.cs` | Excludes locked instances from marquee results |
| `SelectionManager.ReverseLoopToFindIpso()` | `SelectionManager.cs` | Prevents click-selection of locked instances on canvas |
| `ListBoxDisplay` (variable grid) | `WpfDataUi/Controls/ListBoxDisplay.xaml.cs` | Disables Add/Delete/Edit in list variables (e.g. polygon Points) |

### Locked + IsActive Interaction (Critical)

When a locked instance is selected and the cursor is over one of its polygon verts, `PolygonPointInputHandler.HandlePush` must: detect the vert, set `IsActive = true` (to suppress the rectangle selector), but not set `_grabbedIndex` (so `OnDrag` is a no-op), and return `true` to consume the push. Without setting `IsActive`, the rectangle selector activates on drag because the cursor over a vert is typically not "over body".

### Locked Selection Display

`LockedSelectionVisual` draws a dashed bounding rectangle for a locked selected instance, replacing the resize handles that would normally appear. It shows regardless of the instance's `Visible` property. Registered in `StandardWireframeEditor`; not used in `PolygonWireframeEditor`.

### Locked Instances Are Still Tree-Selectable

Locked instances cannot be canvas-clicked or rectangle-selected, but **can always be selected via the tree view** — the only way to select a locked instance to unlock it. Multi-selection of mixed locked/unlocked is supported; transforms apply only to unlocked members.

## `_lastPushWasOnLockedBody`

Tracked in `SelectionManager.ProcessInputForSelection()` — set to `true` when the selected instance is locked and the cursor is over the body. Used in `ProcessRectangleSelection()` to prevent deselection when the user releases the mouse over a locked body without dragging.

## Selection Event Cascade

When the user selects an instance (via tree view or wireframe), `SelectedState` orchestrates a synchronous cascade of plugin events:

```
User selects instance
  → SelectedState.HandleSelectedInstances()
    → PerformAfterSelectInstanceLogic()
      → SelectedStateSave = element.States[0]  (forced default state)
        → PluginManager.ReactToStateSaveSelected()  ← fires FIRST
    → PluginManager.InstanceSelected()               ← fires SECOND
```

**Key behaviors:**
- State selection fires BEFORE instance selection (from inside `PerformAfterSelectInstanceLogic`). State is only force-selected when the current state doesn't belong to the new element (checked via `AllStates.Contains`).
- Both events trigger `RefreshEntireGrid` in `MainVariableGridPlugin`. A `_stateJustRefreshedGrid` flag prevents the double refresh — set by `HandleStateSelected`, checked and consumed by `HandleInstanceSelected`.
- `MainTreeViewPlugin` responds to `InstanceSelected` by syncing the tree view node. It sets `SuppressCallAfterClickSelect` on `ElementTreeViewManager` so the `Select` methods update the visual tree node without re-firing `CallAfterClickSelect`, which would cause a redundant plugin cascade.

**`IsInUiInitiatedSelection` vs `SuppressCallAfterClickSelect`:** `IsInUiInitiatedSelection` is set during `OnSelect` to prevent programmatic `Select` calls from re-entering while the tree view processes a user-initiated selection — but it's cleared before plugin events fire, so it doesn't prevent the `MainTreeViewPlugin` sync path. `SuppressCallAfterClickSelect` handles that case specifically.

### Cascade Key Files

| File | Purpose |
|------|---------|
| `Gum/ToolStates/SelectedState.cs` | `HandleSelectedInstances`, `PerformAfterSelectInstanceLogic`, `HandleStateSaveSelected` |
| `Gum/Plugins/PluginManager.cs` | `InstanceSelected`, `ReactToStateSaveSelected` event dispatch |
| `Gum/Plugins/InternalPlugins/TreeView/MainTreeViewPlugin.cs` | Tree view sync with `SuppressCallAfterClickSelect` |
| `Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.cs` | `Select` methods, `CallAfterClickSelect`, both suppression flags |
| `Gum/Plugins/InternalPlugins/VariableGrid/MainVariableGridPlugin.cs` | `_stateJustRefreshedGrid` double-refresh guard |

## Key Files Summary

| File | Purpose |
|------|---------|
| `Tool/EditorTabPlugin_XNA/SelectionManager.cs` | Main coordinator; manages `IsOverBody`, routes events to handlers, passes `isHandlerActive` to rectangle selector |
| `Tool/EditorTabPlugin_XNA/RectangleSelector.cs` | Marquee selection; activation gated on `isHandlerActive` and `IsOverBody` |
| `Tool/EditorTabPlugin_XNA/Editors/Handlers/InputHandlerBase.cs` | Base class; provides default `HandlePush` with lock check |
| `Tool/EditorTabPlugin_XNA/Editors/Handlers/MoveInputHandler.cs` | Move gesture; also handles axis lock and snap-to-unit for multi-selection |
| `Tool/EditorTabPlugin_XNA/Editors/Handlers/ResizeInputHandler.cs` | Resize handle gestures |
| `Tool/EditorTabPlugin_XNA/Editors/Handlers/PolygonPointInputHandler.cs` | Polygon vertex editing; overrides `HandlePush` (must manage lock manually) |
| `Tool/EditorTabPlugin_XNA/Editors/EditorContext.cs` | Provides `IsSelectionLocked()` helper used throughout handlers |
| `Tool/EditorTabPlugin_XNA/Editors/Visuals/LockedSelectionVisual.cs` | Dashed bounding outline for locked selected instances; display-only, no interaction |
| `GumDataTypes/InstanceSave.cs` | `Locked` property definition |
| `Gum/ToolCommands/ElementCommands.cs` | `MoveSelectedObjectsBy()`; skips locked instances in multi-move |
| `WpfDataUi/Controls/ListBoxDisplay.xaml.cs` | Variable grid list control; respects `IsReadOnly` (driven by `Locked`) |

Related Skills

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-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.

gum-tool-font-generation

447
from vchelaru/Gum

Reference guide for Gum's bitmap font generation pipeline — how the tool converts font properties into .fnt/.png files via bmfont.exe. Load this when working on BmfcSave, HeadlessFontGenerationService, FontManager, BmfcTemplate.bmfc, font cache naming, texture size estimation, or the GumProjectFontGenerator CLI.

gum-tool-file-watch

447
from vchelaru/Gum

Reference guide for Gum's FileWatch system. Load this when working on file watching, external file change detection, IgnoreNextChangeUntil, FileWatchManager, FileWatchLogic, FileChangeReactionLogic, or reloading assets/elements when files change on disk.

gum-tool-errors

447
from vchelaru/Gum

Reference guide for Gum's error detection and display system. Load this when working on the Errors tab, error icons ("!" mark) in the tree view, ErrorChecker, ErrorViewModel, IErrorChecker, AllErrorsViewModel, MainErrorsPlugin, RequestErrorRefreshMessage, or adding new error checks.

gum-tool-dialogs

447
from vchelaru/Gum

Reference guide for Gum's dialog/popup systems. Load this when working on dialog windows, DialogService, DialogWindow, DeleteOptionsWindow, dialog scrolling, dialog layout, or adding new dialog types.

gum-tool-delete-logic

447
from vchelaru/Gum

Reference guide for Gum's delete architecture. Load this when working on delete commands, IEditCommands delete methods, IDeleteLogic, DeleteLogic, DeleteOptionsWindow, HandleDeleteCommand, AskToDeleteState, AskToDeleteStateCategory, or DeleteSelection.