Window shells (application layout paradigms) (1.32)¶
Independent top-level hosts in QWinUI3.Extras share ShellWindow
(chrome + WindowHelper) — they do not subclass StandardWindow.
Chrome reliability (DPI, backdrop, dialog owners): window-chrome.md.
Title-bar slots & hit-test recipes: title-bar-cookbook.md (2.05).
Geometry recipe: window-helper.md.
High-DPI / multi-monitor matrix: high-dpi.md (1.58).
Linux matrix detail: platform-linux-wayland.md.
Frost / RHI: graphics-backend.md.
Gallery: Window shells (WindowParadigmPage) · High-DPI & monitors · Multi-window (MultiWindowPage, 1.56) · Main host uses BackdropSolid + geometryPersistenceKey: "GalleryMain".
Runnable sample: examples/multi-window.
ShellWindow vs StandardWindow¶
ShellWindow (Extras) |
StandardWindow (Platform) |
|
|---|---|---|
| Audience | App layouts / workbench shells | Gallery host + low-level AppWindow |
| Chrome | WindowChrome + WinUI TitleBar slots |
PlatformTitleBar (caption host) |
| Layout helpers | Blank / Nav / MenuStatus / Dialog / Tool / Overlay | DialogWindow / ToolWindow / CompactOverlayWindow |
| Typical use | Ship product UI with title / navModel / Menu |
Custom AppWindow presenter / backdrop experiments |
Prefer ShellWindow family for applications. Keep StandardWindow when you need Platform presenters without Extras.
Win + Linux soak matrix (1.32)¶
Re-checked against Gallery Window paradigm page + Platform resolveBackdrop / geometry clamp.
| Surface | Windows | Linux (Wayland/X11) | Ship note |
|---|---|---|---|
StandardWindow + BackdropSolid |
Works | Works | Gallery default; safest product chrome |
ShellWindow / NavigationWindow + Solid |
Works | Works | Prefer for apps (NavigationWindow defaults Solid) |
BlankWindow / MenuStatus / Dialog / Tool / Overlay shells |
Works | Works | Roles via paradigm / presenter APIs |
BackdropMica / MicaAlt / Acrylic |
Works (DWM) | Coerced → Solid | Paint with effectiveBackdrop; pin OpenGL for frost — graphics-backend.md |
BackdropTransparent / None |
DIY fill | DIY fill | No system material |
geometryPersistenceKey |
Works | Works | Restore clamps to available screens (see below) |
| NC hit-test / Snap Layouts | Works | Unsupported | QML caption handles input on Linux |
Bootstrap configureEnvironment |
Required early | Required early | Calls configurePlatformEnvironment + style / IME |
Do not ship Mica as a Linux feature — copying a Windows sample is fine; the platform coerces.
Geometry persistence (supported recipe)¶
Set a non-empty key on either host:
StandardWindow {
backdrop: WindowHelper.BackdropSolid
geometryPersistenceKey: "MainWindow"
}
ShellWindow {
geometryPersistenceKey: "MainWindow"
}
Behavior (1.32):
- Debounced save on resize/move; always save on close.
- Stores normal frame + maximized vs windowed + optional screen name under
QSettings→WindowGeometry/<key>. - Restore runs
clampGeometryToScreens: prefer saved screen → any intersecting screen → primary center; reject frames smaller than 160×120; fit insideavailableGeometry(taskbar-safe). - Empty key = off.
clearSavedGeometry()/WindowHelper.clearWindowGeometry(key)forgets the entry.
Full API notes: window-helper.md.
Multi-window & secondary shells (1.56)¶
Integrators often open an inspector / preview / settings tool beside the main frame. Prefer one process, shared Theme, and distinct geometry keys — not a second app instance.
| Role | Prefer | Geometry key | Ownership |
|---|---|---|---|
| App chrome | NavigationWindow / ShellWindow |
e.g. "MyAppMain" |
Primary |
| Inspector / palette | ToolShellWindow |
e.g. "MyAppTool" |
Independent top-level |
| True dialog HWND | DialogShellWindow / DialogWindow |
Optional (often centered) | ownerWindow + openDialog() → setTransientParent |
| In-page confirm | ContentDialog |
n/a | Same window Overlay — dialogs-flyouts.md |
ShellWindow {
id: main
geometryPersistenceKey: "MyAppMain"
backdrop: WindowHelper.BackdropSolid
ToolShellWindow {
id: tool
geometryPersistenceKey: "MyAppTool"
backdrop: WindowHelper.BackdropSolid
visible: false
}
DialogShellWindow {
id: dlg
ownerWindow: main
backdrop: WindowHelper.BackdropSolid
}
}
tool.visible = true
dlg.openDialog(main) // setTransientParent + centerOnScreen + show
Shared Theme¶
Theme is a process-wide singleton. Toggling Theme.dark / accent in any window updates every shell. Do not create a second QGuiApplication just for a tool window.
Win + Linux notes¶
| Topic | Windows | Linux (Wayland / X11) |
|---|---|---|
| Solid chrome | Preferred | Preferred (Mica coerced → Solid) |
| Transient parent | Stacks dialog with owner | Call anyway; compositor may still own stacking |
| Geometry keys | Unique per role | Same; restore clamps to availableGeometry |
| Bootstrap | configureEnvironment early |
Required for CSD / DPI — platform-linux-wayland.md |
Failure modes (dialog behind main, off-screen restore): window-chrome.md.
Field harden (2.14)¶
| Check | Why |
|---|---|
openDialog(owner) not raw visible = true |
Wires setTransientParent + centerOnOwner |
| Owner visible before dialog | Parent surface must exist on Wayland |
WindowHelper.ensureWindowCreated before first show |
Realizes handle when spawning from Component |
centerOnOwner not primary screen |
Dialog lands on owner's monitor |
| Portal readout | WindowHelper.portalParentWindow(owner) — security-trust.md Wayland regression |
Platform code (2.14): setTransientParent calls ensureWindowCreated on both child and parent (same realize path as portal parent_window 1.79).
Runnable sample: examples/multi-window (qwinui3_example_multi_window). Gallery: Multi-window.
Onboarding + z-order (2.43)¶
When the app also runs a first-run coach (1.55):
| Check | Why |
|---|---|
| Coach on main shell only | Tips anchor to primary NavigationWindow / ShellWindow |
| Defer until main visible | Avoid tips on a hidden or zero-size window |
Pause tour before openDialog |
Secondary shell mid-step breaks focus + stacking |
| Onboarding Settings category | Not WindowGeometry/* — multi-window-onboarding.md |
Shared chrome API¶
title: qsTr("App")
subtitle: qsTr("Optional")
symbol: FluentIcons.Home
preferredHeightOption: WindowHelper.TitleBarHeightTall
isBackButtonVisible: true
rightHeader: Button { text: qsTr("Account") }
captionButtonHover: Theme.fillSubtle
titleBarBackground: Theme.bgAcrylic
Shell types¶
| Type | Notes |
|---|---|
BlankWindow |
Empty client |
NavigationWindow |
NavigationView hostContent + pane modes |
MenuStatusWindow |
menusInTitleBar, multi-segment StatusBar |
DialogShellWindow |
Dialog paradigm (WindowHelper.ParadigmDialog) |
ToolShellWindow |
Tool / palette paradigm |
CompactOverlayShellWindow |
Compact overlay presenter |
Dialog / Tool / Overlay snippets¶
DialogShellWindow {
id: dlg
title: qsTr("Confirm")
ownerWindow: mainWindow
width: 440; height: 280
}
dlg.openDialog()
ToolShellWindow {
title: qsTr("Inspector")
width: 320; height: 480
}
CompactOverlayShellWindow {
title: qsTr("Now playing")
width: 360; height: 200
}
Gallery demos: WindowParadigmPage.
NavigationWindow / NavigationView¶
paneDisplayMode: left | leftCompact | leftMinimal | top | auto
(auto switches left ↔ leftCompact at autoCompactThreshold, default 1008.)
leftMinimal: pane is a light-dismiss overlay (does not push content).- Pane search:
isPaneSearchEnabled+paneSearchModel paneHeader/paneFooterslots- Item
badge/badgeValue→InfoBadge - Drag reorder:
isReorderable+onModelReordered - Keyboard Home/End/type-ahead; compact flyout ↑↓ Enter Esc
- Top overflow
…lists only clipped items
Extractable app shell (1.50)¶
Prefer examples/gallery-shell over copying Gallery:
NavigationWindow {
geometryPersistenceKey: "MyAppMain"
hostContent: false
pageModule: "MyApp"
paneDisplayMode: "auto"
footerText: qsTr("Settings")
footerComponent: "SettingsPage"
navModel: [
{ type: "item", key: "home", title: qsTr("Home"),
symbol: FluentIcons.Home, component: "HomePage" }
]
}
| Mode | When |
|---|---|
hostContent: true (default) |
Single content: slot |
hostContent: false + pageModule |
Gallery-style StackView pages + footer |
Gallery Main.qml enables auto, pane search, badges, and reorder as the living sample. More: navigation.md.
StatusBar¶
StatusBar {
text: qsTr("Ready")
progress: 0.4
centerContent: Label { text: qsTr("Ln 12") }
content: Label { text: qsTr("UTF-8") }
}
ContentDialog queue¶
ContentDialog.show() enqueues via ContentDialogQueue (one visible dialog at a time). Full FIFO / owner / Esc recipe: dialogs-flyouts.md (1.48).
ContentDialogQueue.show(dialogA)
ContentDialogQueue.cancel(dialogA) // drop pending
ContentDialogQueue.clearQueue() // drop all pending
ContentDialogQueue.replaceCurrent(dialogB) // close active without pumping; open B; queue resumes after
Theme tokens (WinUI-aligned)¶
| Token | Typical WinUI | QWinUI3 |
|---|---|---|
| Control corner | 4px | Theme.cornerControl |
| Overlay / flyout corner | 8px | Theme.cornerOverlay / cornerCard |
| Focus outer / inner | 2px + 1px | strokeFocusOuter / strokeFocusInner + focusOuter / focusInner |
| Nav pane expanded | ~320 / 280 | navPaneWidth (280) |
| Nav compact | 48 | navPaneCompactWidth |
| Control padding | 12×7 | paddingControlH / paddingControlV |
| Spacing scale | 8 / 12 / 24 | spacing / spacingLoose / spacingSection |
Accessibility: Theme.followSystemAccessibility (default true) is applied by
ThemeSync on StandardWindow / ShellWindow (1.69) — copies
WindowHelper.systemReducedMotion / systemHighContrast into
Theme.reducedMotion / Theme.highContrast. Override from
ThemeAppearanceSettings when follow is off. Not Gallery-only.
Startup (Bootstrap)¶
Prefer one-call bootstrap before QGuiApplication:
#include "Bootstrap.h"
QWinUI3::configureEnvironment(argv[0]); // style + Wayland/DPI + QPA sanitize
That wraps WindowHelper::configurePlatformEnvironment. Manual configurePlatformEnvironment alone still works for Linux CSD/DPI but skips style / Windows QPA sanitize / IME unset — see packaging-consumer.md.