Skip to content

Object Model

phroun edited this page Aug 22, 2026 · 1 revision

Everything the display holds for a connection is an object: it has an id, a type, properties, and a place in a tree.

p=new panel layout=vbox children={
	b=new button caption="OK"
	l=new label caption="Ready"
}

Three objects, one containing the other two.


Two kinds of object

Trinkets are things placed on screen in their own right — a button, a panel, a list, a window. They take the common properties, they have a size and a position, and they can hold focus.

Virtual types are parts of another object rather than objects placed on their own: an item in a list, a column or cell in a tree, a menuitem, a dockentry, an option. They carry no trinket identity, so they take none of the common properties:

new listview children={ new item caption="a" bg=blue }
  ->  property "bg" is not supported by this type

Colour the list, not the row. The split is visible in describe, which marks virtual types as such, and in this wiki's type index.

A third thing exists that is neither: collection, which is packaging. A parent that understands one adopts its members as though they had been written out directly. See TreeView.

One id space

Trinkets and virtual objects draw ids from the same allocator, so an id identifies exactly one object whatever kind it is:

lv=new listview children={ i=new item caption="row" }
iid=lv.i
  ->  lv = 4, iid = 5

There is no separate numbering for items, and no way for a virtual id to collide with a trinket's. A client can keep one table of everything it built.

Three ways to address an object

a correlation key set p bg=blue
a dotted path set p.b caption="Go"
the raw id set 2 caption="Go"

The raw id works anywhere a key does, which is what makes the two-batch patterns practical: read the numbers out of a reply, keep them, and address objects by number afterwards without naming anything.

Paths name every level and do not search — see Protocol Overview.

Identity is not name

name is a debug and tooling label. It is not how anything is addressed, and two objects may share one harmlessly.

Identity is the id: the number the display assigned, surfaced through a correlation key. Two keys may not share an object's place in your table, but two objects may certainly share a name.

Containment

An object's children are what it holds. What a parent accepts is its own business, and refusing is the normal case:

new button children={ new label caption="x" }
  ->  this type does not accept children

new listview children={ new label caption="x" }
  ->  listview: children must be items, got *trinkets.Label

Three patterns recur:

  • A container takes many childrenPanel, and the layout decides how they are arranged.
  • A window takes exactly one content trinket, so anything with several parts goes in a panel.
  • Some parents sort children by type. An MDIPane treats a window child as a hosted window and any other trinket as its background, regardless of the order they are written.

set … children={…} appends; it does not replace. That is how a list grows a row or a pane spawns a window.

Top-level objects

Not everything is inside something. A few types are built at the top level and adopted by the display as the connection's own chrome:

Window with main your primary window
MenuBar merged into the desktop's bar
StatusBar your status line
MessageBox a modal dialog

They are not children of anything and must not be placed inside a window's children. Application is top-level too, but exists before the connection does — you never build it.

Lifetime

destroy removes an object and takes its correlation key with it:

destroy lv
destroy lv
  ->  destroy: unknown key path "lv"

Destroying a container does not release its descendants' keys. The whole subtree leaves the display with the parent, but every key you had surfaced below it keeps working:

p=new panel layout=vbox children={ b=new button caption="OK" }
bid=p.b
destroy p
set bid caption="after"     # succeeds
set p.b caption="after"     # succeeds

None of those writes error, and none of them are visible: they are reaching objects that no longer have anywhere to be drawn. Only p's own key went away.

So a client that destroys a subtree should drop its own record of what was inside it. The display will not tell you those handles are spent, because as far as the connection is concerned the objects are still there.

A virtual child can be destroyed on its own, which is the ordinary way to remove one row or one entry:

lv=new listview children={
	a=new item caption="Alpha"
	b=new item caption="Beta"
}
destroy lv.a

See also

Protocol Overview — the verbs and how keys work · Common Properties — what every trinket accepts · Properties and Values · Events

Clone this wiki locally