# Gum Tool Delete Logic

> Gum Delete Logic Reference

- Skill: `vchelaru/gum-tool-delete-logic` (Agent Skill)
- Install (CLI): `npx skillmds@latest add vchelaru/gum-tool-delete-logic`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vchelaru/gum-tool-delete-logic/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: vchelaru (https://skillmd.com/u/vchelaru)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vchelaru/gum-tool-delete-logic

---


# Gum Delete Logic Reference

## Two Delete Patterns

There are two distinct delete flows depending on the object type being deleted.

### Pattern 1 — AskTo* (states and categories)

Used for states and state categories. These types have blocking conditions that must be checked before any dialog is shown, so they have their own typed methods on `IEditCommands`.

**Flow**: `IEditCommands.AskTo*` → validate (behavior deps, plugin hooks, default-state check) → simple Yes/No dialog → undo lock → `IDeleteLogic.Remove*`

**Entry points**:
- `IEditCommands.AskToDeleteState(stateSave, stateContainer)`
- `IEditCommands.AskToDeleteStateCategory(category, container)`

**Why not DeleteOptionsWindow**: States and categories are in-memory only — no XML files, no child hierarchy — so plugins have nothing to contribute to the delete dialog.

### Pattern 2 — DeleteSelection (elements, behaviors, instances)

Used for screens, components, behaviors, and instances. All go through one shared entry point that dispatches based on what is currently selected.

**Flow**: `IEditCommands.DeleteSelection` → undo lock → `IDeleteLogic.HandleDeleteCommand` → `DoDeletingLogic` → `ShowDeleteDialog` (creates `DeleteOptionsWindow`) → `IDeleteLogic.Remove*`

**Entry point**: `IEditCommands.DeleteSelection()`

**Why DeleteOptionsWindow**: Plugins contribute runtime UI to this dialog (e.g. `DeleteObjectPlugin` adds "Delete XML file?" and "Delete children?" options via the `DeleteOptionsWindowShow` plugin event).

## Responsibility Split

| Class | Responsibility |
|-------|---------------|
| `IEditCommands` / `EditCommands` | All user-triggered deletes. Shows dialogs, acquires undo locks, then delegates to `IDeleteLogic`. Only entry point callers should use. |
| `IDeleteLogic` / `DeleteLogic` | Pure data mutation after confirmation. `Remove*` methods do not show dialogs. `HandleDeleteCommand` is the exception — it orchestrates the DeleteOptionsWindow flow and is only called from `EditCommands.DeleteSelection`. |

## Contributing an option to DeleteOptionsWindow

`DeleteOptionsWindowShow` and `DeleteConfirmed` are declared on `WpfPluginBase`, not `PluginBase` — a
plugin contributing a delete option must derive from it.

Both fire **once per batch** with the full `objectsToDelete` array, while `ElementDelete` fires **once
per element**. Their relative order is not stable either: `NotifyConfirmed` runs before removal in
`PerformConfirmedMixedTypeDelete` and after it in `DoDeletingLogic`'s elements-only branch. Keep both
handlers pure functions of the element rather than relying on ordering across them.

A checkbox cached in a plugin field leaks state across deletes: a cancelled delete never fires
`DeleteConfirmed`, and `DeleteOptionsWindow.CloseWithResultFalse` clears `MainStackPanel.Children`
without touching the plugin's field, so the next delete reads a stale checked value. Reset the field at
the top of `DeleteOptionsWindowShow`.

## Post-delete selection

After removing instances, selection must fall back to a surviving sibling → parent instance → owning element, or the editor goes blank. Both the single-instance (`PerformConfirmedSingleInstanceDelete`) and multi-instance (`PerformConfirmedMixedTypeDelete`) paths funnel through one helper, `SelectAfterInstanceRemoval`.

## Callers

All delete actions funnel through `IEditCommands`:
- **Delete key** → `HotkeyManager` → `IEditCommands.DeleteSelection`
- **Element tree right-click Delete** → `ElementTreeViewManager` → `IEditCommands.DeleteSelection`
- **State tree right-click Delete state** → `StateTreeViewRightClickService` → `IEditCommands.AskToDeleteState`
- **State tree right-click Delete category** → `StateTreeViewRightClickService` → `IEditCommands.AskToDeleteStateCategory`
- **Menu strip Remove > Element** → `MenuStripManager` → `IEditCommands.DeleteSelection`
- **Menu strip Remove > State/Category** → `MenuStripManager` → `IEditCommands.AskToDeleteState` / `AskToDeleteStateCategory`

Do not call `IDeleteLogic` methods directly from UI code — always go through `IEditCommands`.

## Testability

`DeleteLogic` (headless, `Tools/Gum.Presentation/Managers/DeleteLogic.cs`) delegates dialog display to the WPF-shell `IDeleteDialogService`, whose implementation `DeleteDialogService` (`Gum/Services/Dialogs/DeleteDialogService.cs`) creates the `DeleteOptionsWindow` and calls `ShowDialog()` — that class cannot be unit-tested directly. The `internal BuildDeleteDialogMessage(Array, List<InstanceSave>?)` method on `DeleteLogic` is the testable seam for asserting dialog message content (`InternalsVisibleTo("GumToolUnitTests")` is already configured).

## Key Files

| File | Purpose |
|------|---------|
| `Gum/Commands/IEditCommands.cs` | Interface with architecture overview comment |
| `Gum/Commands/EditCommands.cs` | Implementation; AskTo* dialog logic lives here |
| `Tools/Gum.Presentation/Managers/IDeleteLogic.cs` | Interface for pure data-mutation operations |
| `Tools/Gum.Presentation/Managers/DeleteLogic.cs` | Data mutation + delete-dialog orchestration via `IDeleteDialogService` |
| `Gum/Services/Dialogs/DeleteDialogService.cs` | WPF shell: creates/shows `DeleteOptionsWindow`, calls the concrete `PluginManager` |
| `Tools/Gum.Presentation/Logic/ReferenceTypes.cs` | `ElementReferences` class; `GetDeleteImpactDetails()` and `ExcludeContainersBeingDeleted()` used to build impact warnings in the delete dialog |
| `Tools/Gum.Presentation/Logic/ReferenceFinder.cs` | `GetReferencesToVariable()` — enumerates every instance-level assignment of a variable project-wide, including through the inheritance chain; used by `DeleteVariableService.GetIfCanDeleteVariable` to block variable deletes today |
| `Gum/Plugins/InternalPlugins/Delete/DeleteObjectPlugin.cs` | Contributes "Delete XML?" and "Delete children?" to DeleteOptionsWindow |
| `Gum/Plugins/InternalPlugins/StatePlugin/StateTreeViewRightClickService.cs` | State/category right-click menu; calls AskTo* methods |
| `Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.RightClick.cs` | Element tree right-click; calls DeleteSelection |
| `Gum/Managers/HotkeyManager.cs` | Delete key handler; calls DeleteSelection |

