Gum Tool Tree View Reference
SkillSearchGum's element tree (Screens/Components/Standard/Behaviors panel). Triggers: GumTreeView, GumTreeNode, ElementTreeViewManager, ElementTreeViewCreator, MainTreeViewPlugin, TreeIconRegistry, tree icons, node add/refresh/drag. For the '!' overlay see gum-tool-errors; for selection plumbing see gum-tool-selection.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Gum Tool Tree View Reference skill
What this skill tells your AI
The instructions your AI receives, as published by vchelaru/gum in .claude/skills/gum-tool-tree-view/SKILL.md and read by ahel’s review.
The left-hand panel listing Screens, Components, Standard Elements, Behaviors, and the instances
inside an open element. The model and logic are framework-neutral in Tool/TreeViewPlugin.Core
(net10.0, shared by both heads); each head supplies only the panel, through IElementTreeView.
File map
| File | Purpose |
|---|---|
Tool/TreeViewPlugin.Core/GumTreeNode.cs (+ GumTreeNodeCollection.cs) | The node model both trees bind to; implements ITreeNodeMutable |
Tool/TreeViewPlugin.Core/ElementTreeViewManager.cs (+ .RightClick.cs) | Builds, refreshes, searches and selects nodes; builds the right-click menu as ContextMenuItemViewModels |
Tool/TreeViewPlugin.Core/IElementTreeView.cs | The panel contract the manager talks to, its factory, and the external-drop event args |
Tool/TreeViewPlugin.Core/TreeSelection/TreeSelectionModel.cs | Selection state and rules (click, range, toggle, keyboard nav, drag start, pruning) both controls feed |
Tool/TreeViewPlugin.Core/TreeSelection/ (other files) | Click/range/key decision classes on neutral enums; TreeDropKind and TreeDropLogic |
Tool/TreeViewPlugin.Core/TreeIconCatalog.cs | Icon index → artwork path + theme color key, shared by both heads' registries |
Tool/TreeViewPlugin.Core/MainTreeViewPlugin.cs | Wires plugin events to RefreshUi(...) and error-indicator updates; loaded by both hosts |
Tool/TreeViewPlugin.Core/TreeViewStateService.cs, CollapseToggleService.cs | Expansion state: persisted across sessions, and the collapse-button toggle |
Gum/Plugins/InternalPlugins/TreeView/WpfElementTreeView.cs | WPF panel: GumTreeView, search box, flat results, collapse buttons, chip palette; WPF drag/cursor glue |
Gum/Controls/GumTreeView.cs (+ .DragDrop.cs) | WPF control: hit testing, expander clicks, drag start, drop adornment; delegates selection to TreeSelectionModel |
Gum/Themes/Frb.TreeView.xaml, Gum/Controls/TreeIconRegistry.cs, TreeNodeIcon.cs | WPF row template and icon drawing |
Tool/Gum.Avalonia/Plugins/TreeView/ | Avalonia panel (AvaloniaElementTreeView), row-list control (AvaloniaGumTreeView), palette, icons |
Tools/Gum.Presentation/Services/RefreshCoalescer.cs | Collapses N RequestRefresh() calls in one synchronous burst into a single IDispatcher-posted refresh |
The states tree (center-top "States" tab) shares StateTreePluginBase, StateTreeRightClickService
and StateTreeKeyboardHandler in Tools/Gum.Presentation/Plugins/InternalPlugins/StatePlugin/;
MainStatePlugin (WPF) and AvaloniaStatePlugin only build their tree control.
ElementTreeViewManager and its RightClick partial speak ITreeNode/ITreeNodeMutable, delegating
to headless twins in Tools/Gum.Presentation/Managers/ (TreeNodeImageLogic, the TreeNode*Extensions
families, TreeNodeExpansionPaths). Prefer adding logic there over growing the manager.
Selection is on the model, not the container
TreeView enforces a single selected item and clears the previous one on every change, so
TreeViewItem.IsSelected is deliberately never set. TreeSelectionModel tracks the selection and the
row visuals bind to GumTreeNode.IsSelected. The Avalonia tree goes further and is a flat,
virtualized row list rather than a TreeView. Consequences:
- Keyboard navigation is
TreeSelectionModel.HandleKeyDown, called from each control's key handler. - A change to selection behavior goes in
TreeSelectionModel(with a test), never in one head's control. IsExpandedis ordinary two-way bound state, so expansion survives a rebuild without being captured and replayed.
Icons
TreeIconRegistry maps an index (the shared TreeNodeImageIndices constants, produced by the
headless TreeNodeImageLogic) to a pack URI plus a theme color key; TreeNodeIcon renders the pair.
- Source PNGs must be white-on-transparent, alpha carrying the shading. Tinting fills a shape with
the theme brush and uses the artwork as an
OpacityMask, so a colored source multiplies wrong. - Adding an icon is a constant in
TreeNodeImageIndicesplus an entry inTreeIconRegistry— in any position. The numbering is not tied to load order. - Icons re-tint on theme change via
TreeIconRegistry.NotifyThemeChanged(); nothing is regenerated.
Refresh model
RefreshUi() is diff-based — existing nodes are reused and only differing ImageIndex/position/Tag/
Text are written. Replacing nodes wholesale would drop selection and scroll position.
Tag distinguishes node kinds: folder/container nodes have Tag == null; element nodes carry an
ElementSave/BehaviorSave; instance nodes an InstanceSave.
MainTreeViewPlugin.HandleElementImported requests a refresh through a RefreshCoalescer rather than
calling RefreshUi() directly, so importing N elements in one batch (Forms theme, .gumx import)
produces one refresh instead of N.
Gotchas
- Reordering within one collection must be remove-then-insert.
GumTreeNodeCollectionthrows if a node is inserted into the collection it already belongs to, because detaching first would shift the index the caller computed. Reparenting across collections is a plain add. - Drag payloads travel in
TreeDragPayload, not on the drag data. Gum's*Savetypes aren't[Serializable], so anything put on a WPF data object comes back null; the data carries only a marker format (TreeDragPayload.DataFormatin WPF,AvaloniaDragFormats.TreeNodesin Avalonia). Both heads' canvas drop readers and search-result lists use the same static. ITreeNode.FullPathis backslash-separated.CopyPasteLogicslices a"Components\\"prefix off it.- Persisted expansion state is forward-slash-joined node
Textpaths (TreeNodeExpansionPaths). Changing either the separator or the use ofTextsilently discards every user's saved state. - Virtualization is off (the WPF
TreeViewdefault). That is what makesGumTreeView.ContainerFor/EnsureVisiblereliable — turning it on would break container lookup for off-screen nodes. GumTreeView.EnsureVisibledefers to aLoadeddispatcher callback, since a newly-expanded ancestor's child has no container until layout runs.- The Avalonia row (
TreeRowView) is an indent column plus a highlightBorderholding the expander, icon and text, stretched to the row's right edge, and the rows themselves span the viewport (ItemsControl.MinWidthfollowsScrollViewer.Viewport), matching the WPFRowBorder. Hover/selection colors go on the highlight, never on the row. A press on the expander callsAvaloniaGumTreeView.NotifyExpanderPressed()so the matching release neither selects nor opens the context menu (#4694); keep that if the expander is reworked.
Signals
- GitHub stars
- 620
- Forks
- 80
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
gum-tool-tree-view- Source
- github.com/vchelaru/gum