Skip to content

win32: native-control backend (child HWNDs over the framebuffer) - #89

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

win32: native-control backend (child HWNDs over the framebuffer)#89
tannevaled merged 1 commit into
mainfrom
feat/win32-native-controls

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

The Windows peer of the cocoa native-control backend (window v0.59.0). toolkit.NativeControl descriptors become real Win32 child HWNDs parented to the framebuffer window — the structural analog of NSView subviews. Mirrors the cocoa liveControl/syncNative/applySpec/makeControl structure and the immediate-mode-safe value-diff binding exactly.

Controls (all nine kinds)

  • Button → BUTTON (BS_PUSHBUTTON); Label → STATIC
  • Entry → EDIT (WS_BORDER|ES_AUTOHSCROLL); SecureEntry → EDIT (ES_PASSWORD)
  • Checkbox/Switch → BUTTON (BS_AUTOCHECKBOX — no native toggle pre-WinUI); Radio → BS_AUTORADIOBUTTON
  • Slider → msctls_trackbar32 (float [Min,Max] ↔ integer [0,1000])
  • PopUp → COMBOBOX (CBS_DROPDOWNLIST)

Notifications (native → app)

The idiomatic parent-notification path: children send the parent WM_COMMAND (BN_CLICKED / EN_CHANGE / CBN_SELCHANGE), the trackbar WM_HSCROLL. wndProc routes these to onCommand/onHScroll, which look the control up by HWND and report through the liveControl's app callbacks, then repaint. syncNative is hooked at the same per-frame points cocoa uses; the parent gains WS_CLIPCHILDREN so the StretchDIBits blit never paints over a child.

Verification

Cross-builds clean for windows/amd64 + windows/arm64; go vet + gofmt clean; no host regression; the Linux 100% coverage gate on internal/win32 is unaffected (new files are windows-tagged). A pure gatherNative unit test (windows lane) mirrors cocoa's; a gated on-device test (WINDOW_WIN32_INTEGRATION) is the runtime proof.

Honest caveat: verified by cross-compilation and structural parity with the proven cocoa backend — not executed on real Windows in this environment. Runtime behaviour (control creation, WM_COMMAND delivery, trackbar scroll, combo selection) awaits the on-device test.

Add internal/win32/native_windows.go, the Windows peer of the Cocoa
native-control backend, so toolkit.Native descriptors become real Win32
child controls parented to the framebuffer window.

Each frame the app describes the controls it wants (a Surface's Controls
provider or WalkNative over a widget tree); the backend reconciles a
map[Key]*liveControl -- creating new children, repositioning and
value-updating existing ones, destroying gone ones. The value binding is
immediate-mode-safe exactly as in Cocoa: a value is pushed into a control
only when it differs from what the control last reported, so the person's
caret and selection are never disturbed.

Control kinds:
  NativeButton      -> BUTTON  (BS_PUSHBUTTON)
  NativeLabel       -> STATIC
  NativeEntry       -> EDIT    (WS_BORDER|ES_AUTOHSCROLL)
  NativeSecureEntry -> EDIT    (ES_PASSWORD)
  NativeCheckbox    -> BUTTON  (BS_AUTOCHECKBOX)
  NativeRadio       -> BUTTON  (BS_AUTORADIOBUTTON)
  NativeSwitch      -> BUTTON  (BS_AUTOCHECKBOX; no native toggle pre-WinUI)
  NativeSlider      -> msctls_trackbar32 (InitCommonControlsEx, float<->int)
  NativePopUp       -> COMBOBOX (CBS_DROPDOWNLIST, CB_ADDSTRING items)

Notifications flow the idiomatic way: children send the parent WM_COMMAND
(BN_CLICKED / EN_CHANGE / CBN_SELCHANGE) and the trackbar sends WM_HSCROLL;
wndProc routes both to onCommand/onHScroll, which look the control up by
HWND and report through the liveControl's app callbacks. The parent gains
WS_CLIPCHILDREN so the StretchDIBits blit never paints over a child.
syncNative is seeded in Run and reconciled each frame via paintFrame.

Rects arrive in framebuffer coordinates and are scaled to physical client
pixels by frameScale (identity in the native/physical-framebuffer model).

Tests: native_windows_test.go mirrors the Cocoa gatherNative provider-vs-
walk unit test (runs on the windows lane); nativelive_windows_test.go is
the WINDOW_WIN32_INTEGRATION-gated on-device proof. Cross-builds clean for
windows/amd64 and windows/arm64; gofmt and go vet clean; the Linux 100%
coverage gate is unaffected (the new files are windows-tagged).
@tannevaled
tannevaled merged commit 4094781 into main Aug 31, 2026
27 checks passed
@tannevaled
tannevaled deleted the feat/win32-native-controls branch August 31, 2026 13:31
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