Skip to content

gpui: render deferred overlays above native child views - #24

Open
huacnlee wants to merge 17 commits into
mainfrom
gpui-webview-overlay
Open

gpui: render deferred overlays above native child views#24
huacnlee wants to merge 17 commits into
mainfrom
gpui-webview-overlay

Conversation

@huacnlee

@huacnlee huacnlee commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Why

Native child views such as WKWebView and Windows composition visuals are hosted outside GPUI's normal scene. A native view placed above GPUI's primary render surface also covers deferred GPUI content, so popovers, dialogs, menus, and other window-level overlays cannot appear above it.

This PR introduces an opt-in three-plane composition model:

                  Front / closest to the user

  ┌─────────────────────────────────────────────────────┐
  │ GPUI overlay surface (transparent)                  │
  │ deferred draws: Popover, Dialog, menus, tooltips    │
  ├────────────── overlay_scene_start ──────────────────┤
  │ Native surface                                     │
  │ WKWebView / IDCompositionVisual                     │
  ├─────────────────────────────────────────────────────┤
  │ GPUI base surface                                   │
  │ root scene, layout, editor chrome                   │
  └─────────────────────────────────────────────────────┘

                  Back / window background

The root UI remains on the existing renderer, while deferred and window-level draws are replayed onto the transparent surface above the native content.

Why this is significant

This change is significant because it gives GPUI a general composition boundary for content that GPUI does not render itself. GPUI can continue to own the application shell, layout, commands, and overlays, while a specialized native surface renders in the middle of the same visual stack.

A WebView is the first concrete use case, but the same architecture can provide a foundation for other embedded surfaces, for example:

  • richer Markdown Preview and other HTML-based previews
  • native video, audio visualization, or camera playback surfaces
  • PDF and platform document viewers
  • maps and other SDK-provided native views
  • GPU, 3D, or data-visualization canvases with their own rendering pipeline
  • platform-specific tools, third-party SDK UI, or extension-provided surfaces

This PR does not make all of those integrations complete by itself; each one still needs its own lifecycle, input, focus, and platform adapter. The important first step is now demonstrated: GPUI and an independently rendered native surface can work as one composed interface, including GPUI content both below and above it.

If this foundation proves reliable, Zed could build on it to embed WebView-backed experiences such as a richer Markdown Preview and other HTML preview surfaces. macOS and Windows both have mature WebView implementations that make this direction practical. Linux is likely to require more platform work because its display-server, desktop, toolkit, and WebKit environments are more varied. That makes Linux support harder, but not a fundamental blocker; it can be solved in follow-up work.

API

  • Window::enable_scene_overlay() enables layered scene presentation for a window. Without this opt-in, platforms continue using the existing single-surface draw path.
  • Window::create_native_surface() creates a platform-native slot between the GPUI base and overlay planes. The initial implementation exposes this portal on Windows.
  • PlatformNativeSurface is the platform-neutral handle for a native slot:
    • set_bounds synchronizes its geometry in device pixels.
    • set_visible includes or excludes it from composition.
    • platform_handle exposes the platform attachment object, such as an IDCompositionVisual.

Key implementation points

  • Frame::overlay_scene_start records the scene boundary immediately before deferred draws are painted.
  • PlatformWindow::draw_layered(scene, overlay_start) presents the base and overlay ranges separately. Its default implementation preserves the previous single-surface behavior on unsupported platforms.
  • Scene::is_empty() checks for drawable primitives rather than bookkeeping operations. The macOS overlay uses this to capture input only while visible overlay content exists; otherwise events pass through to the native view.
  • macOS new_overlay_renderer creates a transparent CAMetalLayer renderer that shares the base renderer's Metal device and sprite atlas. AppKit places this layer above native child views.
  • Windows builds a DirectComposition tree ordered as base visual, native portal container, and overlay visual. The overlay has its own transparent swap chain, while DirectCompositionPortal implements PlatformNativeSurface.

Example

cargo run -p gpui --example native_webview

The macOS-only example embeds WKWebView directly, without adding a wry dependency to GPUI. It demonstrates:

  • GPUI Popover and Dialog content above the live WebView
  • outside-click dismissal even when the click is geometrically over the WebView
  • focus transfer between GPUI and WebView keyboard input
  • WebView resizing with the window
  • native CALayer border and rounded clipping

Companion component integration and documentation: longbridge/gpui-component#2626

Current scope

  • macOS: layered GPUI overlay with a directly hosted native child view
  • Windows: DirectComposition native-surface portal and layered renderer support
  • Linux: deferred because current wry/WebKitGTK integration is not suitable for this iteration

WebView-specific focus and clipboard behavior remains in the embedding component so the GPUI API stays focused on rendering and native-surface composition.

Test Plan

  • cargo test -p gpui scene::tests --lib
  • cargo check -p gpui -p gpui_macos
  • cargo check -p gpui --example native_webview
  • manually verified Popover and Dialog rendering above a live WKWebView
  • manually verified overlay dismissal from clicks over the WebView
  • manually verified focus transfer and keyboard input between GPUI and WKWebView
  • manually verified native WebView resizing, border, and rounded clipping

Add a transparent macOS GPUI overlay surface that renders deferred window content above embedded native child views while preserving WebView input when the overlay is empty.

Co-authored-by: Codex <codex@openai.com>
huacnlee and others added 9 commits July 30, 2026 22:15
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Split DirectComposition into base, native portal, and transparent overlay visuals. Add a platform-neutral native surface handle for binding WebView2 composition controllers.

Co-authored-by: Codex <codex@openai.com>
@huacnlee

Copy link
Copy Markdown
Owner Author

Windows implementation checkpoint: deafa650ce adds a DirectComposition base / native portal / transparent overlay visual tree, a second premultiplied-alpha swap chain, and a platform-neutral native-surface handle. The corresponding wry CompositionController path is on longbridge/wry:gpui-composition-controller (2d0b3e2), and the component integration is in longbridge/gpui-component#2626 (4e560a95). This checkpoint still requires native Windows compilation and runtime validation; pointer/focus/IME forwarding and device-loss rebinding are not yet complete.

huacnlee and others added 7 commits July 30, 2026 23:36
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.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