UDreamTreeView
A tree whose hierarchy is code, not an asset: a list whose rows carry an indent and a twisty.
Inherits from UDreamListViewBase.
Declared in Public/Controls/DreamTreeView.h.
A tree whose hierarchy is code, not an asset: a list whose rows carry an indent and a twisty.
A tree IS a list that indents, and this class is written to be exactly that much more than UDreamListView -- both derive from UDreamListViewBase, which owns the viewport, the scrolled column, the row template and the rows; this one adds a depth per item, a per-row indent, and a twisty that expands and collapses. FDreamTreeViewStyle is built the same way round: it carries a whole FDreamListStyle rather than restating its fields, so styling a project's lists styles its trees' rows too.
THE SOURCE IS FLAT, AND ON PURPOSE
Items (or ItemObjects) plus ItemDepths: the pre-order walk of the tree, with each item's depth beside it. That is the shape UUITreeView flattens ITS source into before it can lay a row out (Items + ItemDepths, rebuilt whenever expansion moves), and it is the only shape a control can take from a designer -- a nested source needs an interface on the item class, which is a contract a .dui author cannot sign and a details panel cannot draw.
What the flat form buys, beyond being authorable: the subtree of a row is exactly the run of rows that follow it at a greater depth, so collapsing is a skip rather than a graph walk, and no item ever needs to be asked who its children are.
Everything is expanded to begin with -- the authored list IS what you see -- and CollapsedItems is the set of exceptions. Selection stays an index into the SOURCE, so it survives a collapse.
OnGetItemChildren BUILDS THE FLAT SOURCE; IT DOES NOT REPLACE IT
UMG's TreeView asks each item for its children and walks the answer, and so does this one -- SetRootItems plus OnGetItemChildren (or IUITreeViewItem::GetTreeChildren, which this plugin's behaviour-side tree already speaks) flattens the hierarchy in pre-order into exactly the Items + ItemDepths pair above.
Flattening rather than walking lazily is the whole design, and it is what keeps every index in this control's API meaning something: SelectedIndex, ItemDepths, GetRowWidget, ScrollItemIntoView and CollapsedItems are all indices into the SOURCE, and a source that only materialises what is currently expanded would renumber itself every time a twisty moved. The walk therefore takes the WHOLE tree, including the children of collapsed nodes, and collapsing stays what it has always been here: a skip over a run of deeper rows at display time. A collapsed parent keeps its twisty because its children are still in the source to be counted.
The cost of that decision is stated rather than hidden: an unbounded or lazily-loaded hierarchy cannot be expressed this way, because the walk visits every node once. A tree that large wants the recycling stack (UUITreeView) instead, which is why that class stays.
Cycles are survivable: an item already visited is not visited again, so a graph produces a tree rather than a hang.
LEFT AND RIGHT ARE THE TREE'S
Up and down step through the rows that show, as a list's do. Left and right open and close, as STreeView::OnKeyDown has them: right on a folded parent unfolds it, right on an open one moves to its first child; left on an open parent folds it, left anywhere else moves to the parent. Where the key has nothing to do -- right on a leaf, left on a root that is already folded -- it still belongs to the tree and focus stays put, which is what STreeView does with the arrow keys. This library has one navigation road for the keyboard and the gamepad, so a D-pad gets the same answer; in UMG a D-pad goes round OnKeyDown and would leave the tree sideways instead. The twisty is not a navigation stop (UMG's expander arrow is not focusable either), so a press that follows a click on it starts from the row.
See UDreamListViewBase for the shape of the tree it builds, why it hosts the plain scroll view
rather than the recycling one, and how these controls sit beside the each language feature.
Properties
| Name | Type | Category | In the details panel | From Blueprint | Description |
|---|---|---|---|---|---|
Style | FDreamTreeViewStyle | Tree View | yes | GetStyle / SetStyle | This instance's own look. The project sheet wins while StyleSource says so AND a sheet actually exists; with no sheet in the project this IS the look in effect -- which is why it stays editable instead of being gated on the enum. |
ItemDepths | TArray<int32> | Tree View | yes | GetItemDepths / SetItemDepths | One depth per item, parallel to Items / ItemObjects: 0 is a root, 1 is a child of the nearest preceding 0, and so on. Missing entries read as 0, so a tree given no depths at all is simply a flat list -- which is the right thing for it to be. |
CollapsedItems | TSet<int32> | Tree View | yes | GetCollapsedItems / SetCollapsedItems | The collapsed items, by source index. An exception set rather than an expanded set because a tree that opens showing nothing is a tree nobody can see they authored. |
CollapsedItemObjects | TSet<TObjectPtr<UObject> > | Tree View | - | read only | The same exceptions, by IDENTITY, for a source that has ItemObjects. |
TwistyTemplateNode | TObjectPtr<UDreamWidget> | Tree View | - | read only | The template's twisty. Every row has a copy of it, found by display name. |
TwistyGlyphTemplateNode | TObjectPtr<UDreamWidget> | Tree View | - | read only | The glyph inside it -- the stand-in for a state whose brush holds no image. |
RootItems | TArray<TObjectPtr<UObject> > | Tree View | yes | read / write | The top of the hierarchy, for a tree that is authored as a GRAPH rather than as a flat array. |
OnGetItemChildren | FDreamTreeGetItemChildren | Tree View | - | read / write | Who an item's children are. UMG's OnGetItemChildren, with its signature and its job. |
Functions
| Function | Kind | Description |
|---|---|---|
void CollapseAll() | callable | Collapse All |
void ExpandAll() | callable | Expand All |
TSet<int32> GetCollapsedItems() | pure | Get Collapsed Items |
TArray<int32> GetItemDepths() | pure | Get Item Depths |
float GetRowIndent(int32 InItemIndex) | pure | How far in this item's row starts, in pixels: its depth times the style's IndentPerLevel. |
FDreamTreeViewStyle GetStyle() | pure | By value, not by reference: a UFUNCTION return has to be a value, and a style is a small struct. |
bool IsItemExpanded(int32 InItemIndex) | pure | Is Item Expanded |
bool ItemHasChildren(int32 InItemIndex) | pure | True when the next item is deeper -- which, in a pre-order flat list, is what a parent is. |
void RefreshTree() | callable | Walk the roots again and rebuild the flat source from what the children provider says now. |
void SetCollapsedItems(TSet<int32> InCollapsed) | callable | Replace the whole fold state at once and re-derive the visible rows. |
void SetItemDepths(TArray<int32> InDepths) | callable | Replace the depths alone, for a source that did not move. |
void SetItemExpanded(int32 InItemIndex, bool bInExpanded) | callable | Moves the twisty, rebuilds the visible rows, then says so. A no-op if it was already there. |
void SetItemExpansion(int32 InItemIndex, bool bInExpanded) | callable | UMG's name for SetItemExpanded, which is the one this control was written with. |
void SetItemsWithDepths(TArray<FText> InItems, TArray<int32> InDepths) | callable | Replace the source and its depths together, which is the only way they are ever coherent. |
void SetRootItems(TArray<UObject*> InRootItems) | callable | Replace the roots and re-walk. The hierarchical counterpart of SetItemsWithDepths. |
void SetStyle(FDreamTreeViewStyle InStyle) | callable | Replace the whole look and re-push it. The indent and the twisty are style, so rows rebuild. |
void ToggleItemExpansion(int32 InItemIndex) | callable | Toggle Item Expansion |
Events
| Event | Signature | Description |
|---|---|---|
OnItemExpansionChanged | void DreamTreeExpansionChangedEvent__DelegateSignature(int32 ItemIndex, bool bExpanded) | Per depth level, in whichever direction the twisty was clicked. |
Compared with UMG
Every Blueprint-facing member of the UMG class, and where it went. adopt: same name, same meaning. map: here under another name or on another type. reject: deliberately absent, with the reason.
| UMG | Member | Status | Here | Note |
|---|---|---|---|---|
UTreeView | SetItemExpansion | adopt | SetItemExpansion | A one-line forward to SetItemExpanded, which is the name this control was written with and which every existing caller uses. By source index, so it survives a collapse above it. The player reaches it too: Left and Right on a row open, close and walk to parent or first child as STreeView::OnKeyDown does with the arrow keys -- and, this library having one navigation road, a D-pad does the same, where in UMG it would leave the tree sideways. |
UTreeView | ExpandAll | adopt | ExpandAll | |
UTreeView | CollapseAll | adopt | CollapseAll | |
UTreeView | BP_OnGetItemChildren | map | OnGetItemChildren | FDreamTreeGetItemChildren, with UMG's signature. Single-cast like UMG's: this one is a QUESTION, and two answers to a question is an ambiguity nothing can resolve. Unbound, the walk falls back to IUITreeViewItem::GetTreeChildren. |
UTreeView | BP_OnItemExpansionChanged | map | OnItemExpansionChanged | Carries the source index and the new state. Fired after the visible rows have been rebuilt, so a handler counting rows sees the tree the user just made. |