TurtleGit for Mac

View Markdown source ↗

Native file-list interaction ownership

This family preserves FullRowSelect, status colors, native selection and embedded controls while making shared list decoration safe across row creation, reload, sorting, delegate replacement and retirement. Implementation and grouped native checks initially passed Debug and six native case/engine pairs after recorded fixture-oracle repairs. A subsequent physical Debug Log check confirmed a real duplicated-label defect; measured geometry and direct text drawing now pass focused native consumers and bounded physical Log acceptance. QA record.

Source contract

Pinned upstream 7338078f8ddd924b8cddee35f512f2286072136d defines FullRowSelect=true in Settings/SettingsAdvanced.cpp:46. TortoiseProc/ResizableColumnsListCtrl.h:86–93 enables LVS_EX_FULLROWSELECT conditionally alongside header drag/reorder. This is a selection-painting preference, not a replacement selection model. Git/GitStatusListCtrl.cpp:1255ff distinguishes selected/focused transitions from checkbox changes. Its context menu uses the selection mark and hit-tested column at 1639ff, and drag at 3767ff uses the selected file paths. StoreScrollPos/RestoreScrollPos at 899–931 retain scroll position and the selection/focus mark. The primary Log commit list explicitly uses full-row selection at TortoiseProc/GitLogListBase.cpp:264; this family concerns the shared native file/reference list decorator, not replacing that Log graph/list.

Native implementation

NativeListSelection keeps the table's selected indexes, keyboard navigation, context clicks, accessibility and embedded controls under native/consumer ownership. Full-row mode uses ordinary AppKit selection; disabled mode draws only the measured primary label, retaining row backgrounds, source status colors, key-window emphasis and high-contrast behavior. The drawing overlay does not accept mouse hits or become an accessibility element.

Row factories now configure only the supplied row. They do not request table cells, enumerate table rows or reorder subviews while AppKit creates a row. Deferred, coalesced reconciliation checks the current table/delegate/owner and generation, then uses current row mappings to update cell backgrounds and place the existing label overlay. Reload/sort cannot replay a captured obsolete row index. This addresses a concrete reentrant construction path; earlier NSTableView warnings had no stack proving that this was their sole cause.

Cross-table row reuse releases the previous owner's decoration and restores its saved style before binding the new owner. Retirement invalidates queued work, restores only rows it still owns and releases its overlay/observers. A late update for a closed window cannot revive its owner. Explicit remove/reinstall or reparenting into a different live window permits fresh attachment. Replacing the native delegate retires the old proxy before capturing the replacement. The SwiftUI probe preserves the primary column object through reorder, but resolves a new one when the schema replaces that object.

Consumer replacement exposed a related ownership gap: a retired predecessor's late teardown could remove decoration installed by its successor on the same native table. Install/remove now accept a trailing optional client object, retained weakly with exact identity. CommitFileInteraction and the shared probe pass themselves consistently; predecessor removal cannot retire the new client, and a closed client's late install cannot revive the same owner. Existing callers without a client keep their previous API contract. Pending interaction configuration also retains its existing active/generation fence.

The same delegate replacement could leave keyboard routing consulting the old coordinator: WorktreeListTable.swift:255 uses originalDelegate(of:) to route F5. That accessor now unwraps only a live proxy which is still the table's actual delegate; otherwise it returns the current native delegate. The lifecycle case includes a real replacement delegate. This is bounded keyboard-routing correctness, not full physical keyboard acceptance.

Commit, Log changed files and Working Tree continue using CommitFileInteraction for menu snapshots, checkbox/focus independence, drag, keyboard selection, column layout and refresh-position ownership. Its teardown removes shared selection decoration; replacing a table removes the previous table's owner with its own client receipt. Context clicks retain native highlight/anchor and resolve the clicked row into a captured menu request, an existing macOS interaction adaptation. The shared decorator does not dispatch Git operations or reinterpret checked versus highlighted files. Bounded caller review found no additional demonstrated menu/checkbox/drag action gap beyond that client handoff repair.

Checkpoint and remaining scope

The authored fixture and scripts/test-native-list-interactions.py exercise callbacks, lifecycle and consumers with both Git executables: row callbacks without recursive cell or hierarchy work; forwarding/custom rows/colors/label/full-row/sort/reuse; client/delegate replacement, retired callbacks and reparenting; and actual Commit, Log and Working Tree checkbox/multiselection/sorted drag/watermark paths. The consumer cases reuse the existing report/workspace fixture symbols. Checks use private repositories, preferences and pasteboards, offscreen owned windows and cached receivers. Debug session 41727 exited zero, retaining three contradictory exit-zero diagnostics. Localization session 23162 passed 38 locales/1,318 labels. Initial native session 10240 failed callbacks/system with “Custom row subclass was replaced” (receiver 79625 reaped). The fixture's exact class/cast oracle was replaced with original object identity plus the runtime class chain, since decoration deliberately subclasses a supplied row. Focused session 39362 then failed “Native row absent” (79745 reaped); offscreen lazy row acquisition now requests materialization. Session 9520 passes identity/class-chain assertions but fails the custom background-color assertion (79826 reaped). Investigation found that AppKit sets an attached selected row's background to RGB 0.117647/0.117647/0.117647, including without the adapter, instead of the factory's requested 0.31/0.18/0.45. The adapted row matches that independent native baseline. Focused session 51524 passed callbacks/system (79972 reaped), proving the exact original delegate-emitted object and superclass chain, plus explicit reuse marker/object/class/background assertions. Delegate installation itself recreates native rows, so no same-object claim is made across the entire binding, nor a blanket arbitrary custom draw-color guarantee. All three previous failures are retained as fixture acquisition/oracle issues; production source did not change after Debug.

Session 29901 passed lifecycle/consumers with both Git executables (receivers 80002, 80019, 80308, 80318), and 12407 passed bundled callbacks (80565). Together with system callbacks these are six distinct case/engine passes. All nine attempted receivers, including the three earlier failures, were reaped and independently absent; no GUI/cache remained, preserving installed Finder 77954. None of the final logs emits the reentrant delegate warning. That bounded result does not prove global resolution or the precise origin of the earlier warning: the workloads differ and no original stack established its cause.

Six optional light/dark label-only consumer PNGs were captured under build/qa/native-list-interactions-captures-2026-10-25/receiver-80019. The parent reviewed all six and found transparent/incomplete compositing, missing normal glyphs/icons and overlapping left label text in dark Log. They remain diagnostic artifacts, unsuitable for full visual acceptance or website publication. A subsequent deliberate physical Debug Log check independently showed original a/b labels visible to the left of their highlighted overlay: that is a real geometry defect, not merely offscreen compositing. Evidence remains under build/qa/native-list-interactions-physical-2026-10-25.

The repair adds NativeListSelectionLabelMarker, a public NSView-backed measurement of each primary Text label, wired into Commit, Log and Working Tree cell backgrounds. The adapter can then cover the actual native label bounds rather than approximate an unexposed SwiftUI text position. Actual geometry assertions were added. The initial six passes remain historical evidence.

Geometry Debug session 6247 exited zero, retaining seven contradictory exit-zero diagnostics. The fresh all-six native run 41843 checks compare actual marker bounds converted into table coordinates against label text bounds within 0.5 point, require the highlight to cover both label edges, and verify hit/accessibility transparency, resize, path-column reorder/restore and both appearances across all three tables. They run even when optional image capture is disabled. Run 41843 passed all six pairs (reaped receivers 81365, 81375, 81415, 81713, 81723, 81733). This remains a historical geometry checkpoint, rather than final physical acceptance: the next physical Log observation shows the duplicate left glyphs fixed, but early alpha..../beta.... truncation. The initial padding hypothesis was not proven: an AppKit probe (session 41204) showed identical drawing/title rectangles. Padding Debug 80509 exited zero with one contradictory exit-zero diagnostic, but its physical check showed no visible improvement. That inverse-frame helper was removed.

The final marker path draws attributed text directly into actual measured content bounds; ordinary native-field fallback remains unchanged. Final Debug 9477 exited zero with two contradictory exit-zero diagnostics. Focused consumer run 28727 passed both Git executables, including strengthened actual attributed layout snapshots; receivers 82756 and 83046 were reaped. The prior fresh 41843 callback/lifecycle coverage remains applicable to this drawing-only repair.

Deliberate physical Debug Log checks in system dark and explicit Light show full alpha.txt/beta.txt, without original left glyphs, while source icons, status and graph remain unaffected. Final images are log-system-text-repaired.png and log-light-text-repaired.png in the physical evidence directory above. Earlier repaired/padding images remain failed attempts. This is bounded Log acceptance, not complete visual acceptance of all three consumers.

Physical precheck cleanup restored the absent FullRowSelect preference baseline, left Follow System unchanged, forgot only Harbor's fixture permission while retaining TiaFileFormat, and quit Debug. The private index/config/six file hashes are unchanged; one installed Finder registration remains elected and Finder 77954 is preserved. Temporary FullRowSelect=false/Harbor permission were retained only across the physical repairs. Final cleanup restored Follow System through the UI and removed FullRowSelect through Advanced empty Apply; after Debug quit, the original absent appearance key was restored with defaults delete. Immediate plist reads were stale, so authoritative defaults reads verified both keys absent. Only Harbor was forgotten, preserving Tia Recent. No owned GUI/cache/ receiver remained; installed Finder 77954 and its single elected registration are unchanged. HEAD, raw index, config and all six fixture file hashes remain unchanged.

No Core behavior or Git command change is included, so this native-only family does not require a full Core run. No new signed build or installation was performed. The installed app remains the prior 3a72244e candidate; that earlier physical acceptance is separate from this source-level/native checkpoint.

Remaining scope includes complete physical keyboard/VoiceOver/window focus acceptance, every shared-list consumer, source visual identity of every dialog, full current hosted CI and App Store archive/export/submission. These bounded passes do not certify the whole port or retrospectively erase prior warnings.

Commit report-row continuation — October 25, 2026

The Commit report-row family implements compact single-line file and group captions, regular/bold configured-font sizing, full value help and exact native geometry ownership/restoration. The original AppKit row-span recursion is captured separately from the source layout defect; its affected-workload checkpoint is recorded without attributing an unproven cause to the shared selection decorator.