Skip to content

Partially addresses #4522: document NeedsLayout invariant + lock in layout-convergence guards#5500

Draft
harder wants to merge 1 commit into
tui-cs:developfrom
harder:kh/4522-needslayout-lifecycle
Draft

Partially addresses #4522: document NeedsLayout invariant + lock in layout-convergence guards#5500
harder wants to merge 1 commit into
tui-cs:developfrom
harder:kh/4522-needslayout-lifecycle

Conversation

@harder

@harder harder commented Jun 18, 2026

Copy link
Copy Markdown
Member

Partially addresses #4522 (NeedsLayout is buggy).

⚠️ Draft. This intentionally does not fully close #4522 — see "What this deliberately does not do".

Summary

This PR delivers the safe, verifiable portion of #4522 and — critically — measures the current layout-convergence behavior to determine whether the long-feared NeedsLayout/SetFrame convergence redesign is still warranted after the #4973-era fixes (#5357 / #5358 / #5359). It is not, and this PR locks in the good behavior with regression guards instead of taking on a high-risk rewrite.

What changed

Library (View.Layout.cs)

  • Replaced the misleading NeedsLayout region comment — which the issue explicitly calls "bogus" ("We expose no setter…") — with an accurate description of the real invariant and the internal write sites, and a note on why the setter stays internal (test seam) pending full removal.
  • LayoutSubViews now re-reads GetContentSize() after the SubViewLayout event as well. It already did so after the OnSubViewLayout virtual (Partially Fixes #4522 - Stale ContentSize capture #4863); this closes the symmetric gap so a handler on the event that calls SetContentSize is honored.

Tests

Measured convergence (the key finding)

Fan-out of a single property change on current develop:

Tree (depth × breadth) total views application layout passes views that re-ran LayoutSubViews FrameChanged
3 × 3 10 1 5 1
6 × 3 19 1 8 1
8 × 4 33 1 10 1

The only residual is O(depth) ancestor re-layout that changes no frame. Removing it needs dependency-aware invalidation, which is high-risk to Dim.Auto correctness for an already-single-pass gain.

What this deliberately does NOT do

These remain as deferred, now data-backed as low priority (see plans/4522-needslayout-lifecycle.md).

Verification

  • All-views render diff: SHA of Driver.ToString() for all 61 concrete view types with the LayoutSubViews change applied vs. reverted → byte-identical (combined hash CCF30A9EA3C45AB5 both ways). The change is provably render- and layout-neutral across all views.
  • Existing AllViewsDrawTests passes (every view draws with exactly one layout pass).
  • UnitTestsParallelizable: 17,382 passed, 0 failed. UnitTests.NonParallelizable: 74 passed, 0 failed.

To pull down this PR locally

git remote add copilot https://github.com/harder/Terminal.Gui.git
git fetch copilot kh/4522-needslayout-lifecycle
git checkout kh/4522-needslayout-lifecycle

🤖 Generated with Claude Code

…-read, add layout-convergence guards

Addresses the safe, verifiable portion of tui-cs#4522 (`NeedsLayout` is buggy) and
documents — with measurements — why the remaining "convergence redesign" is not
warranted after the tui-cs#4973-era fixes (tui-cs#5357/tui-cs#5358/tui-cs#5359).

Library:
- View.Layout.cs: replace the "bogus" NeedsLayout region comment (called out in
  the issue) with an accurate description of the real invariant and the internal
  write sites; note why the setter is internal (test seam) pending full removal.
- View.Layout.cs: LayoutSubViews now re-reads GetContentSize() after the
  SubViewLayout *event* too (it already did after the OnSubViewLayout virtual,
  tui-cs#4863), so a handler on that event that calls SetContentSize is honored.

Tests:
- StaleContentSizeCaptureTests: +1 regression test for the SubViewLayout-event
  content-size case.
- LayoutConvergenceTests (new): permanent guards locking in the deterministic
  properties established by tui-cs#5357/tui-cs#5358/tui-cs#5359 — single-pass convergence, no
  spurious FrameChanged, bounded ancestor-chain fan-out (no subtree fan-out),
  Dim.Auto growth correctness, and Pos.Right sibling repositioning.
- AllViewsRenderFingerprintTests (new): all-views smoke guard (every concrete
  View lays out + draws in design mode without throwing); also the harness used
  to prove the LayoutSubViews change is byte-identical in rendering across all
  61 view types.

Verification: UnitTestsParallelizable 17,382 passed; UnitTests.NonParallelizable
74 passed; all-views Driver.ToString() fingerprint identical with/without the
library change.

See plans/4522-needslayout-lifecycle.md for the full assessment and measured
fan-out numbers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@harder harder mentioned this pull request Jun 18, 2026
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.

NeedsLayout is buggy

1 participant