windows: draw the selected tab as a folder joined to the terminal - #756
Open
deblasis wants to merge 2 commits into
Open
windows: draw the selected tab as a folder joined to the terminal#756deblasis wants to merge 2 commits into
deblasis wants to merge 2 commits into
Conversation
The selected tab is stroked in the accent colour on the three sides that do not meet the pane, and the strip behind it is the same colour as that stroke, so the tab reads as a card lifted out of the strip. On the fourth side, where the tab meets the terminal, a cover the width of the tab is drawn over the pane border, so the tab fill runs into the terminal with no line across the join. Horizontal tabs open at the top, vertical tabs at the left, and both get the same treatment. The cover cannot live in the strip: content drawn there is clipped to the strip and never reaches the pane row. It is drawn in the pane row instead, positioned from the selected tab's offset within the strip viewport and clipped to that viewport, so a tab scrolled half out of view gets half a cover rather than one hanging in space. Three things move it, and all three are handled: opening or closing a tab (the new tab has no bounds on the first dispatcher pass, so a one-shot LayoutUpdated re-places it once it does), resizing or moving the window, and switching the layout (placed from the completion callback, since placing it from pre-animation geometry left it drifted by the delta). The vertical strip's selected title also gains its accent colour at startup. UpdateCursorAccentColors ran before the layout snap and so never reached the strip on the first pass, leaving the selected title at 1.11:1. It now runs after, and measures 14.46:1. The covers are gated on whether vertical tabs are wanted, not on the hidden strip's Visibility. The hidden strip is Visible by XAML default and is deliberately flipped by the priming pass, so the Visibility check hid the cover on every launch.
Crash. A user hit an access violation inside NavigationView.set_SelectedItem from SyncSelectionFromManager on a new tab. In horizontal-tab mode the vertical strip is collapsed and, by design, never primed -- priming it from the constructor crashed XAML's measure walk, so it has no template for the whole session. Assigning SelectedItem is where MUXC resolves the selected item's container and selection indicator, and there is nothing to resolve on a control that has never been laid out. The sync is now latched while the strip is unloaded and replayed on Loaded. This is a hypothesis with a mechanism behind it, not a reproduced fix: the crash did not reproduce in 45 tab opens across three launches, and it is still worth landing because driving a never-templated control was wrong regardless. The MenuItems mutations are also fenced with the sync latch now. An insert before the current selection shifts what MUXC considers selected and raises SelectionChanged for a tab the user did not pick, which activates the wrong tab and comes back around to assign SelectedItem from inside MUXC's own notification. Reachable from Move Tab Left/Right. Vertical seam. The clip meant to keep the cover inside the scrolling row list clipped to the host instead, which is Row 0 with RowSpan 2 and so covers the whole window -- the clamp did nothing. Worse, the top half of it clamped to the top of the caption row, so a row scrolled above the list drew a bar starting in the title bar. It clips to the list's own scroller now. Quake. SetStripHidden collapses both strips without re-raising either seam event, leaving a bar of tab colour lying across a terminal with no strip above it. Both covers come down with the strip. Cost and lifetime. QueueBridgeUpdate had no coalescing, so one new tab re-placed the cover four to six times. The layout retry could re-arm forever: it fires when the selected tab reports zero width, a collapsed never-primed strip reports that permanently, and the layout-switch completion asked the strip that had just LEFT for a fresh placement. It asks the one that arrived, refuses to arm on a collapsed strip, and caps the re-arms. Both seam handlers now also catch the COM and null shapes XAML interop throws on a tree that is going down, since they run from dispatcher callbacks where an escape is an unhandled exception on the UI thread, and both events are detached on close. Reverted an unreferenced x:Name and a stray blank line in TabHost.xaml. Not taken: hopping the collection-changed sync onto the dispatcher. The argument for it was that it runs inside MUXC's own MenuItems processing, which it does not -- MUXC has finished by the time our handler continues. It also breaks a wiring test that pins the collection change as the only signal reaching the strip when a non-active tab closes. The latch above is what addresses the reported stack.
deblasis
force-pushed
the
feat/folder-tab-seam
branch
from
August 26, 2026 10:29
a38b1e6 to
1528a10
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The selected tab is stroked in the accent colour on the three sides that do not
meet the pane, and the strip behind it is that same colour, so the tab reads as
a card lifted out of the strip. On the fourth side a cover the width of the tab
is drawn over the pane border, so the tab's fill runs into the terminal with no
line across the join. Horizontal opens at the top, vertical at the left, both
get the same treatment.
Where the cover lives
Not in the strip. Content drawn there is clipped to the strip and never reaches
the pane row. It is drawn in the pane row, positioned from the selected tab's
offset within the strip viewport and clipped to that viewport, so a tab scrolled
half out of view gets half a cover rather than one hanging in space.
Three things move it, all handled:
pass, so a one-shot
LayoutUpdatedre-places it once it does.pre-animation geometry left it drifted by the delta.
Two fixes that came out of it
The covers are gated on whether vertical tabs are wanted, not on the hidden
strip's
Visibility. That strip isVisibleby XAML default and isdeliberately flipped by the priming pass, so the
Visibilitycheck hid thecover on every launch.
UpdateCursorAccentColorsran before the layout snap and never reached thevertical strip on the first pass, leaving the selected title at 1.11:1. It runs
after now, and measures 14.46:1.
Known limit
This holds for one pane per tab.
PositionActiveBorderOverLeafframes theactive leaf, not the tab, so with a split the folder can join to nothing.
# 753tracks the two-border rework that makes the geometry deterministic, alongwith the spawn animation.
Stack
# 755zero config, this branch's base for the rest# 756folder tab and seam covers# 757seam diagnosticMerge in order. Each part is under the pr-gate size cap on its own.
Review
A subagent review over this branch found, and the second commit fixes: the
vertical seam clipping to the host rather than to the scrolling row list, so
the clamp did nothing and a scrolled-out row drew a bar into the title bar;
both covers surviving the quake strip being force-hidden; the layout retry
able to re-arm forever on a collapsed strip, and the layout-switch completion
asking the strip that had just left; no coalescing, so one new tab re-placed
the cover four to six times; and the two seam handlers not catching the COM
and null shapes XAML throws on a tree going down, from dispatcher callbacks
where an escape is unhandled.
It also produced a mechanism for the reported access violation inside
NavigationView.set_SelectedItem, and the fix for it. Read that commitmessage before trusting it: the crash still has no reproduction.