Gum Editor Selection System Reference

SkillDev tools

Gum editor selection, click/drag, marquee, input handlers (move/resize/rotate/polygon points), IsActive flag, locked instances, SelectionManager coordination, selection event cascade (plugin events, forced default state, tree view sync).

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Gum Editor Selection System Reference skill

What this skill tells your AI

The instructions your AI receives, as published by vchelaru/gum in .claude/skills/gum-tool-selection/SKILL.md and read by ahel’s review.

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.

This migrated in two parts: MoveInputHandler/ResizeInputHandler/RotationInputHandler/InputHandlerBase, EditorContext, SelectionManager, and RectangleSelector now live under the headless Tools/Gum.Presentation/...; PolygonPointInputHandler and LockedSelectionVisual are still under Tool/EditorTabPlugin_XNA/Editors/....

Input Handlers

Base class: Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/Handlers/InputHandlerBase.cs

Each handler represents one interaction mode. Concrete handlers:

HandlerFileResponsibility
MoveInputHandlerTools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/Handlers/MoveInputHandler.csDrag-to-move selected instance(s)
ResizeInputHandlerTools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/Handlers/ResizeInputHandler.csResize handle dragging
RotationInputHandlerTools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/Handlers/RotationInputHandler.csRotation handle dragging
PolygonPointInputHandlerTool/EditorTabPlugin_XNA/Editors/Handlers/PolygonPointInputHandler.csPolygon 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: Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/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 Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/EditorContext.cs) returns true when the selected instance is locked.

Where Locking Is Enforced

LocationFileWhat It Prevents
InputHandlerBase.HandlePush()InputHandlerBase.csBase lock check; handlers that don't override inherit this
PolygonPointInputHandler.HandlePush()PolygonPointInputHandler.csOverrides base; manually checks lock before allowing vert select/add
PolygonPointInputHandler.TryHandleDelete()PolygonPointInputHandler.csPrevents DEL key from deleting verts
PolygonPointInputHandler.UpdateHover()PolygonPointInputHandler.csHides the "add point" sprite on polygon edges
ElementCommands.MoveSelectedObjectsBy()Gum/ToolCommands/ElementCommands.csSkips locked instances in multi-selection moves
ResizeInputHandler.ApplySizeChange()ResizeInputHandler.csSkips locked instances during resize
MoveInputHandler.ApplyAxisLockIfNeeded()MoveInputHandler.csSkips locked instances during axis-lock correction
MoveInputHandler.ApplyAxisLockToSelectedState()MoveInputHandler.csSkips locked instances when writing axis-lock to state
MoveInputHandler.SnapSelectedToUnitValues()MoveInputHandler.csSkips locked instances during snap-to-unit
RectangleSelector.GetElementsInRectangle()RectangleSelector.csExcludes locked instances from marquee results
SelectionManager.ReverseLoopToFindIpso()SelectionManager.csPrevents click-selection of locked instances on canvas
ListBoxDisplay (variable grid)WpfDataUi/Controls/ListBoxDisplay.xaml.csDisables 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

FilePurpose
Gum/ToolStates/SelectedState.csHandleSelectedInstances, PerformAfterSelectInstanceLogic, HandleStateSaveSelected
Gum/Plugins/PluginManager.csInstanceSelected, ReactToStateSaveSelected event dispatch
Gum/Plugins/InternalPlugins/TreeView/MainTreeViewPlugin.csTree view sync with SuppressCallAfterClickSelect
Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.csSelect methods, CallAfterClickSelect, both suppression flags
Gum/Plugins/InternalPlugins/VariableGrid/MainVariableGridPlugin.cs_stateJustRefreshedGrid double-refresh guard

Key Files Summary

FilePurpose
Tools/Gum.Presentation/Wireframe/SelectionManager.csMain coordinator; manages IsOverBody, routes events to handlers, passes isHandlerActive to rectangle selector
Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/RectangleSelector.csMarquee selection; activation gated on isHandlerActive and IsOverBody
Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/Handlers/InputHandlerBase.csBase class; provides default HandlePush with lock check
Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/Handlers/MoveInputHandler.csMove gesture; also handles axis lock and snap-to-unit for multi-selection
Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/Handlers/ResizeInputHandler.csResize handle gestures
Tool/EditorTabPlugin_XNA/Editors/Handlers/PolygonPointInputHandler.csPolygon vertex editing; overrides HandlePush (must manage lock manually)
Tools/Gum.Presentation/Plugins/InternalPlugins/EditorTab/Editors/EditorContext.csProvides IsSelectionLocked() helper used throughout handlers
Tool/EditorTabPlugin_XNA/Editors/Visuals/LockedSelectionVisual.csDashed bounding outline for locked selected instances; display-only, no interaction
GumDataTypes/InstanceSave.csLocked property definition
Tools/Gum.Presentation/ToolCommands/ElementCommands.csMoveSelectedObjectsBy(); skips locked instances in multi-move
WpfDataUi/Controls/ListBoxDisplay.xaml.csVariable grid list control; respects IsReadOnly (driven by Locked)

Signals

GitHub stars
620
Forks
80
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
gum-tool-selection
Source
github.com/vchelaru/gum