Keep overlay surfaces mapped so they open sharp (#13419)

The menu, emoji picker, clipboard, OSD, reminder flow and Wi-Fi QR code
mapped a fresh surface on every open. Qt drew its first frames before
Hyprland sent the surface's fractional scale: the pixel ratio stepped
2, 1, then 1.6, so on a 1.6 display each overlay showed blurry for
~350ms before going sharp.

A new OverlayWindow keeps the surface. Hidden, it parks as a 1x1,
input-less layer below windows, off the overlay layer so it never
blocks direct scanout. Showing only resizes and raises it, so the scale
is already settled. Content stays hidden until the surface has grown,
so no 1x1 frame is stretched across the screen, and the window follows
the focused monitor each time it is shown. The image picker's own
fullscreen parking moves onto it too.

Each parked overlay keeps a Qt window alive: at rest the shell holds
~40 MiB more RSS and ~20 MiB more GPU memory.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier HanssonandClaude Opus 5.5 authored and GitHub committed 2026-09-27 18:03:01 +02:00
1 parent 6762769bbf
commit 48de7823da
14 files changed
+164 -96

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
// A fullscreen overlay whose surface outlives each open. A fresh surface draws
// its first frames before the compositor sends its fractional scale, so an
// overlay mapped per open flashed blurry until the scale arrived. Closed, the
// surface parks as a 1x1, input-less, content-less layer below windows: small
// enough to cost nothing, off the overlay layer so it never blocks direct
// scanout for fullscreen apps. Opening only resizes and raises it.
PanelWindow {
id: window
// Whether the overlay is showing. Drive this instead of visible.
property bool shown: false
property int shownLayer: WlrLayer.Overlay
property int shownKeyboardFocus: WlrKeyboardFocus.Exclusive
// The surface no longer lands on the focused output by being mapped there,
// so it follows the focused monitor each time it is shown. Unset until the
// first show lets the compositor choose.
property var targetScreen: null
property Region emptyRegion: Region {}
function focusedScreen() {
var monitor = Hyprland.focusedMonitor
var name = monitor ? String(monitor.name || "") : ""
for (var i = 0; i < Quickshell.screens.length; i++) {
if (Quickshell.screens[i].name === name) return Quickshell.screens[i]
}
return null
}
onShownChanged: if (shown) targetScreen = focusedScreen() || targetScreen
visible: true
screen: targetScreen
anchors { top: true; left: true; bottom: shown; right: shown }
implicitWidth: 1
implicitHeight: 1
mask: shown ? null : emptyRegion
color: "transparent"
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: shown ? shownLayer : WlrLayer.Bottom
WlrLayershell.keyboardFocus: shown ? shownKeyboardFocus : WlrKeyboardFocus.None
// Draw nothing until the surface has actually grown. A frame drawn while it
// is still 1x1 holds only the scrim's color, which the compositor would
// stretch across the whole screen until the fullscreen frame arrives.
Binding {
target: window.contentItem
property: "visible"
value: window.shown && window.width > 1 && window.height > 1
}
}
+1
View File
@@ -15,6 +15,7 @@ KeyboardPanel 1.0 KeyboardPanel.qml
MultiSelect 1.0 MultiSelect.qml MultiSelect 1.0 MultiSelect.qml
NumberField 1.0 NumberField.qml NumberField 1.0 NumberField.qml
OpticalGlyph 1.0 OpticalGlyph.qml OpticalGlyph 1.0 OpticalGlyph.qml
OverlayWindow 1.0 OverlayWindow.qml
Panel 1.0 Panel.qml Panel 1.0 Panel.qml
PanelActionButton 1.0 PanelActionButton.qml PanelActionButton 1.0 PanelActionButton.qml
PanelController 1.0 PanelController.qml PanelController 1.0 PanelController.qml
+2 -7
View File
@@ -311,15 +311,10 @@ Item {
} }
} }
PanelWindow { OverlayWindow {
id: panel id: panel
visible: root.opened shown: root.opened
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
WlrLayershell.namespace: "omarchy-clipboard" WlrLayershell.namespace: "omarchy-clipboard"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
exclusionMode: ExclusionMode.Ignore
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+2 -7
View File
@@ -157,15 +157,10 @@ Item {
path: root.omarchyPath + "/shell/plugins/emojis/emojis.json" path: root.omarchyPath + "/shell/plugins/emojis/emojis.json"
onLoaded: root.loadEmojis(text()) onLoaded: root.loadEmojis(text())
} }
PanelWindow { OverlayWindow {
id: panel id: panel
visible: root.opened shown: root.opened
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
WlrLayershell.namespace: "omarchy-emojis" WlrLayershell.namespace: "omarchy-emojis"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
exclusionMode: ExclusionMode.Ignore
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+4 -30
View File
@@ -1,11 +1,11 @@
import Quickshell import Quickshell
import Quickshell.Hyprland
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import QtQuick import QtQuick
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Shapes import QtQuick.Shapes
import qs.Commons import qs.Commons
import qs.Ui
import "ImagePickerModel.js" as ImagePickerModel import "ImagePickerModel.js" as ImagePickerModel
Item { Item {
@@ -36,9 +36,6 @@ Item {
property string themeRows: "" property string themeRows: ""
property bool themeMode: false property bool themeMode: false
property bool themeOpenPending: false property bool themeOpenPending: false
// The surface outlives each open, so it no longer lands on the focused
// output by itself. Unset until the first open lets the compositor choose.
property var targetScreen: null
// Bound to the central [image-picker] section in shell.toml via Color.qml. // Bound to the central [image-picker] section in shell.toml via Color.qml.
// `dimColor` tints unselected slices and text outlines on top of the scrim; // `dimColor` tints unselected slices and text outlines on top of the scrim;
// it intentionally tracks the foundational background, not a surface role. // it intentionally tracks the foundational background, not a surface role.
@@ -234,7 +231,6 @@ Item {
requestSerial += 1 requestSerial += 1
themeMode = false themeMode = false
themeOpenPending = false themeOpenPending = false
targetScreen = focusedScreen() || targetScreen
imageDirs = nextImageDirs imageDirs = nextImageDirs
imageRows = nextImageRows imageRows = nextImageRows
@@ -278,14 +274,6 @@ Item {
property var imageArray: [] property var imageArray: []
function focusedScreen() {
var monitor = Hyprland.focusedMonitor
var name = monitor ? String(monitor.name || "") : ""
for (var i = 0; i < Quickshell.screens.length; i++) {
if (Quickshell.screens[i].name === name) return Quickshell.screens[i]
}
return null
}
function currentThemePreview() { function currentThemePreview() {
var name = String(themeNameFile.text() || "").trim() var name = String(themeNameFile.text() || "").trim()
@@ -456,25 +444,11 @@ Item {
onExited: root.releaseNextDoneFile() onExited: root.releaseNextDoneFile()
} }
PanelWindow { OverlayWindow {
id: panel id: panel
shown: root.opened
// Stay mapped between opens. A fresh surface renders its first frames shownKeyboardFocus: root.imagesLoaded ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
// before the compositor sends its fractional scale, so the picker flashed
// blurry, and it had to re-upload every thumbnail texture. Closed, it
// waits transparent and input-less on the bottom layer: anything left on
// the overlay layer would block direct scanout for fullscreen apps.
visible: true
screen: root.targetScreen
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
mask: root.opened ? null : closedMask
WlrLayershell.namespace: "omarchy-image-selector" WlrLayershell.namespace: "omarchy-image-selector"
WlrLayershell.layer: root.opened ? WlrLayer.Overlay : WlrLayer.Bottom
WlrLayershell.keyboardFocus: root.opened && root.imagesLoaded ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore
Region { id: closedMask }
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+5 -9
View File
@@ -1065,15 +1065,10 @@ Item {
if (root.guardsPending) Qt.callLater(function() { root.evaluateGuards() }) if (root.guardsPending) Qt.callLater(function() { root.evaluateGuards() })
} }
} }
PanelWindow { OverlayWindow {
id: panel id: panel
visible: root.opened && root.rowsLoaded shown: root.opened && root.rowsLoaded
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
WlrLayershell.namespace: "omarchy-menu" WlrLayershell.namespace: "omarchy-menu"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
exclusionMode: ExclusionMode.Ignore
// The card opens centered exactly as always. The first search keystroke // The card opens centered exactly as always. The first search keystroke
// or submenu move freezes the top line where it currently sits — from // or submenu move freezes the top line where it currently sits — from
@@ -1086,12 +1081,13 @@ Item {
readonly property int centeredTop: Math.max(Style.gapsOut, Math.round((height - root.cardHeight) / 2)) readonly property int centeredTop: Math.max(Style.gapsOut, Math.round((height - root.cardHeight) / 2))
readonly property int effectiveCardTop: cardTop >= 0 ? cardTop : centeredTop readonly property int effectiveCardTop: cardTop >= 0 ? cardTop : centeredTop
function freezeCardTop() { function freezeCardTop() {
if (visible && cardTop < 0) { if (shown && cardTop < 0) {
cardTop = effectiveCardTop cardTop = effectiveCardTop
maxRowsHeight = root.visibleRowsHeight maxRowsHeight = root.visibleRowsHeight
} }
} }
onVisibleChanged: if (!visible) { cardTop = -1; maxRowsHeight = -1 } // The surface stays mapped between opens, so closing is shown going false.
onShownChanged: if (!shown) { cardTop = -1; maxRowsHeight = -1 }
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+3 -7
View File
@@ -123,15 +123,11 @@ Item {
function ping(): string { return "ok" } function ping(): string { return "ok" }
} }
PanelWindow { OverlayWindow {
id: panel id: panel
visible: root.opened shown: root.opened
anchors { top: true; bottom: true; left: true; right: true } shownKeyboardFocus: WlrKeyboardFocus.None
color: "transparent"
WlrLayershell.namespace: "omarchy-osd" WlrLayershell.namespace: "omarchy-osd"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore
// Visual-only surface: keep the layer-shell input region empty so the OSD // Visual-only surface: keep the layer-shell input region empty so the OSD
// never blocks clicks to the desktop below it. // never blocks clicks to the desktop below it.
mask: Region {} mask: Region {}
+2 -7
View File
@@ -210,14 +210,9 @@ Item {
} }
} }
PanelWindow { OverlayWindow {
visible: root.opened shown: root.opened
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
exclusionMode: ExclusionMode.Ignore
WlrLayershell.namespace: "omarchy-network-qr" WlrLayershell.namespace: "omarchy-network-qr"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
// Deep scrim: the floating code needs the backdrop to carry the contrast // Deep scrim: the floating code needs the backdrop to carry the contrast
// on any wallpaper. // on any wallpaper.
+2 -7
View File
@@ -92,15 +92,10 @@ Item {
} }
} }
PanelWindow { OverlayWindow {
id: panel id: panel
visible: root.opened shown: root.opened
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
WlrLayershell.namespace: "omarchy-reminders" WlrLayershell.namespace: "omarchy-reminders"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
exclusionMode: ExclusionMode.Ignore
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
+3 -2
View File
@@ -81,8 +81,9 @@ layer_absent() {
! layer_present "$1" ! layer_present "$1"
} }
# The image selector stays mapped between opens, parked on the bottom layer, so # Overlays built on OverlayWindow (the menu, image selector, emoji picker,
# it keeps its textures and fractional scale. Showing means on the overlay. # clipboard and the like) stay mapped between opens, parked 1x1 on the bottom
# layer, so they keep their fractional scale. Showing means on the overlay.
layer_on_overlay() { layer_on_overlay() {
hyprctl -j layers | jq -e --arg ns "$1" '[.[].levels["3"][]? | select(.namespace == $ns)] | length > 0' hyprctl -j layers | jq -e --arg ns "$1" '[.[].levels["3"][]? | select(.namespace == $ns)] | length > 0'
} }
+2 -2
View File
@@ -63,7 +63,7 @@ source_file=$DEFAULTS_FILE
original_position=$(jq -r '.bar.position // "top"' "$source_file") original_position=$(jq -r '.bar.position // "top"' "$source_file")
omarchy-shell shell summon omarchy.menu '{"menu":"root"}' >/dev/null omarchy-shell shell summon omarchy.menu '{"menu":"root"}' >/dev/null
wait_until "root menu opens" 15 layer_present "omarchy-menu" wait_until "root menu opens" 15 layer_on_overlay "omarchy-menu"
wait_until "root menu content is visible" 15 screen_contains "Apps" wait_until "root menu content is visible" 15 screen_contains "Apps"
screenshot "success-menu-01-root" screenshot "success-menu-01-root"
@@ -86,7 +86,7 @@ screenshot "success-menu-05-position-submenu"
wtype -k Down -k Down -k Return wtype -k Down -k Down -k Return
wait_until "menu bar position changes to left" 20 bar_position_is "left" wait_until "menu bar position changes to left" 20 bar_position_is "left"
wait_until "menu bar becomes vertical" 20 bar_is_vertical wait_until "menu bar becomes vertical" 20 bar_is_vertical
wait_until "menu closes after selecting a position" 15 layer_absent "omarchy-menu" wait_until "menu closes after selecting a position" 15 layer_off_overlay "omarchy-menu"
screenshot "success-menu-06-bar-left" screenshot "success-menu-06-bar-left"
if ((config_existed)); then if ((config_existed)); then
+10 -10
View File
@@ -24,12 +24,12 @@ open_and_close() {
# Search and select an emoji. The host harness separately proves the shortcut # Search and select an emoji. The host harness separately proves the shortcut
# with a QMP hardware key chord, while this test focuses on UI behavior. # with a QMP hardware key chord, while this test focuses on UI behavior.
omarchy-shell shell summon omarchy.emojis >/dev/null omarchy-shell shell summon omarchy.emojis >/dev/null
wait_until "emoji picker opens" 15 layer_present "omarchy-emojis" wait_until "emoji picker opens" 15 layer_on_overlay "omarchy-emojis"
wtype "rocket" wtype "rocket"
sleep 1 sleep 1
screenshot "success-emoji-picker-search" screenshot "success-emoji-picker-search"
wtype -k Return wtype -k Return
wait_until "emoji picker selection closes" 15 layer_absent "omarchy-emojis" wait_until "emoji picker selection closes" 15 layer_off_overlay "omarchy-emojis"
# Seed two clipboard entries, search for the older one, and copy it back out. # Seed two clipboard entries, search for the older one, and copy it back out.
clipboard_token="Omarchy acceptance clipboard $(date +%s)" clipboard_token="Omarchy acceptance clipboard $(date +%s)"
@@ -39,21 +39,21 @@ printf '%s' "clipboard decoy" | wl-copy
sleep 1 sleep 1
omarchy-shell shell summon omarchy.clipboard >/dev/null omarchy-shell shell summon omarchy.clipboard >/dev/null
wait_until "clipboard opens" 15 layer_present "omarchy-clipboard" wait_until "clipboard opens" 15 layer_on_overlay "omarchy-clipboard"
wtype "$clipboard_token" wtype "$clipboard_token"
wait_until "clipboard search finds test text" 15 screen_contains "Omarchy acceptance clipboard" wait_until "clipboard search finds test text" 15 screen_contains "Omarchy acceptance clipboard"
screenshot "success-clipboard-search" screenshot "success-clipboard-search"
wtype -M shift -k Return -m shift wtype -M shift -k Return -m shift
wait_until "clipboard selection closes" 15 layer_absent "omarchy-clipboard" wait_until "clipboard selection closes" 15 layer_off_overlay "omarchy-clipboard"
wait_until "clipboard selection restores test text" 15 bash -c '[[ $(wl-paste --no-newline) == "$1" ]]' _ "$clipboard_token" wait_until "clipboard selection restores test text" 15 bash -c '[[ $(wl-paste --no-newline) == "$1" ]]' _ "$clipboard_token"
# Exercise the system branch without invoking any destructive action. # Exercise the system branch without invoking any destructive action.
omarchy-shell shell summon omarchy.menu '{"menu":"system"}' >/dev/null omarchy-shell shell summon omarchy.menu '{"menu":"system"}' >/dev/null
wait_until "system menu opens" 15 layer_present "omarchy-menu" wait_until "system menu opens" 15 layer_on_overlay "omarchy-menu"
wait_until "system menu content is visible" 15 screen_contains "Shutdown" wait_until "system menu content is visible" 15 screen_contains "Shutdown"
screenshot "success-system-menu" screenshot "success-system-menu"
wtype -k Escape wtype -k Escape
wait_until "system menu closes" 15 layer_absent "omarchy-menu" wait_until "system menu closes" 15 layer_off_overlay "omarchy-menu"
# Preview both visual selectors and cancel without changing user state. These # Preview both visual selectors and cancel without changing user state. These
# cover thumbnail generation, the image-grid overlay, and current selection. # cover thumbnail generation, the image-grid overlay, and current selection.
@@ -74,7 +74,7 @@ wait_until "theme selector closes" 15 layer_off_overlay "omarchy-image-selector"
# Walk the reminder flow through each input screen, but dismiss before it # Walk the reminder flow through each input screen, but dismiss before it
# schedules a real timer in the test user's session. # schedules a real timer in the test user's session.
omarchy-shell shell summon omarchy.reminders >/dev/null omarchy-shell shell summon omarchy.reminders >/dev/null
wait_until "reminder flow opens" 15 layer_present "omarchy-reminders" wait_until "reminder flow opens" 15 layer_on_overlay "omarchy-reminders"
screenshot "success-reminder-01-minutes-prompt" screenshot "success-reminder-01-minutes-prompt"
wtype "5" wtype "5"
sleep 1 sleep 1
@@ -83,7 +83,7 @@ wtype -k Return
wait_until "reminder message prompt opens" 15 screen_contains "Reminder message" wait_until "reminder message prompt opens" 15 screen_contains "Reminder message"
screenshot "success-reminder-03-message-prompt" screenshot "success-reminder-03-message-prompt"
wtype -k Escape wtype -k Escape
wait_until "reminder flow closes" 15 layer_absent "omarchy-reminders" wait_until "reminder flow closes" 15 layer_off_overlay "omarchy-reminders"
# Render a real shell notification and clear it through the notification IPC. # Render a real shell notification and clear it through the notification IPC.
omarchy-shell notifications dismissAll >/dev/null omarchy-shell notifications dismissAll >/dev/null
@@ -101,7 +101,7 @@ if window_present "(?i)omawrite" >/dev/null 2>&1; then
fi fi
omarchy-menu summon apps >/dev/null omarchy-menu summon apps >/dev/null
wait_until "apps menu opens" 15 layer_present "omarchy-menu" wait_until "apps menu opens" 15 layer_on_overlay "omarchy-menu"
sleep 1 sleep 1
screenshot "success-apps-menu-open" screenshot "success-apps-menu-open"
@@ -111,7 +111,7 @@ screenshot "success-apps-menu-search"
wtype -k Return wtype -k Return
wait_until "apps menu launches the top search hit" 60 window_present "(?i)omawrite" wait_until "apps menu launches the top search hit" 60 window_present "(?i)omawrite"
wait_until "apps menu closes after launching" 15 layer_absent "omarchy-menu" wait_until "apps menu closes after launching" 15 layer_off_overlay "omarchy-menu"
close_windows "(?i)omawrite" close_windows "(?i)omawrite"
wait_until "Omawrite window closes" 30 window_absent "(?i)omawrite" wait_until "Omawrite window closes" 30 window_absent "(?i)omawrite"
+2 -8
View File
@@ -70,14 +70,8 @@ assert(
'image picker leaves theme mode when another caller opens it' 'image picker leaves theme mode when another caller opens it'
) )
assert( assert(
/PanelWindow \{[\s\S]*?visible: true[\s\S]*?mask: root\.opened \? null : closedMask[\s\S]*?WlrLayershell\.layer: root\.opened \? WlrLayer\.Overlay : WlrLayer\.Bottom/.test(imagePickerQml) && /OverlayWindow \{\s*id: panel\s*shown: root\.opened\s*shownKeyboardFocus: root\.imagesLoaded \? WlrKeyboardFocus\.Exclusive : WlrKeyboardFocus\.None/.test(imagePickerQml),
/Region \{ id: closedMask \}/.test(imagePickerQml), 'image picker parks on OverlayWindow and takes the keyboard once images load'
'image picker keeps its surface mapped, parked input-less below windows while closed'
)
assert(
/function openSelector[\s\S]*?targetScreen = focusedScreen\(\) \|\| targetScreen/.test(imagePickerQml) &&
/screen: root\.targetScreen/.test(imagePickerQml),
'image picker follows the focused monitor on each open'
) )
assert( assert(
/source: item\.sourceActivated && item\.thumbnailPath \? Util\.fileUrl\(item\.thumbnailPath\) : ""[\s\S]*asynchronous: false/.test(imagePickerQml), /source: item\.sourceActivated && item\.thumbnailPath \? Util\.fileUrl\(item\.thumbnailPath\) : ""[\s\S]*asynchronous: false/.test(imagePickerQml),
+70
View File
@@ -0,0 +1,70 @@
#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS'
const fs = require('fs')
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8')
const overlay = read('shell/Ui/OverlayWindow.qml')
// Closed, the surface parks 1x1, input-less and without keyboard below
// windows: off the overlay layer, so it never blocks direct scanout.
assert(
overlay.includes('visible: true') &&
overlay.includes('anchors { top: true; left: true; bottom: shown; right: shown }') &&
overlay.includes('implicitWidth: 1') &&
overlay.includes('implicitHeight: 1') &&
overlay.includes('mask: shown ? null : emptyRegion') &&
overlay.includes('WlrLayershell.layer: shown ? shownLayer : WlrLayer.Bottom') &&
overlay.includes('WlrLayershell.keyboardFocus: shown ? shownKeyboardFocus : WlrKeyboardFocus.None'),
'overlay window parks a 1x1 input-less surface below windows while hidden'
)
// Content stays hidden until the surface has grown, so no frame drawn at 1x1
// is stretched across the screen.
assert(
/target: window\.contentItem\s*property: "visible"\s*value: window\.shown && window\.width > 1 && window\.height > 1/.test(overlay),
'overlay window draws nothing until the surface has grown'
)
assert(
/onShownChanged: if \(shown\) targetScreen = focusedScreen\(\) \|\| targetScreen/.test(overlay) &&
overlay.includes('screen: targetScreen'),
'overlay window follows the focused monitor each time it is shown'
)
const overlays = {
'shell/plugins/menu/Menu.qml': 'shown: root.opened && root.rowsLoaded',
'shell/plugins/emojis/Emojis.qml': 'shown: root.opened',
'shell/plugins/clipboard/Clipboard.qml': 'shown: root.opened',
'shell/plugins/osd/Osd.qml': 'shown: root.opened',
'shell/plugins/reminders/ReminderFlow.qml': 'shown: root.opened',
'shell/plugins/panels/wifiqr/Panel.qml': 'shown: root.opened',
'shell/plugins/image-picker/ImagePicker.qml': 'shown: root.opened',
}
for (const [file, shown] of Object.entries(overlays)) {
const qml = read(file)
assert(
qml.includes('OverlayWindow {') && qml.includes(shown) && !/PanelWindow \{\s*(id: panel\s*)?visible: root\.opened/.test(qml),
`${file} keeps its surface through OverlayWindow`
)
}
// Window visibility never changes now, so close-time resets key off shown.
const menuQml = read('shell/plugins/menu/Menu.qml')
assert(
menuQml.includes('onShownChanged: if (!shown) { cardTop = -1; maxRowsHeight = -1 }') &&
menuQml.includes('if (shown && cardTop < 0) {') &&
!/onVisibleChanged: if \(!visible\) \{ cardTop/.test(menuQml),
'the menu unfreezes its layout when the overlay hides'
)
// The OSD never takes the keyboard or input, shown or not.
const osd = read('shell/plugins/osd/Osd.qml')
assert(
osd.includes('shownKeyboardFocus: WlrKeyboardFocus.None') && osd.includes('mask: Region {}'),
'the OSD stays click-through and keyboard-free while shown'
)
JS