Skip to content

Fix #5518: Padding adornment fills with stale attribute when parent skips ClearViewport (#5358)#5519

Merged
tig merged 2 commits into
developfrom
tig/imageview-x-minus-1-overdraw-test
Jun 24, 2026
Merged

Fix #5518: Padding adornment fills with stale attribute when parent skips ClearViewport (#5358)#5519
tig merged 2 commits into
developfrom
tig/imageview-x-minus-1-overdraw-test

Conversation

@tig

@tig tig commented Jun 22, 2026

Copy link
Copy Markdown
Member

Fixes #5518.

Symptom

A view with a right Padding next to a raster ImageView (winprint's settings panel ↔ page preview) shows the image pane's background bleeding into the padding "seam" column after a partial (focus) redraw — on both Sixel and Kitty, so not encoder-specific. In fact it isn't ImageView-specific at all: a plain View reproduces it.

Root cause

Thickness.Draw fills an adornment via driver.CurrentAttribute and never sets an attribute itself — it relies on the caller having set one. The parent's ClearViewportSetAttributeForRole used to set CurrentAttribute to the parent's background right before adornments drew.

Since #5358 (584c02526) the parent skips ClearViewport on a child-only redraw (the intended fan-out optimization). So on a partial redraw the Padding adornment inherits a stale CurrentAttributeColor.None left behind by a sibling raster ImageView's transparent-marking (or the sibling's background for a plain View) — and fills the padding column with it. Driver-level tracing pinned the writer exactly:

WRITE r2c9 attr=[None,None,None] via:
   OutputBufferImpl.FillRect
   Thickness.Draw          ← the neighbour's Padding adornment
   View.DrawAdornments

(Bisect: passes at 584c02526^, fails from 584c02526 on.)

Fix

Set the view's Normal attribute before the no-subview Padding.Thickness.Draw, so the fill is deterministic regardless of prior draw state. It does not touch the #5358 clear optimization, so the fan-out tests keep passing.

The same latent pattern exists for Margin/Border Thickness.Draw (they also fill with the ambient attribute); I scoped this PR to the demonstrated Padding case and left those, since their "correct" attribute differs (border scheme, transparent margin) and no repro exercises them.

Validation

  • New regression test ImageViewNeighbourPaddingTests (fails before, passes after).
  • UnitTestsParallelizable: 17449 / 0 failed. UnitTests.NonParallelizable: 74 / 0 failed. The fan-out / clipping tests that the naive "always clear" approach broke (SubViewOnlyRedrawTests, TabsFanOutDiagnosticTests, ViewDrawingClippingTests, BorderArrangementTests) all stay green.

🤖 Generated with Claude Code

…dding column

A raster ImageView placed at Pos.Right of a view that has a right Padding column
overwrites that padding column (the cell one to the LEFT of the ImageView's frame)
when it redraws. The full first draw repaints the neighbour so it looks correct; a
partial redraw (a focus round-trip) leaves the overwrite behind.

Surfaced in winprint as the page-preview pane bleeding its canvas colour into the
settings panel's seam (on both Sixel and Kitty). Not actually raster-specific — a
plain View at Pos.Right reproduces it too — so the fix likely belongs in the View
draw/clip path.

Test currently FAILS (reproduces #5518).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dec1d4af2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@tig

tig commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Root cause: regression bisected to #5358 (584c02526)

git bisect pins the failure to 584c02526 "Gate self-content draws on needsDrawSelf to stop child-only redraw fan-out" (#5358). The test passes at its parent (584c02526^) and fails from that commit onward.

Mechanism

The neighbour pane has a transparent right Padding adornment (the "seam" column). With instrumentation:

  • Before Stop child-only redraws from escalating into full parent redraws #5358: on the focus-driven partial redraw, the parent (Runnable) re-cleared its whole viewport every frame, so the seam column was re-established as the parent's background. The transparent padding composited against that fresh background → correct.
  • After Stop child-only redraws from escalating into full parent redraws #5358: the parent enters Draw() only via SubViewNeedsDraw (needsDrawSelf == false), so DoClearViewport is skipped (the intended optimization). The neighbour's transparent padding column now composites against stale backing — it shows the image pane's content (Color.None raster-transparent for a Sixel/Kitty ImageView, or the pane's solid background for a plain View) instead of the parent's background.

So the seam cell goes from the window background to the image pane's background on the partial redraw — the bug winprint reported.

Fix is non-trivial (don't just revert the gate)

The naive fix — always DoClearViewport — makes this test pass but breaks the #5358 contract (SubViewOnlyRedrawTests.ChildOnlyDirty_ParentDoesNotClearOrRedrawSelf and ParentWithBorder_ChildOnlyDirty_ParentDoesNotClear both assert the parent must not clear at all on a child-only redraw; 3 draw tests fail). The correct fix has to preserve the optimization while ensuring a transparent adornment/ViewportSettings.Transparent region composites against the parent's current background after a partial redraw — i.e. the fix belongs in the transparency-compositing / clip-exclusion path, not in re-enabling the parent clear.

Repro test in this PR stays as the red guard.

Thickness.Draw fills the adornment via the driver's CurrentAttribute and never
sets one itself, relying on the caller having set it. The parent's ClearViewport
(SetAttributeForRole) used to do that right before adornments drew. Since #5358
the parent skips its ClearViewport on a child-only redraw, so the Padding adornment
inherited a stale CurrentAttribute — e.g. Color.None left by a sibling raster
ImageView's transparent-marking — and filled the padding column with foreign
content (the seam-overdraw winprint reported, on both Sixel and Kitty).

Set the view's Normal attribute before the no-subview Padding.Thickness.Draw so the
fill is deterministic regardless of prior draw state. Does not touch the #5358 clear
optimization. Full parallelizable (17449) + non-parallelizable (74) suites stay green;
the #5518 repro test now passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tig tig changed the title Repro test for #5518: ImageView overwrites neighbour's right-padding column on partial redraw Fix #5518: Padding adornment fills with stale attribute when parent skips ClearViewport (#5358) Jun 23, 2026
@tig tig requested review from Copilot and harder June 23, 2026 02:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a redraw regression where a view’s right Padding thickness could be filled using a stale Driver.CurrentAttribute when the parent skips ClearViewport during child-only redraws (#5358), causing background “bleed” into the padding seam (#5518).

Changes:

  • Set VisualRole.Normal before calling Padding.Thickness.Draw (...) when Padding.View is not present, ensuring deterministic padding fill regardless of prior draw state.
  • Add a regression test reproducing the seam overwrite via focus-driven partial redraws with an ImageView adjacent to a padded neighbour.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
Terminal.Gui/ViewBase/View.Drawing.Adornments.cs Forces a known attribute before padding thickness fill to avoid inheriting stale CurrentAttribute on partial redraws.
Tests/UnitTestsParallelizable/Views/ImageViewNeighbourPaddingTests.cs New regression test covering the seam/padding overwrite scenario from #5518.

@tig tig requested a review from BDisp June 24, 2026 16:01
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.

ImageView (Sixel): on redraw, the column at Frame.X-1 is overdrawn with the image background

3 participants