Skip to content

Concepts ​

The vocabulary the deep-dives use, defined once. Each term is grounded in at least one surveyed implementation, so a definition can be checked rather than argued about.

Last reviewed: August 19, 2026


Structure ​

Subject ​

The value being edited. A UObject in Unreal, an Object in Godot, one or more objects in WinForms, a &mut dyn PartialReflect in bevy, and — in Qt's property browser — nothing at all, because the model is the subject.

Model node vs presented node ​

A model node is an element of whatever structure the library keeps; a presented node is one row on screen. The corpus's cleanest statement of the difference is Qt's: a QtProperty is a model node, a QtBrowserItem is one occurrence of it in one browser, and items(property) returns a list because a property may be shown several times (qtpropertybrowser.h:215).

Three of five subjects collapse the two: Godot's presented node is a widget, bevy's is a stack frame, and WinForms' GridEntry is both at once.

Node address ​

Whatever the library uses to say "this node" across time. Five distinct answers appear:

AddressSubjectSurvives a rebuild by
pointer to a model nodeQtthere being no rebuild
(object, property path string)Godotre-lookup; the panel restores selection and focus by hand
entry object, re-matched structurallyWinFormsEqualsIgnoreParent comparison of freshly built children
handle (IPropertyHandle)Unrealthe handle itself, with IsValidHandle() to ask
hashed structural path (egui::Id)bevybeing recomputed identically next frame
mutable cursor + propertyPathUnitythe path string; the cursor is transient by design
field path + synthetic row keyrjsfthe key travelling with the element, not the index
remote objectId lease + path()DevToolsrefetching; the lease may be revoked entirely

Descent decision ​

The predicate that answers "is this value a leaf, or a subtree?". Type-driven (Godot: Variant type + hint), converter-driven (WinForms: TypeConverter.GetPropertiesSupported), discriminant-driven (bevy: ReflectMut::Struct vs Opaque), manager-driven (Qt: hasValue()), or — in D — a compile-time static if over isAggregateType, demonstrated in reflect-descent.d.

Materialisation ​

When the children of an expandable node come into existence. Eager (Qt builds the whole browser-item tree at insert), lazy on expand (WinForms GridEntry.Children; Godot's sub-inspector, which checks the fold state first), or per frame while visible (bevy, where a closed region simply does not execute).

Cut ​

Stopping a descent that would not terminate. A visited set over values (Unity, keyed by managedReferenceId), over $refs (rjsf), over types (the D compile-time walk must, or the build fails), a structural ban (Qt refuses the insert), a depth cap (nobody, except Godot's purely cosmetic colour level), or nothing at all plus the observation that recursion is user-driven (Godot, WinForms, DevTools).

Driven vs automatic walk ​

The distinction that decides whether a guard is needed at all. A walk is driven when a human expands one level (DevTools, Godot) or when the data itself is finite (array items, oneOf branches); it is automatic when the code descends on its own — expand-all (Unity) or schema resolution (rjsf). Both subjects that carry a visited set carry it in an automatic walk, and neither guards painting.

Cut with an affordance ​

Rendering the cut as a row the reader can act on rather than as silence. rjsf's CyclicSchemaField is a placeholder with an Expand button that opens exactly one more level; DevTools's "show all" button past 200 children is the same shape. erased-descent.d reproduces it in D.

Erasure boundary ​

Where a statically-dispatched descent becomes a runtime call. The Rust derive family puts one in the child walk (&mut dyn EguiProbe), which is why a recursive type is ordinary there and a build error in a D CTFE walk. Moving the boundary is the lever; the cost is a virtual call and an allocation per open node.


Metadata ​

Metadata channel ​

Where per-field facts other than the type come from: .NET attributes read through TypeDescriptor (WinForms), a PropertyInfo hint/hint-string/usage triple (Godot), a string map (UPROPERTY(meta=…), Unreal), a type registry of arbitrary option objects (bevy), or D UDAs — the only compile-time member of the family (uda-metadata.d).

Static vs value-dependent metadata ​

A label, a group and a range are functions of the type; "show this field only when that one is gradient" is a function of the value. Runtime channels blur the two (Godot's _validate_property rewrites usage flags per rebuild); a compile-time channel cannot, so a value-dependent condition has to be carried as data and evaluated per frame.

Provenance ​

Not just the value, but why it is that value. VS Code's row carries scopeValue, defaultValue, defaultValueSource, isConfigured, hasPolicyValue and per-language overrides — the difference between "unset" and "set to the same thing as the default", and between "you may change this" and "policy forbids it". No developer-inspector subject models it.

Mirror ​

A serialized copy of the subject that the editor edits instead of the object, pulled before drawing and pushed at a commit point (Unity: Update() / ApplyModifiedProperties()). The push is where undo attaches.

Descriptor ​

A runtime object standing for one field: PropertyDescriptor (WinForms), PropertyInfo (Godot), FProperty (Unreal). D has no runtime equivalent; the nearest thing is a value computed in CTFE, which is a different object with a different lifetime.


Editing ​

Editor dispatch ​

Mapping a value to the widget that edits it. Factory bound to a manager (Qt), registry keyed by concrete type (bevy's InspectorEguiImpl), converter plus UITypeEditor, overridable per property (WinForms), plugin chain with last-registered-wins (Godot's EditorInspectorPlugin).

Mutation ownership ​

Who writes the value. Direct in place (bevy: &mut), through the model's setter (Qt: the factory calls manager->setValue), through a transaction (Godot: EditorUndoRedoManager; WinForms: IDesignerHost.CreateTransaction), or through a mediating handle that owns the protocol (Unreal: SetValue performs the transaction and notification itself).

Transient vs committed edit ​

A slider mid-drag and a slider released are different events. Unreal separates them at the API (SetValue flags plus NotifyFinishedChangingProperties); Godot has a changing flag whose meaning is narrower than it looks — the value is written on every keystroke, and the flag only suppresses the rebuild that would destroy the widget being typed into (editor_inspector.cpp:5829); Godot's undo merging (UndoRedo::MERGE_ENDS) recovers the drag case for undo purposes only. Qt and bevy have no such concept.

Commit point ​

When typed text becomes a value. Per keystroke (Qt, bevy) or on commit with conversion and failure handling (WinForms: ConvertTextToValue at commit, a modal error dialog, and the reader's text preserved unless they choose to revert).

Write-back through an immutable parent ​

Editing size.width when size is a value type. WinForms models it explicitly: children of a GetCreateInstanceSupported value become ImmutablePropertyDescriptorGridEntry, and [NotifyParentProperty] makes the change walk up to the owner that can be re-assigned. Getting this wrong is silent: the edit appears to happen and does not reach the object.


Presentation ​

Mixed value ​

What a row shows when the selected objects disagree. Blank (WinForms, driven by allEqual == false), a per-object list the customizer may render however it likes (Unreal's GetPerObjectValues), or the first object's value with no indication at all (Godot, multi_node_edit.cpp:144).

Restriction ​

A value that is present but unavailable, with a reason. Unreal has IsRestricted/IsHidden/IsDisabled with OutReasons; bevy reaches the same effect for variants by disabling unconstructable ones and explaining on hover. Distinct from hiding the whole row.

Constructability ​

Whether a variant or element can be created blank. A registry question at runtime (ReflectDefault, bevy); a compile-time one when a derive macro generates the construction (derive crates — a field without Default fails the build); and in D almost a non-question, since every type has .init, the residual case being an author's @disable this() — measured in sumtype-variants.d.

Variant migration ​

Carrying data across a change of variant instead of discarding it. Only rjsf attempts it: sanitizeDataForNewSchema keeps the keys whose name and resolved type match, recursing into objects and arrays. Every type-driven subject discards.

Fetch policy ​

Bounding a walk before it reaches the renderer, by limiting what is even retrieved: DevTools's 200 visible children, 100-element buckets and recursive [from … to] ranges. Distinct from virtualization (render fewer of the rows you have) and from pagination (show a window of a known list).

Virtualization vs pagination ​

Rendering only visible rows while the model holds all of them (WinForms's _visibleRows; sparkles:ui's viewSlice), versus showing a fixed-size window of a collection with page controls (Godot's max_array_dictionary_items_per_page). Pagination is what a widget-per-row architecture reaches for when virtualization is unavailable.