Skip to content

Windows parity: jump palette dimming, anchoring, attention-first order and a scrollbar that stays dark on hover (#776) - #792

Merged
coneilen merged 4 commits into
mainfrom
coneilen-microsoft-jump-palette-residuals
Oct 14, 2026
Merged

coneilen merged 4 commits into
mainfrom
coneilen-microsoft-jump-palette-residuals

Conversation

@coneilen

@coneilen coneilen commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Complete the jump-palette residuals from #788: dim the owning client area, center the palette at the macOS sheet position, apply macOS attention-first ranking and render overflow scrolling in the dark palette, including while the native scrollbar is hovered, dragged, clicked or wheeled.

Refs #776. This PR was taken over from lane E (1st escalation) after its compositor-popup hover fix was rejected twice in source review. That candidate was not built on: the grey hover flash has a simpler cause (see Changes), and the reviewer's open finding about region ordering is closed by the new mechanism. This does not modify the coordinator-owned ledger or close the issue.

Changes

  • Center the 460px palette on its owning client, clamping above the footer/below the toolbar only for small owners. Add a client-confined, nonactivating layered black backdrop at 40% opacity, destroyed on modal dismissal.
  • Populate Entry.needs_human from exact project/node membership in model.attention_entries. The only App.zig edit is the coordinator-approved entry-construction line.
  • Match macOS ranking: empty-query stable attention partition; nonempty prefix, then substring, then displayed type/state, then project; ties prefer attention, then localized case-insensitive title.
  • Retain native LISTBOX scroll state, mouse input and UIA ScrollPattern. A cosmetic child strip paints the native scrollbar geometry dark; native rendering/scroll messages and a 33ms timer refresh the thumb.
  • Hover/drag/track defect fix. The strip was created WS_EX_TRANSPARENT, which makes a window exempt from the sibling clip of the windows beneath it, so the LISTBOX's own nonclient scrollbar painting landed on the strip. A live control run on the previous head reproduces it (Light scrollbar pixel during hover at frame1 ... RGB=135). The strip is now an opaque WS_CHILD | WS_CLIPSIBLINGS sibling above the list, so it is clipped out of that painting; WM_NCHITTEST still returns HTTRANSPARENT, so input reaches the native list. Being a child of the palette, it inherits the rounded window region, so no region is copied and there is no GetWindowRgn dependency to fail or latch.
  • The palette's rounded region is installed before any child is moved in WM_SIZE, so no child is laid out under a stale clip.
  • Forward WM_MOUSEWHEEL arriving at the palette to the results list, guarded against reentry. Without WS_EX_TRANSPARENT the wheel over the strip reached the strip, and the wheel also never scrolled the results while the focused window was the search field (this machine uses MouseWheelRouting=2).
  • Open an overflowing palette at its first result. The list is filled while zero-height, which left it scrolled by one row, hiding the attention-first row on open.

Test plan

After loading C:\gct\e2\environment.ps1, commands ran in graphcode-windows; all evidence is under C:\gcs\e2 (lane E's earlier evidence stays under C:\gcs\e\residuals).

RED: & $env:GRAPHCODE_ZIG0152 test src\JumpPalette.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 "-I$env:GRAPHCODE_WINGHOSTTY_ROOT\include" --cache-dir C:\gcs\e2\zig-cache --global-cache-dir C:\gcs\e2\zig-global --test-filter "palette residual overflow at creation" --test-filter "opaque sibling decoration" --test-filter "owner region is installed" -> 1 passed, 2 failed: the strip still had WS_EX_TRANSPARENT and the owner region was stale when the list moved (red-attempt1.log).
GREEN: & $env:GRAPHCODE_ZIG0152 test src\JumpPalette.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 "-I$env:GRAPHCODE_WINGHOSTTY_ROOT\include" --cache-dir C:\gcs\e2\zig-cache --global-cache-dir C:\gcs\e2\zig-global --test-filter "palette residual" -> All 19 tests passed on the final source (green-palette-residual-final.log).
REGRESSION: & $env:GRAPHCODE_ZIG0152 test src\JumpPalette.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 "-I$env:GRAPHCODE_WINGHOSTTY_ROOT\include" --cache-dir C:\gcs\e2\zig-cache --global-cache-dir C:\gcs\e2\zig-global -> All 40 tests passed on the final source (regression-root-a4.log); baseline before this change was 32 passed (baseline-head.log).

Additional REDs, each observed before its fix (retained under C:\gcs\e2): --test-filter "wheel over the decoration" failed on the scroll assertion after a precondition mistake of mine was kept separately (red-wheel-precondition-failure.log, then red-wheel.log; that test was written against an earlier strip-level handler and is regression coverage for the final palette-level handler); --test-filter "wheel delivered to the focused" failed because wheel sent to the search field did not scroll the results (red-wheel-edit.log); --test-filter "opens scrolled to the first result" failed expected 0, found 1 (red-top-index.log). Observed GREEN: green-attempt1.log, green-wheel.log, green-wheel-edit.log, green-top-index.log.

Tests that passed on their first run and are therefore regression coverage only, not an observed RED: "overflow at creation" (the real production WM_CREATE path with 12 overflowing entries), "posted wheel reaches the native list", "wheel the list declines does not recurse". The structural "opaque sibling decoration" test is a configuration contract: hidden windows cannot prove clipping, and a device-context probe on a message-only parent was empty, so rendered clipping is proven only by the live captures below.

Final-source local runs: palette tests 19/19 (green-palette-residual-final.log), JumpPalette root 40/40 (regression-root-a4.log), zig fmt --check clean, pwsh -NoProfile -File Tools\windows\validate.ps1 -Task visual-baseline passed on the rebased head 3d0bd3ea (visual-baseline-final.log: VisualBaseline.Tests.ps1: PASS, Tests Passed: 5, Failed: 0, Skipped: 0; process-scoped scratch profile, Pester 5.7.1 from C:\v5modules). Full pwsh -NoProfile -File Tools\windows\validate.ps1 -Task windows-shell -SkipTrayLive -ShellPart unit -ShellValidationRoot C:\gc-e2 -SwiftExecutable (the pinned toolchain) ran on the frozen head 740ccea1 under the desktop lease with a process-scoped scratch profile: exit 0, All 1214 tests passed. for the App root (it includes the new palette tests), the JumpPalette root 40/40, Windows shell scaffold contract: PASS (58 source files wired; shard 0 of 1 executed 55 of 55 sections) and WINDOWS_SHELL_DEFAULT_PROFILE_UNCHANGED=verified (regression-unit-final.log). It ran while other lanes shared the machine, and some roots show windows.

CI on the pre-rebase head 740ccea1b62da393d89ea2163e62ed704d12d73f (same graphcode-windows tree): Windows shell validation 38554850921 and Windows port validation 38554850945 succeeded, as did DCO, TDD evidence, Linux, macOS shared Swift and Windows release hardening. In that run, shard 2 reported the JumpPalette root All 40 tests passed., shard 0 ran the App root (1214 tests: 1209 passed, 5 skipped, 0 failed) including the new palette tests, and the integration job emitted SCRUBBED_SHELL_STARTUP: PASS; executed=5 and UIA_JUMP_PALETTE_EVIDENCE (logs ci-job-115821960891.log, ci-job-115821960746.log, ci-job-115821960539.log). Full local integration was NotExecuted.

Live evidence (desktop lease, 100% DPI, synthetic fixture, injected input)

Identity of the final captures: git head 740ccea1b62da393d89ea2163e62ed704d12d73f, JumpPalette.zig SHA-256 5E649F2FDC9D8BA7AD6F54F92603D587452C8037E161D34E5945E1E32DF05848, binary 8CFD0C5E5187A1F97D56CEFEC0431C22A4EFD076FD227080831A1C948281E9E0, harness CA56A5F8B0979FC0E3C98862161C3545E882855A02D7C4CC19E239AAD8251E13 (per-run capture-*.run.json records the exact command and the harness hash actually used; identity-final.json, identity-slot-before.json and identity-slot-after-captures.json record the source and binary and name earlier, superseded harness hashes, because I kept editing the harness during the slot). Harness: a modified copy of lane E's capture-residuals.ps1; PrintWindow first, then an ownership-checked screen fallback (WindowFromPoint at the centre and on a 40 px grid, before and after) because PrintWindow omits the owned popup; no ActivateWindow.

Control reproduces the defect, same harness. On harness 3BD73BAB, PR #792's previous head binary (AE52F9C8) failed the hover check, Light scrollbar pixel during hover at frame1 x5 y19 RGB=163,163,163 (cap\capture-control12-052350.log, frame kept in cap\control12-052350), while the final binary passed 12 rows on that same harness (cap\capture-a412-052250.log and .run.json). An earlier control on my first harness also failed, RGB=135,135,135 (cap\capture-control12.log, cap\control12). The final captures were then rerun after two further harness fixes (a bug on the 8-row path, and the failure-list count) on harness CA56A5F8, and are the ones shown below. The fix passes there: 12 rows, hover (16 + 120 samples with 1px cursor jitter), thumb drag (24 samples while the thumb moves, 60 held with jitter), track click, arrow click, posted wheel and filter/clear, all dark (cap\capture-a412-052434.log; 8 rows cap\capture-a48-052415.log). The frame count is screen samples, not guaranteed compositor frames.

macOS Windows before (merged #788 build) Windows after (this head) Criteria shown
macOS 750 jump palette Before, 8 rows After, 8 rows Dimmed owner client, palette centred, attention-first row (Ref Goal, DONE)
Same macOS reference (scroll indicators hidden) Before, 12 rows, light scrollbar, no dimming After, 12 rows Overflow opens at the first result with a dark scrollbar
not applicable not applicable After, 12 rows, scrollbar hovered Scrollbar hovered (cursor over the thumb, not drawn in the capture) stays dark

Neighbouring canvas, before opening and after Escape (same hash 1BBF3B77A98D... in the 8-row run): before open after Escape. Palette tone, type-bar and dimmed-canvas pixels were verified per image. The "before" images are lane E's valid runs on the merged #788 binary (C0D183A7, source C221CD56), not recaptured here.

Wheel. A post to the focus window, a post to the cosmetic strip and a post to the list all scroll the real list, with focus on the list and on the search field (the focused window class and the foreground process are asserted, and the list is reset to top index 0 before each case; 1 to 2 posts were needed per case). The final 12-row run also had UIA: ScrollPattern LargeIncrement advanced the list and a UIA hit test at the thumb centre returned the native Thumb.

Disclosures. Hardware wheel injection (mouse_event, SendInput) did not scroll even a plain Win32 LISTBOX with no GraphCode code on this machine (wheel-control.log to wheel-control4.log), and a raw LISTBOX ignores its first posted wheel message; the harness therefore posts WM_MOUSEWHEEL after a primer, so hardware-wheel routing is NOT VERIFIED. The machine's UIA Win32 proxy also stopped exposing patterns for any LISTBOX between 04:56 and about 05:10 (control binary and a raw probe included), so several runs failed or degraded; those logs are kept (cap\retained-failures.md lists every run, including harness bugs of mine). Inherited from lane E and still retained: the earlier foreground-capture attempt that photographed the wrong app, capture-before8-verified.log (the border sample hit the rounded edge) and capture-before12-verified.log (top index started at 1). That last one was a product defect, not only harness noise: lane E's harness normalised it with LB_SETTOPINDEX(0), hiding that an overflowing palette opened scrolled by one row; this PR now fixes it (740ccea1) and the harness asserts opening index 0.

Rebase. After all local evidence above was taken, the branch was rebased onto origin/main 2627d357 (it brings in #802, #803 and #796). The rebased commits' graphcode-windows tree is byte-identical to the one the local evidence ran on (git tree 29ef1c55d6f3c271e7b74760bea1eb89f8b1c859, JumpPalette.zig blob 1ea3828c...), and main's delta is Tools\windows\validate.ps1 plus a plan document. The local unit run therefore does not include #802's added validate.ps1 lines; exact-head CI on the rebased head is the regression for those: Windows shell validation 38566774591 and Windows port validation 38566774881 succeeded on 3d0bd3ea16c3c6b8c8fd4668089b3276d3cdaa9f, as did DCO, TDD evidence, Linux, macOS shared Swift and Windows release hardening. Shard 2 reported the JumpPalette root All 40 tests passed. (including palette residual overflow opens scrolled to the first result), shard 0 the App root 1209 passed; 5 skipped; 0 failed., and the integration job SCRUBBED_SHELL_STARTUP: PASS; executed=5 and UIA_JUMP_PALETTE_EVIDENCE (logs ci-rebased-job-115861421488.log, ci-rebased-job-115861421976.log, ci-rebased-job-115861421547.log); every windows-shell job and windows-spikes passed.

Not verified

  • Hardware wheel over the real palette (injected mouse_event or SendInput, or a physical wheel); only posted WM_MOUSEWHEEL and hidden-window messages.
  • UIA availability was intermittent on this machine: it did not engage from 04:56 to about 05:10 and on a few degraded runs, and engaged again on capture-a412-052250, the control run and the final 12-row run; the final run's ScrollPattern and Thumb hit test are real evidence, the degraded runs are not. Assistive-technology conformance and physical keyboard or mouse input are not covered.
  • Mixed or non-100% DPI, other fixtures (no-match state, filtered-height recentering), other Windows versions.
  • Rendered fidelity against the macOS scroll indicator beyond "dark track and thumb"; the macOS build was not run (unavailable on this host).
  • Full local integration (-ShellPart integration) was NotExecuted; exact-head CI integration passed.

Rubber-duck review

Reviewer: gpt-6.1-sol, high (I am a Claude model), through the repository rubber-duck agent. It opened logs, images and source; rejection count 0.

First review, head f92fa179. No blocking source finding. It accepted the mechanism and the closure of the previous owner's open finding (region installed before child moves; the strip is a child of the palette so it inherits the rounded region and there is no GetWindowRgn to fail or latch; real WM_CREATE overflow test), correctly classified which new tests are regression-only, and set conditions: final-head runtime evidence; assert the focus class and foreground process in the wheel matrix (it recorded them without enforcing them); sample the vertical movement of a held thumb, not only the held position; and refresh provenance. Responses: all four are done (the harness now asserts the focus window class, the foreground PID and a reset top index of 0; 24 samples are taken while the thumb moves, then 60 held with jitter; every run writes a .run.json with the head, the hashes and the exact command; the final captures were taken on the frozen head). Building those checks also exposed a real defect it had not seen (an overflowing palette opened scrolled by one row), fixed in 740ccea1 test-first.

Final review, head 740ccea1. No BLOCKING finding. Verdict: ACCEPT-WITH-CONDITIONS. It judged the delta (LB_SETTOPINDEX(0) after the list is sized in WM_SIZE) correct for the fixed-size popup and found no conflict with user scrolling or filtering. It confirmed the previous conditions closed and the earlier open finding closed. Findings and responses: (1) SHOULD-FIX, the same-harness comparison was attributed to the wrong harness hash: corrected above, the control and a passing final-binary run both used harness 3BD73BAB, and the final captures used CA56A5F8; (2) NIT, UIA availability was described too exclusively: corrected in the disclosures; (3) the local unit run was still pending when it reviewed: it has since passed (above). It also noted that posting the wheel to the strip, the focus window and the list exercises the recipient and parent-forwarding paths, not the OS's wheel targeting, and that the primer and retries are justified by the raw LISTBOX controls but do not prove first-notch responsiveness; both are stated as NOT VERIFIED.

Checklist

  • I have read the Contributing Guidelines
  • I have signed off my commits (git commit -s) per the DCO
  • Tests pass locally (make test)
  • Code follows the existing style (make check)
  • I added the test/contract before the implementation and observed the intended RED failure

coneilen and others added 4 commits October 14, 2026 05:49
Refs #776

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 47fe2bfe-f8ec-4057-91f9-8dbcbaa52d39
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
…ting

The cosmetic scrollbar strip was a WS_EX_TRANSPARENT child, which exempts it
from the clip of the LISTBOX beneath it, so native hover, drag and track
painting repainted over the dark strip until the next timer tick. Drop the
style so the sibling is clipped out of native nonclient painting; input still
reaches the list through WM_NCHITTEST/HTTRANSPARENT.

Install the rounded owner region before moving the list so no child is laid
out under a stale clip, and cover the real overflowing WM_CREATE path.

Refs #776

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fb6077ed-123a-42aa-9288-775d63658ce6
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
…er children

Windows routes the wheel to the focused window under hybrid routing, which is
the search field in the palette, and a non-transparent decoration sibling can
receive it too. Neither scrolled the results. Forward WM_MOUSEWHEEL arriving
at the palette to the results list, with a reentrancy guard for wheel the
list itself declines.

Refs #776

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fb6077ed-123a-42aa-9288-775d63658ce6
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
The results list is filled while it is still zero-height, which left it
scrolled by one row when more rows than fit were present, so the
attention-first row was hidden on open. Reset the top index after the list is
laid out.

Refs #776

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fb6077ed-123a-42aa-9288-775d63658ce6
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
@coneilen
coneilen force-pushed the coneilen-microsoft-jump-palette-residuals branch from 740ccea to 3d0bd3e Compare October 14, 2026 12:49
@coneilen coneilen changed the title Windows parity: jump palette dimming, anchoring, attention-first order and dark scrollbar (#776) Windows parity: jump palette dimming, anchoring, attention-first order and a scrollbar that stays dark on hover (#776) Oct 14, 2026
@coneilen
coneilen marked this pull request as ready for review October 14, 2026 13:14
@coneilen
coneilen merged commit 13758a7 into main Oct 14, 2026
27 checks passed
@coneilen
coneilen deleted the coneilen-microsoft-jump-palette-residuals branch October 14, 2026 14:34
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