Skip to content

TreeView

phroun edited this page Aug 22, 2026 · 4 revisions

Wire name treeview

Rows that nest, optionally across several columns, with sorting and in-place editing. A ListView is the flat single-column case; where you need indentation, a header, or a second column, this is the trinket.

t=new treeview caption="Name" showheader treelines children={
	new column id=size caption="Size" width=10 align=right sortable numeric
	new item caption="Report.txt"
	new item caption="Folder" expanded children={
		new item caption="inner.txt"
	}
}

Properties

Property Type Default Meaning
caption string Header caption over the key (tree) column.
descending flag false Sort direction indicator points down.
editable flag false The key (tree) column joins the row editor (edits the item caption).
fit_width flag true Squeeze columns to the width (no horizontal scrolling).
fixed_left int 0 Visible columns pinned outside horizontal scrolling, from the left.
fixed_right int 0 Visible columns pinned outside horizontal scrolling, from the right.
indent_width int Indent width per tree level.
key_width int 20 Key column width in text cells (scroll mode).
ledger flag false Alternate non-selected rows in the ledger colors.
selected int -1 Selected visible-row index.
showheader flag false Show the column header row.
showkey flag true Show the key (tree) column first.
sorted flag false Show the sort indicator.
sortedby int -1 Sort column: -1 = the key column, else a column index.
treelines flag false Connector lines in the indent space; leaf items get a glyph too.

Plus the common properties.

Events

activate — An item was activated — double-clicked, or Enter on the current item.

Field Type Meaning
trinket uint The tree's object ID.
item uint The activated item.
selected int Row index of the item among the visible rows.

change — The current item moved.

Field Type Meaning
trinket uint The tree's object ID.
item uint The item now current.
selected int Row index of the current item among the visible rows.

edit — An in-place cell edit was committed.

Field Type Meaning
trinket uint The tree's object ID.
item uint The edited item.
column int Index of the edited column.
value string The committed cell text.

expand — A branch opened or closed.

Field Type Meaning
trinket uint The tree's object ID.
item uint The branch item.
expanded flag Set when the branch opened, unset when it closed.

sort — The sort changed — a column header was used, or sorting was cleared.

Field Type Meaning
trinket uint The tree's object ID.
sorted flag Set while a sort is in force.
sortedby int Index of the column sorted on.
descending flag Set when the sort runs high to low.

The three parts

A treeview is built from three virtual types, and which one a thing is decides where it attaches.

Type Attaches to Is
item the treeview, or another item a row
column the treeview a column of the grid
cell a column one column's value for one row

Items nest; columns do not. Cells go under the column, never under the treeview — a cell names the row it belongs to by ID rather than sitting inside it:

new treeview children={ new cell item=1 value="x" }
  ->  treeview: children must be items or columns, got *trinkets.wireCell

There is no fourth part. The tree column — the one holding the captions and the indentation — is not a column object; it is the treeview itself, and caption, key_width, showkey and editable are its properties.

Items

Rows are children of the shared virtual item type, the same one ListView and ComboBox use. Here it may nest:

new treeview children={
	new item caption="Fruit" expanded children={
		new item caption="Apple"
		new item caption="Pear"
	}
	new item caption="Roots"
}

expanded defaults to false, so a branch built without it starts closed and its children are not rows yet. The tree above holds four items either way, but drop the expanded and it has two visible rows instead of four.

Items are addressable after they are built. A correlation key on a nested item reaches it by path:

tv=new treeview children={
	a=new item caption="Report.txt"
	b=new item caption="Folder" children={
		c=new item caption="inner.txt"
	}
}
set tv.b expanded
set tv.b.c caption="renamed.txt"
destroy tv.a

set on a live item takes effect immediately, including new children:

set tv.b children={ new item caption="late" }

appends to a branch that is already on screen.

Columns

A column is a description of a slice of the grid, not a container of values:

new treeview showheader children={
	new column id=size caption="Size" width=10 align=right sortable numeric
	new column id=kind caption="Kind" width=12
	new item caption="Report.txt"
}
Property Type Default Meaning
align left | center | right left Cell text alignment.
caption string Header caption.
editable flag false Cells in this column can be edited in place (Enter opens the row editor).
enum int Wire ID of a collection of option objects; the cell editor becomes a choice box.
enum_store key | value value What a chosen option stores in the data field (cells always DISPLAY the option value).
hidden flag false Column is not displayed.
id word Stable key cell values are stored under.
max_width int 0 Maximum width in text cells (0 = unbounded).
min_width int 3 Minimum width in text cells.
numeric flag false Sort by each cell's numeric equivalent (parsed once per value).
optional flag true Column appears in the [=] show/hide chooser.
resizable flag true Header divider drag-resizes this column.
sortable flag false Header click requests a sort on this column.
sortproxy int -1 Column index whose values actually sort when this column is chosen (-1 = itself).
width int 8 Width in text cells.

id is what cells are stored under, so give every column one. Values live on the item under that key, not on the column, which has a consequence worth knowing: two columns declared with the same id build without an error and then show the same value in both, since one key is all there is between them. A cell sent to either fills both.

Columns are resizable and optional by default, unlike most flags on the wire. A column built with no flags at all can already be dragged wider at its divider and hidden from the [=] chooser. Say !resizable or !optional to take that back.

showheader is what puts the header row on screen; without it the captions exist but are never drawn, and there is nothing to click to sort.

Cell values

Values are column-major: each column owns its values, keyed by item.

Within one build script the item IDs do not exist yet, so filling a grid takes two batches. Build the shape, read the IDs out of the reply, then send the values:

tv=new treeview caption="Name" showheader children={
	sizec=new column id=size caption="Size" width=10 align=right
	a=new item caption="Report.txt"
	b=new item caption="Folder" expanded children={
		c=new item caption="inner.txt"
	}
}

A bare reference statement surfaces an object's wire ID without changing anything, which is how the nested ones are reached:

aid=tv.a
bid=tv.b
cid=tv.b.c

The reply carries aid, bid and cid as numbers, and the values go in against them:

set tv.sizec children={
	new cell item=741 value="12 KB"
	new cell item=743 value="--"
	new cell item=744 value="1 KB"
}
Property Type Default Meaning
item int Wire ID of the item this value belongs to.
value string Cell text for this column and item.

A cell naming an item that does not exist is accepted and dropped — no error. Since the IDs come from a reply, a wrong one means the batches went out of step, and the symptom is a blank cell rather than a message.

Collections

A collection is packaging: a parent that understands one adopts its members as though they had been written out directly.

Property Type Default Meaning
of word Advisory member kind (columns, cells, ...).
new treeview children={
	new collection of=columns children={
		new column id=a caption="A" width=5
		new column id=b caption="B" width=6
	}
	new item caption="row"
}

builds the same two columns as writing them inline. of is advisory — it documents what is inside and is not checked against the members.

Collections earn their keep for the enum below, where the column has to point at a group of objects rather than contain them.

Sorting

sortable on a column makes its header clickable. The key column is always sortable and does not opt in.

A click cycles three states, and each one raises sort:

Click sorted descending
first set unset
second set set
third unset unset

sortedby is the column index, and -1 is the key column:

new treeview sorted sortedby=-1 children={ … }    # sorted by name

sort is a request, not a report of work done. The treeview re-orders its own rows and moves the indicator, and the event tells an application that holds the real data to re-order that too. An application that sorts server- side sends back set tv sorted sortedby=1 !descending to match.

numeric sorts a column by each value's numeric equivalent rather than as text, so 9 KB lands before 10 KB. sortproxy names a different column whose values do the sorting when this one is chosen — a formatted size displayed in one column, the byte count sorted in another.

Sorting is stable against edits: changing an item's caption or a cell's value while a sort is in force re-sorts the rows, and the selection stays on the item rather than on the row index.

In-place editing

editable on a column lets its cells be edited; editable on the treeview does the same for the key column, editing the item's caption.

Return opens the row editor on the current Enter-target column, and the target starts on the key column — so with both editable, Return edits the caption first. Left and Right walk the target across columns while the editor is closed.

A committed edit raises edit with the column index, and the key column reports column=-1 — the same convention sortedby uses.

Nothing is written to the item by the editor alone. edit is the request; the value the tree shows afterwards is whatever the application sends back as a cell.

Choice cells

A column can offer a fixed set of choices instead of free text. The options are a collection, and the column points at it:

kinds=new collection of=options children={
	new option key=png value="PNG image"
	new option key=txt value="Text"
}
tv=new treeview editable children={
	kindc=new column id=kind caption="Kind" editable
	a=new item caption="file"
}

An option is a stable key and the text shown for it:

Property Type Default Meaning
key word Stable option key.
value string Displayed option value.

enum takes the collection's numeric wire ID, not its correlation key, so this is a second batch as well — the number below is whatever the build reply gave for kinds:

set tv.kindc enum=738 enum_store=key

Writing enum=kinds is an error — enum: expected an integer. Correlation keys name objects in statement position; they do not resolve inside a property value.

enum_store decides what a chosen option puts in the data field: value (the default) stores the text that was shown, key stores the stable key behind it. Cells always display the value either way, so key is what you want whenever the display text might be translated or reworded.

Keys

Does
Up Down Move the current item.
Left Collapse the branch, or step out to its parent.
Right Expand the branch, or step into its first child.
S-Left S-Right The same, kept working while the plain arrows walk columns in an editable grid.
Plus Minus Expand / collapse the current branch.
* Expand everything.
Home End First / last row.
PageUp PageDown A screenful.
Space Activate; on a choice cell, open the picker.
Return Edit where a column is editable, otherwise activate.
Click Select. Double-click activates.
Header click Sort, or drag a divider to resize.

The sort commands and the column chooser (trinket_sort_ascending, trinket_sort_mode_next, trinket_chooser and their siblings) have no default key. They are reachable with the mouse, or through the header focus zone below, or by an application binding a key to them.

Tab moves into the header bar as a stop of its own before leaving the trinket: one stop for the whole bar, then drilling in cycles the column captions and the [=] chooser button, with Space sorting the caption under the cursor.

Two events that do not fire

Expand-all is silent. * opens every branch without raising expand for any of them. A client tracking expansion state by subscription will be out of step after it.

A wire-side change does not echo. set tv.b expanded opens the branch and raises nothing, because the client that sent it already knows. Events report what the user did.

Rows are not items

selected is a row index among the visible rows, so it counts the children of an open branch and skips those of a closed one. Collapsing a branch above the selection therefore changes the index of everything under it, without anything having been selected or deselected.

The events avoid the problem by carrying both: item is the identity, and selected is where it happens to be sitting. Track the item.

selected has ListView's ordering rule too — it indexes rows that must exist already, so it belongs after children, not before:

new treeview selected=1 children={new item caption="a"; new item caption="b"}
  ->  selected = 0

new treeview children={new item caption="a"; new item caption="b"} selected=1
  ->  selected = 1

No error either way.

Width

fit_width (the default) squeezes the columns into the space available and there is no horizontal scrolling. Turn it off and the columns keep the widths they were given, key_width applies to the tree column, and the grid scrolls sideways.

fixed_left and fixed_right pin that many columns outside the scroll, so the name stays put while the rest of the row moves.

Appearance

treelines draws connectors down the indent space and a glyph on each leaf. indent_width sets how far a level steps. ledger bands the non-selected rows in the scheme's ledger colors.

showkey can turn the tree column off entirely, which leaves a plain multi-column grid with no indentation — the treeview used as a table.

See also

ListView — the flat single-column case · ComboBox — a list collapsed into a field · Common Properties · Templates and Aliases

Clone this wiki locally