Skip to content

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):

  1. Debounced save on resize/move; always save on close.
  2. Stores normal frame + maximized vs windowed + optional screen name under QSettingsWindowGeometry/<key>.
  3. Restore runs clampGeometryToScreens: prefer saved screen → any intersecting screen → primary center; reject frames smaller than 160×120; fit inside availableGeometry (taskbar-safe).
  4. 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.

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 / paneFooter slots
  • Item badge / badgeValueInfoBadge
  • 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.