toolkit: Native — a host-filled control region (the anti-Foreign) - #438
Merged
Conversation
… a real control A Native is a rectangle the toolkit lays out but does not paint: a host that can embed platform controls places one of its own where a Native sits, so a password box is the operating system's secure field, a slider is the system slider — the behaviour a drawn imitation cannot carry (secure text entry, the exact focus ring, native accessibility). This is deliberately NOT the old Foreign, retired in #428 for want of a consumer a bare rectangle could serve. A real control is not a picture: the person edits it, so its value must flow BACK to the model, and it survives across frames, so the host must find the same control again rather than rebuild it and lose focus. Native answers both — a mvvm.Observable per value for two-way binding, and a stable Key the host diffs on — which is what earns it the place Foreign could not hold. Its consumer is the go-widgets/window cocoa backend, landing alongside; it is not merged before that caller exists. WalkNative reuses Foreign's sound clip-aware descent (childOffsetter / childContainer / childClipper), returning each placement with the clip an enclosing viewport imposes. Draw/OnEvent stand down once a host claims the region; Children exposes the fallback (for a11y and layout) only while unclaimed, so the native control's own accessibility is never doubled. native.go is 100% covered by native_test.go through fake walk-interface implementations that pin the clip math without a ScrollView's gutter arithmetic.
tannevaled
marked this pull request as ready for review
August 31, 2026 09:55
This was referenced Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⚠ Draft — do not merge until its consumer lands
`Native` is the toolkit half of the native AppKit widget framework. It must land with its consumer — the `go-widgets/window` cocoa `syncNative` host loop (PR to follow) — never before it, because a placement seam with no caller is exactly what got the old `Foreign` retired in #428. Kept as a draft so it is not auto-merged while consumer-less.
What this adds
A `Native` widget: a rectangle the toolkit lays out but does not paint, so a host that can embed real platform controls places one of its own there — a real `NSSecureTextField` for a password, the system slider, etc. Backed by the new `go-macos/appkit` binding on macOS.
Why it is not a revival of Foreign
Foreign carried a rect, a key and an opaque payload, and died for want of a consumer a rect could serve. A real control differs in two ways a bare region cannot express, and both are the reasons its consumer couldn't adopt Foreign:
`WalkNative` reuses Foreign's sound clip-aware descent verbatim. `Draw`/`OnEvent` stand down once claimed; `Children` exposes the fallback (for a11y/layout) only while unclaimed, so a claimed control's accessibility is never doubled.
Tests
`native.go` is 100% covered by `native_test.go`, using fake walk-interface implementations that pin the clip math deterministically (no ScrollView gutter arithmetic). Full toolkit suite green locally; gofmt/vet clean.