ui: Add deferred_foreground layer for active borders and focus rings#2289
Draft
huacnlee wants to merge 2 commits into
Draft
ui: Add deferred_foreground layer for active borders and focus rings#2289huacnlee wants to merge 2 commits into
huacnlee wants to merge 2 commits into
Conversation
Introduces a `deferred_foreground()` API that defers element painting to a foreground layer above all content but below Sheet/Dialog/Notification/Tooltip. This fixes active borders on `ListItem` and `Table` cells/rows being covered by sibling elements painted later in document order, and focus rings via `focus_ring()` in `styled.rs`. Key changes: - `deferred_foreground.rs`: New `DeferredForeground` element captures scroll-clip context and queues child painting; `ForegroundLayer` flushes the global queue during prepaint/paint - `root.rs`: Root now renders Sheet/Dialog/Notification layers internally in the correct stacking order (content → ForegroundLayer → Sheet → Dialog → Notification → Tooltip); `render_sheet/dialog/notification_layer()` become no-ops for backward compatibility - `list_item.rs`, `table/state.rs`: Wrap active-border divs in `deferred_foreground()` - `styled.rs`: `focus_ring()` uses `deferred_foreground()` Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rewrite DeferredForeground to store only non-arena types (bounds, corner radii, border color/width) so the global draw queue never holds arena-backed AnyElement references across prepaint → paint, eliminating the arena panic. - ForegroundLayer now paints quad borders directly via window.paint_quad() instead of re-painting child elements. - Fix focus_ring helper: border properties were incorrectly set on the layout-only child div (ignored); move them onto DeferredForeground via its builder API so the ring is actually visible. - Input: add focus_ring(px(0.)) alongside focused_border so the focused border ring is painted by ForegroundLayer after all siblings, preventing adjacent Input/Button elements from covering the focus indicator. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
Summary
deferred_foreground()— a new element API that defers child painting to a foreground layer drawn above all regular content, but below Sheet/Dialog/Notification/Tooltip overlaysListItemandTableselected rows/cells being painted over by sibling elements (GPUI paints in document order, so later siblings cover earlier borders)focus_ring()instyled.rssuffering the same issueRootitself with a correct stacking order, removing the need for users to callrender_sheet/dialog/notification_layer()manually (those methods are now no-ops for backward compatibility)Architecture
The new layer stack within a window:
DeferredForeground::prepaintcaptures the current scroll clip (ContentMask) and absolute offset, then enqueues the child into a globalForegroundDrawQueue.ForegroundLayer::prepaint/paintdrains the queue and replays each child with its captured context, ensuring correct clipping inside scroll containers.Test plan
🤖 Generated with Claude Code