`ViewportBuilder::with_visible(false)` doesn't work on root viewport
Problem
Consider the example below where a window tab can be torn off and merged into another window, just like in Chromium-based browsers.
When egui's root window with a single tab merges into another window, it has to be destroyed¹, but we can't destroy the root window, so the UI below tries to hide the root window and render all windows as deferred viewports:
This however, still produces a visible root window (the blank "Steady Controller" window in the above gif):
let native_options = eframe::NativeOptions {
...
viewport: egui::ViewportBuilder::default()
.with_title(app_name)
.with_visible(false) // <-- request not to show the root viewport
.with_active(false)
.with_inner_size([1.0, 1.0]),
..Default::default()
};This happens because EpiIntegration::post_rendering unconditionally calls window.set_visible(true) after the first rendered frame, overriding the root viewport's visible(false) request.
Furthermore, if we minimize the blank root viewport, the remaining GUI will be throttled to 100 ms repaint interval.
Solution
Ideally eframe supports an opt-in mode where ViewportId::ROOT remains the logical application/controller root but never owns a native window or rendering surface.
let native_options = eframe::NativeOptions {
root_viewport_mode: eframe::RootViewportMode::Windowless,
...
};I have a working prototype in my fork, but it turned into a 2.5k-line change. eframe currently couples the logical root to both the primary winit window and renderer bootstrap, so making it truly windowless affects WGPU/Glow initialization, repaint scheduling, viewport creation, platform output, and zero-window shutdown. Before submitting it, I'd appreciate guidance on whether this should be split into preparatory PRs or kept as one feature PR.
- an alternative slight of hand solution like making the source root window take over all the target window's tabs and state on tab merge doesn't really work seamlessly: at some point during that shenanigan, before the deferred target window can be destroyed, two windows will appear on top of each other with their shadows stacked, resulting in an unwanted visual artifact
Source: emilk/egui