Skip to content

toolkit: make NativeControl the single native-control currency; Surface publishes it - #440

Merged
tannevaled merged 1 commit into
mainfrom
feat/native-descriptor
Aug 31, 2026
Merged

toolkit: make NativeControl the single native-control currency; Surface publishes it#440
tannevaled merged 1 commit into
mainfrom
feat/native-descriptor

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Follow-up to #438 (Native), driven by the news reader's real integration: its root is a self-rendering `toolkit.Surface`, which has no walkable widget tree, so `WalkNative` alone cannot reach its controls. This makes a flat descriptor the single currency both a widget tree and a Surface produce.

What changes

  • `WalkNative` now returns `[]NativeControl` — a descriptor carrying kind, key, geometry, value, and on-change/on-activate/on-claim callbacks — instead of a `*Native` placement. A `Native` widget adapts to one through `(*Native).control`, wiring the value and callbacks to its observables (and synthesising a stable per-widget Key when the app set none).
  • `Surface` gains a `Controls func() []NativeControl` field and a `NativeControls()` method — the exact parallel to `Elements`/`Damage`. A Surface-based app publishes its native controls the same way it already publishes its accessibility. A backend type-asserts `interface{ NativeControls() []NativeControl }` on its root, or falls back to `WalkNative`.

Why

This is the immediate-mode-friendly shape the reader needs: it describes its controls each frame; the retained OS controls live in the backend, keyed. The consumer — go-widgets/window's cocoa `syncNative`, reworked to reconcile descriptors — lands alongside (both the widget-tree and the Surface-provider paths are proven by on-device live tests).

100% package coverage retained; full suite green.

…ce publishes it

WalkNative now returns []NativeControl — a flat descriptor (kind, key, geometry,
value, and on-change/on-activate/on-claim callbacks) — instead of a *Native
placement. A Native widget adapts to one through (*Native).control, wiring the
value and callbacks to its observables, so a host speaks one shape whether the
app is a widget tree or a self-rendering Surface.

Surface gains a Controls func() []NativeControl field and a NativeControls()
method, the exact parallel to Elements/Damage: a Surface-based app (which has no
walkable widget tree) publishes its native controls the same way it publishes its
accessibility. A host backend type-asserts interface{ NativeControls() []NativeControl }
on its root, or falls back to WalkNative, and reconciles the same descriptors.

This is the immediate-mode-friendly shape the reader needs: it describes its
controls each frame, and the retained OS controls live in the backend, keyed.
@tannevaled
tannevaled merged commit 9d4a246 into main Aug 31, 2026
1 check passed
@tannevaled
tannevaled deleted the feat/native-descriptor branch August 31, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant