Overlay the hover-revealed tab bar instead of reflowing content - #14947
Overlay the hover-revealed tab bar instead of reflowing content#14947bobrnor wants to merge 1 commit into
Conversation
|
Every PR must be linked to a same-repo issue before Oz can review it. This PR is linked to #14945, but no linked issue is marked See the contribution guidelines for the full readiness model. Powered by Oz |
There was a problem hiding this comment.
Every PR must be linked to a same-repo issue before Oz can review it.
This PR is linked to #14945, but no linked issue is marked ready-to-implement yet. Only repository maintainers apply that label, so please wait for a maintainer to mark the issue. Once it is marked, push a new commit or comment /oz-review to re-trigger review.
See the contribution guidelines for the full readiness model.
Powered by Oz
…4945, GH#10784) While the tab bar is hidden, a 12px strip anchored at the window's top-left reveals it on hover. The revealed bar was added to the content column, which is inset by WORKSPACE_PADDING, so it started one pixel below the strip that opened it. Measured at a 1024x768 window: the strip covers RectF(<0, 0, 1024, 12>) while the bar covered RectF(<1, 1, 1023, 36>). The top pixel row therefore revealed the bar and then sat outside it. In fullscreen the cursor clamps to y=0 at the screen edge, which is exactly that band, so the bar oscillated open and closed with the pointer standing still: build_scene replays the last pointer position as a synthetic MouseMoved after every frame, and there is no mouse-exit event to invalidate it. Every flip reflowed the panes by the height of the bar, jumping the whole terminal about twice a second. Add ShowTabBar::Overlay, used for every hover reveal: the bar is positioned at the window's top-left over the content rather than taking a row in the column. It now covers RectF(<0, 0, 1024, 35>), a superset of the strip, so the pointer that opened it stays over it, and revealing it no longer moves any pane. That also addresses GH#10784, where the reflow pushed a tmux top status line out from under the cursor. Because the overlay no longer sits in the column, it stretches itself to the full window width (a positioned stack child is laid out with a zero minimum width) and paints its own background, flattened to a solid color since theme.background() may be a gradient whose stops would be squeezed into the strip. Reveal now takes a 250ms hover-in delay so a sweep across the top of the window doesn't pop the bar open. Hiding stays undelayed on purpose: Hoverable drops the redraw for a state change whose previous change was also synthetic, and with both delays every transition resolves on the synthetic MouseMoved replayed after a frame, so the bar would flip to hidden without scheduling the frame that repaints it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CoefEcZFpauaaUcSVANoMn
898e256 to
e10f4bd
Compare
Description
In fullscreen, a hover-revealed tab bar flickered open and closed while the pointer stood still at the top edge of the screen, reflowing every pane by the height of the bar on each flip.
The point that revealed the bar was not inside the region that kept it revealed. The reveal strip is a positioned stack child anchored at the window's top-left,
TAB_BAR_HOVER_HEIGHT(12) tall. The revealed bar was added to the content column, which is inset byWORKSPACE_PADDING, so it started one pixel lower. Measured from the rendered scene at a 1024x768 window:RectF(<0, 0, 1024, 12>)RectF(<1, 1, 1023, 36>)RectF(<0, 0, 1024, 35>)The top pixel row revealed the bar and then sat outside it. In fullscreen the cursor clamps to
y = 0at the screen edge, which is exactly that band, so the bar oscillated with no mouse movement at all:build_scenereplays the last pointer position as a syntheticMouseMovedafter every frame, and there is no mouse-exit event to invalidate that stored position.This adds
ShowTabBar::Overlay, used for every hover reveal. The bar is positioned at the window's top-left over the content instead of taking a row in the column, so it now covers a superset of the strip — the pointer that opened it stays over it — and revealing it no longer moves any pane. Two things follow from leaving the column: the overlay stretches itself to the full window width (a positioned stack child is laid out with a zero minimum width), and it paints its own background, flattened to a solid color becausetheme.background()may be a gradient whose stops would otherwise be squeezed into a 35 px strip.Revealing now takes a 250 ms hover-in delay, so a sweep across the top of the window no longer pops the bar open. Hiding deliberately stays undelayed:
Hoverabledrops the redraw for a state change whose previous change was also synthetic, and with both delays set every transition resolves on the syntheticMouseMovedreplayed after a frame — the bar would flip to hidden without scheduling the frame that repaints it. That constraint is recorded next to the constant.Linked Issue
Fixes #14945.
Related: #10784 is the same mechanism seen from the other side — the reveal pushing content down made a tmux top status line escape the cursor, so chasing it re-triggered the bar. This removes that chase: content no longer moves when the bar appears, and the 250 ms hover-in delay means a deliberate move to click the status line lands before the bar reveals at all. It is not a complete fix for that report, though — once the bar is revealed it sits over the terminal's top row, so a tmux status line there is covered while the bar is up. Leaving #10784 open for that remainder.
ready-to-specorready-to-implement. — Fullscreen tab bar flickers open and closed when the pointer rests at the top edge of the screen #14945 was filed alongside this PR and has not been triaged yet.Testing
Added
test_hover_revealed_tab_bar_covers_its_hover_strip, which renders both states and asserts the invariant the bug violated: the revealed bar must cover the strip that reveals it. It also asserts the overlay stays a strip at the top rather than growing into the full-window constraint it is now laid out against.Both assertions were verified to be capable of failing, not just passing:
hovered_visibilityreverted toStacked, the coverage assertion fails withrevealed tab bar RectF(<1, 1, 1023, 36>) must cover the hover strip RectF(<0, 0, 1024, 12>)— this is where the measurements above come from.RectF(<0, 0, 1024, 35>), confirming it is a top strip and that the bound is not vacuous.Existing coverage only asserted the
tab_bar_modeenum, never the geometry, which is why this survived.Also run:
cargo test -p warp --lib workspace::(209 passed), the fullcargo test -p warp --libsuite compared against a clean-tree baseline (identical failure set; the one extra failure floats between different tests insecret_redactionacross runs on unchanged code),cargo clippy -p warp --all-targets --tests -- -D warnings, andcargo fmt../script/run.Not manually verified in the GUI — this needs a human pass before merge. Worth checking specifically: the revealed bar looks right over a transparent background and under a gradient theme, hiding feels right now that it is immediate rather than delayed by 500 ms, and dragging a tab out of the revealed bar still behaves.
Agent Mode
🤖 Generated with Claude Code
https://claude.ai/code/session_01CoefEcZFpauaaUcSVANoMn