diff --git a/shell/Ui/ScreenMoveRemap.qml b/shell/Ui/ScreenMoveRemap.qml new file mode 100644 index 00000000..44d2308a --- /dev/null +++ b/shell/Ui/ScreenMoveRemap.qml @@ -0,0 +1,43 @@ +import QtQuick + +// Hyprland leaves an already-mapped layer surface at its old global position +// when its monitor moves within the layout: undocking disables the internal +// panel, the external monitor shifts to x=0, and long-lived surfaces such as +// the bar and background keep rendering at the old offset — or entirely +// off-screen — until they are unmapped and remapped. Watch the screen's +// origin and pulse `remapping` when it moves; the owning window folds that +// into its `visible` binding so the compositor re-places the surface at the +// monitor's new origin. +Item { + id: root + + required property var window + readonly property var screen: window ? window.screen : null + + // Fold into the window's binding: visible: && !guard.remapping + property bool remapping: false + + visible: false + + // A layout reshuffle can move the monitor more than once before it lands. + // Let the positions settle before the single remap pulse. + Timer { + id: settleTimer + interval: 200 + onTriggered: root.remapping = true + } + + // Hold the surface unmapped for a beat so the compositor processes the + // unmap before the remap instead of coalescing them into a no-op. + Timer { + interval: 50 + running: root.remapping + onTriggered: root.remapping = false + } + + Connections { + target: root.screen + function onXChanged() { settleTimer.restart() } + function onYChanged() { settleTimer.restart() } + } +} diff --git a/shell/Ui/qmldir b/shell/Ui/qmldir index 92d39848..aaf9892b 100644 --- a/shell/Ui/qmldir +++ b/shell/Ui/qmldir @@ -24,6 +24,7 @@ PanelSeparator 1.0 PanelSeparator.qml PanelSlider 1.0 PanelSlider.qml PanelToolTip 1.0 PanelToolTip.qml PointerMoveGate 1.0 PointerMoveGate.qml +ScreenMoveRemap 1.0 ScreenMoveRemap.qml PopupCard 1.0 PopupCard.qml SearchableDropdown 1.0 SearchableDropdown.qml TextField 1.0 TextField.qml diff --git a/shell/plugins/background/Background.qml b/shell/plugins/background/Background.qml index 151e7baf..21e1b0a9 100644 --- a/shell/plugins/background/Background.qml +++ b/shell/plugins/background/Background.qml @@ -5,6 +5,7 @@ import QtQuick import QtQuick.Effects import QtQuick.Shapes import qs.Commons +import qs.Ui Item { id: root @@ -185,8 +186,13 @@ Item { required property var modelData screen: modelData - visible: true + visible: !remapGuard.remapping anchors { top: true; bottom: true; left: true; right: true } + + ScreenMoveRemap { + id: remapGuard + window: panel + } color: "transparent" // Keep render updates enabled. The background layer has been observed to // lose its committed buffer while parked with updatesEnabled=false, diff --git a/shell/plugins/bar/Bar.qml b/shell/plugins/bar/Bar.qml index c5be1ff6..0d563324 100644 --- a/shell/plugins/bar/Bar.qml +++ b/shell/plugins/bar/Bar.qml @@ -938,7 +938,12 @@ Item { component BarPanel: PanelWindow { id: barWindow - visible: !root.barHidden + visible: !root.barHidden && !remapGuard.remapping + + ScreenMoveRemap { + id: remapGuard + window: barWindow + } anchors { top: root.position === "top" || root.vertical