Skip to content

ui: Add deferred_foreground layer for active borders and focus rings#2289

Draft
huacnlee wants to merge 2 commits into
mainfrom
active-border
Draft

ui: Add deferred_foreground layer for active borders and focus rings#2289
huacnlee wants to merge 2 commits into
mainfrom
active-border

Conversation

@huacnlee
Copy link
Copy Markdown
Member

Summary

  • Introduces 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 overlays
  • Fixes active borders on ListItem and Table selected rows/cells being painted over by sibling elements (GPUI paints in document order, so later siblings cover earlier borders)
  • Fixes focus_ring() in styled.rs suffering the same issue
  • Moves Sheet/Dialog/Notification rendering into Root itself with a correct stacking order, removing the need for users to call render_sheet/dialog/notification_layer() manually (those methods are now no-ops for backward compatibility)

Architecture

The new layer stack within a window:

self.view (user content)
  └─ list items, table cells, buttons, etc.
ForegroundLayer          ← deferred_foreground items flush here
Sheet layer
Dialog layer
Notification layer
Tooltip overlay

DeferredForeground::prepaint captures the current scroll clip (ContentMask) and absolute offset, then enqueues the child into a global ForegroundDrawQueue. ForegroundLayer::prepaint/paint drains the queue and replays each child with its captured context, ensuring correct clipping inside scroll containers.

Test plan

  • Select a list item — active border should be visible above adjacent items
  • Select a table row/cell — border should not be covered by sibling cells
  • Focus a button — focus ring should render above siblings
  • Open a notification — it should appear above selected-item borders
  • Open a dialog — dialog should appear above all content including borders
  • Open a sheet panel — sheet should appear above borders

🤖 Generated with Claude Code

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>
@huacnlee huacnlee marked this pull request as draft April 24, 2026 12:21
- 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>
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.

1 participant