From cdc11020922b746e5642c67df1fc05d365ff6955 Mon Sep 17 00:00:00 2001 From: bbedward Date: Tue, 19 May 2026 11:23:03 -0400 Subject: [PATCH] popout: fix opening popouts across monitors cc/brightness: fix delegate bindings and pinning --- .../ControlCenter/Components/DetailHost.qml | 26 +- .../Details/BrightnessDetail.qml | 271 +++++++++++------- .../Widgets/BrightnessSliderRow.qml | 5 +- quickshell/Widgets/DankPopoutConnected.qml | 32 ++- quickshell/Widgets/DankPopoutStandalone.qml | 38 ++- quickshell/Widgets/WindowBlur.qml | 11 + 6 files changed, 249 insertions(+), 134 deletions(-) diff --git a/quickshell/Modules/ControlCenter/Components/DetailHost.qml b/quickshell/Modules/ControlCenter/Components/DetailHost.qml index f2983598..45b083fb 100644 --- a/quickshell/Modules/ControlCenter/Components/DetailHost.qml +++ b/quickshell/Modules/ControlCenter/Components/DetailHost.qml @@ -59,21 +59,19 @@ Item { ignoreUnknownSignals: true function onDeviceNameChanged(newDeviceName) { - if (root.expandedWidgetData && root.expandedWidgetData.id === "brightnessSlider") { - const widgets = SettingsData.controlCenterWidgets || []; - const newWidgets = widgets.map(w => { - if (w.id === "brightnessSlider" && w.instanceId === root.expandedWidgetData.instanceId) { - const updatedWidget = Object.assign({}, w); - updatedWidget.deviceName = newDeviceName; - return updatedWidget; - } - return w; - }); - SettingsData.set("controlCenterWidgets", newWidgets); - if (root.collapseCallback) { - root.collapseCallback(); - } + if (!root.expandedWidgetData || root.expandedWidgetData.id !== "brightnessSlider") { + return; } + const widgets = SettingsData.controlCenterWidgets || []; + const newWidgets = widgets.map(w => { + if (w.id === "brightnessSlider" && w.instanceId === root.expandedWidgetData.instanceId) { + const updatedWidget = Object.assign({}, w); + updatedWidget.deviceName = newDeviceName; + return updatedWidget; + } + return w; + }); + SettingsData.set("controlCenterWidgets", newWidgets); } } diff --git a/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml b/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml index ad346c4f..3b02810b 100644 --- a/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/BrightnessDetail.qml @@ -23,79 +23,103 @@ Rectangle { if (!screenName) return ""; const screen = Quickshell.screens.find(s => s.name === screenName); - if (screen) { + if (screen) return SettingsData.getScreenDisplayName(screen); - } - if (SettingsData.displayNameMode === "model" && screenModel && screenModel.length > 0) { + if (SettingsData.displayNameMode === "model" && screenModel && screenModel.length > 0) return screenModel; - } return screenName; } - function resolveDeviceName() { - if (!DisplayService.brightnessAvailable || !DisplayService.devices || DisplayService.devices.length === 0) { + function resolveCurrentDevice() { + const devices = DisplayService.devices || []; + if (!DisplayService.brightnessAvailable || devices.length === 0) return ""; - } const pinKey = getScreenPinKey(); if (pinKey.length > 0) { const pins = SettingsData.brightnessDevicePins || {}; const pinnedDevice = pins[pinKey]; if (pinnedDevice && pinnedDevice.length > 0) { - const found = DisplayService.devices.find(dev => dev.name === pinnedDevice); + const found = devices.find(d => d.name === pinnedDevice); if (found) return found.name; } } + if (instanceId) { + const widgets = SettingsData.controlCenterWidgets || []; + const widget = widgets.find(w => w.id === "brightnessSlider" && w.instanceId === instanceId); + if (widget && typeof widget.deviceName === "string" && widget.deviceName.length > 0) { + const found = devices.find(d => d.name === widget.deviceName); + if (found) + return found.name; + } + } + + if (DisplayService.currentDevice) { + const found = devices.find(d => d.name === DisplayService.currentDevice); + if (found) + return found.name; + } + if (initialDeviceName && initialDeviceName.length > 0) { - const found = DisplayService.devices.find(dev => dev.name === initialDeviceName); + const found = devices.find(d => d.name === initialDeviceName); if (found) return found.name; } - const currentDeviceNameFromService = DisplayService.currentDevice; - if (currentDeviceNameFromService) { - const found = DisplayService.devices.find(dev => dev.name === currentDeviceNameFromService); - if (found) - return found.name; - } - - const backlight = DisplayService.devices.find(d => d.class === "backlight"); + const backlight = devices.find(d => d.class === "backlight"); if (backlight) return backlight.name; - const ddc = DisplayService.devices.find(d => d.class === "ddc"); + const ddc = devices.find(d => d.class === "ddc"); if (ddc) return ddc.name; - return DisplayService.devices.length > 0 ? DisplayService.devices[0].name : ""; + return devices[0].name; + } + + function selectDevice(deviceName) { + if (!deviceName || deviceName === root.currentDeviceName) { + return; + } + const pinKey = getScreenPinKey(); + if (pinKey.length > 0) { + const pins = SettingsData.brightnessDevicePins || {}; + const existing = pins[pinKey]; + if (existing && existing !== deviceName) { + const next = JSON.parse(JSON.stringify(pins)); + delete next[pinKey]; + SettingsData.set("brightnessDevicePins", next); + } + } + root.currentDeviceName = deviceName; + DisplayService.setCurrentDevice(deviceName, true); + Qt.callLater(() => root.deviceNameChanged(deviceName)); } Component.onCompleted: { - currentDeviceName = resolveDeviceName(); + root.currentDeviceName = resolveCurrentDevice(); } - property bool isPinnedToScreen: { + function isDevicePinnedToScreen(deviceName) { const pinKey = getScreenPinKey(); - if (!pinKey || pinKey.length === 0) + if (!pinKey || !deviceName) return false; const pins = SettingsData.brightnessDevicePins || {}; - return pins[pinKey] === currentDeviceName; + return pins[pinKey] === deviceName; } - function togglePinToScreen() { + function togglePinForDevice(deviceName) { const pinKey = getScreenPinKey(); - if (!pinKey || pinKey.length === 0 || !currentDeviceName || currentDeviceName.length === 0) + if (!pinKey || !deviceName) return; const pins = JSON.parse(JSON.stringify(SettingsData.brightnessDevicePins || {})); - - if (isPinnedToScreen) { + if (pins[pinKey] === deviceName) { delete pins[pinKey]; } else { - pins[pinKey] = currentDeviceName; + pins[pinKey] = deviceName; } - SettingsData.set("brightnessDevicePins", pins); } @@ -153,18 +177,23 @@ Rectangle { } Rectangle { + id: monitorHeader width: parent.width height: 40 visible: screenName && screenName.length > 0 && DisplayService.devices && DisplayService.devices.length > 1 radius: Theme.cornerRadius color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) + property bool currentDevicePinned: root.isDevicePinnedToScreen(currentDeviceName) + Item { anchors.fill: parent anchors.margins: Theme.spacingM Row { anchors.left: parent.left + anchors.right: globalPinButton.left + anchors.rightMargin: Theme.spacingS anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingM @@ -180,47 +209,51 @@ Rectangle { font.pixelSize: Theme.fontSizeMedium color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + width: parent.width - Theme.iconSize - Theme.spacingM } } Rectangle { + id: globalPinButton anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - width: pinRow.width + Theme.spacingS * 2 + width: globalPinRow.width + Theme.spacingS * 2 height: 28 radius: height / 2 - color: isPinnedToScreen ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05) + color: monitorHeader.currentDevicePinned ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Theme.withAlpha(Theme.surfaceText, 0.05) Row { - id: pinRow + id: globalPinRow anchors.centerIn: parent spacing: 4 DankIcon { - name: isPinnedToScreen ? "push_pin" : "push_pin" + name: "push_pin" size: 16 - color: isPinnedToScreen ? Theme.primary : Theme.surfaceText + color: monitorHeader.currentDevicePinned ? Theme.primary : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } StyledText { - text: isPinnedToScreen ? I18n.tr("Pinned") : I18n.tr("Pin") + text: monitorHeader.currentDevicePinned ? I18n.tr("Pinned") : I18n.tr("Pin") font.pixelSize: Theme.fontSizeSmall - color: isPinnedToScreen ? Theme.primary : Theme.surfaceText + color: monitorHeader.currentDevicePinned ? Theme.primary : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } } DankRipple { - id: pinRipple + id: globalPinRipple cornerRadius: parent.radius } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor - onPressed: mouse => pinRipple.trigger(mouse.x, mouse.y) - onClicked: root.togglePinToScreen() + enabled: currentDeviceName && currentDeviceName.length > 0 + onPressed: mouse => globalPinRipple.trigger(mouse.x, mouse.y) + onClicked: root.togglePinForDevice(currentDeviceName) } } } @@ -229,9 +262,17 @@ Rectangle { Repeater { model: DisplayService.devices || [] delegate: Rectangle { + id: deviceCard + required property var modelData required property int index + readonly property bool selected: !!(modelData && modelData.name === root.currentDeviceName) + readonly property bool devicePinnedHere: { + SettingsData.brightnessDevicePins; + return root.isDevicePinnedToScreen(modelData ? modelData.name : ""); + } + property real deviceBrightness: { DisplayService.brightnessVersion; return DisplayService.getDeviceBrightness(modelData.name); @@ -241,8 +282,8 @@ Rectangle { height: 100 radius: Theme.cornerRadius color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) - border.color: modelData.name === currentDeviceName ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) - border.width: modelData.name === currentDeviceName ? 2 : 0 + border.color: selected ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) + border.width: selected ? 2 : 0 Column { anchors.fill: parent @@ -251,10 +292,12 @@ Rectangle { Item { width: parent.width - height: Math.max(deviceIconColumn.height, deviceInfoColumn.height, exponentControls.height) + height: Math.max(deviceIconColumn.height, deviceInfoColumn.height, rightControls.height) Row { anchors.left: parent.left + anchors.right: rightControls.left + anchors.rightMargin: Theme.spacingS anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingM @@ -281,7 +324,7 @@ Rectangle { } } size: Theme.iconSize - color: modelData.name === currentDeviceName ? Theme.primary : Theme.surfaceText + color: deviceCard.selected ? Theme.primary : Theme.surfaceText anchors.horizontalCenter: parent.horizontalCenter } @@ -296,7 +339,7 @@ Rectangle { Column { id: deviceInfoColumn anchors.verticalCenter: parent.verticalCenter - width: parent.parent.width - deviceIconColumn.width - exponentControls.width - Theme.spacingM * 3 + width: parent.width - deviceIconColumn.width - Theme.spacingM StyledText { text: { @@ -309,7 +352,7 @@ Rectangle { } font.pixelSize: Theme.fontSizeMedium color: Theme.surfaceText - font.weight: modelData.name === currentDeviceName ? Font.Medium : Font.Normal + font.weight: deviceCard.selected ? Font.Medium : Font.Normal elide: Text.ElideRight width: parent.width horizontalAlignment: Text.AlignLeft @@ -345,80 +388,107 @@ Rectangle { } Row { - id: exponentControls - width: 140 + id: rightControls height: 28 anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingXS - visible: SessionData.getBrightnessExponential(modelData.name) + spacing: Theme.spacingS z: 1 - StyledRect { - width: 28 + Row { + id: exponentControls height: 28 - radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) - opacity: SessionData.getBrightnessExponent(modelData.name) > 1.0 ? 1.0 : 0.4 + spacing: Theme.spacingXS + visible: SessionData.getBrightnessExponential(modelData.name) - DankIcon { - anchors.centerIn: parent - name: "remove" - size: 14 - color: Theme.surfaceText + StyledRect { + width: 28 + height: 28 + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) + opacity: SessionData.getBrightnessExponent(modelData.name) > 1.0 ? 1.0 : 0.4 + + DankIcon { + anchors.centerIn: parent + name: "remove" + size: 14 + color: Theme.surfaceText + } + + StateLayer { + stateColor: Theme.primary + cornerRadius: parent.radius + enabled: SessionData.getBrightnessExponent(modelData.name) > 1.0 + onClicked: { + const current = SessionData.getBrightnessExponent(modelData.name); + const newValue = Math.max(1.0, Math.round((current - 0.1) * 10) / 10); + SessionData.setBrightnessExponent(modelData.name, newValue); + } + } } - StateLayer { - stateColor: Theme.primary - cornerRadius: parent.radius - enabled: SessionData.getBrightnessExponent(modelData.name) > 1.0 - onClicked: { - const current = SessionData.getBrightnessExponent(modelData.name); - const newValue = Math.max(1.0, Math.round((current - 0.1) * 10) / 10); - SessionData.setBrightnessExponent(modelData.name, newValue); + StyledRect { + width: 50 + height: 28 + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) + border.width: 0 + + StyledText { + anchors.centerIn: parent + text: SessionData.getBrightnessExponent(modelData.name).toFixed(1) + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.primary + } + } + + StyledRect { + width: 28 + height: 28 + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) + opacity: SessionData.getBrightnessExponent(modelData.name) < 2.5 ? 1.0 : 0.4 + + DankIcon { + anchors.centerIn: parent + name: "add" + size: 14 + color: Theme.surfaceText + } + + StateLayer { + stateColor: Theme.primary + cornerRadius: parent.radius + enabled: SessionData.getBrightnessExponent(modelData.name) < 2.5 + onClicked: { + const current = SessionData.getBrightnessExponent(modelData.name); + const newValue = Math.min(2.5, Math.round((current + 0.1) * 10) / 10); + SessionData.setBrightnessExponent(modelData.name, newValue); + } } } } StyledRect { - width: 50 - height: 28 - radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) - border.width: 0 - - StyledText { - anchors.centerIn: parent - text: SessionData.getBrightnessExponent(modelData.name).toFixed(1) - font.pixelSize: Theme.fontSizeSmall - font.weight: Font.Medium - color: Theme.primary - } - } - - StyledRect { + id: pinButton width: 28 height: 28 radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) - opacity: SessionData.getBrightnessExponent(modelData.name) < 2.5 ? 1.0 : 0.4 + visible: root.screenName && root.screenName.length > 0 && DisplayService.devices && DisplayService.devices.length > 1 + color: devicePinnedHere ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) DankIcon { anchors.centerIn: parent - name: "add" + name: "push_pin" size: 14 - color: Theme.surfaceText + color: devicePinnedHere ? Theme.primary : Theme.surfaceText } StateLayer { stateColor: Theme.primary cornerRadius: parent.radius - enabled: SessionData.getBrightnessExponent(modelData.name) < 2.5 - onClicked: { - const current = SessionData.getBrightnessExponent(modelData.name); - const newValue = Math.min(2.5, Math.round((current + 0.1) * 10) / 10); - SessionData.setBrightnessExponent(modelData.name, newValue); - } + onClicked: root.togglePinForDevice(modelData.name) } } } @@ -474,22 +544,11 @@ Rectangle { MouseArea { anchors.fill: parent anchors.bottomMargin: 28 - anchors.rightMargin: SessionData.getBrightnessExponential(modelData.name) ? 145 : 0 + anchors.rightMargin: rightControls.width + Theme.spacingS hoverEnabled: true cursorShape: Qt.PointingHandCursor onPressed: mouse => deviceRipple.trigger(mouse.x, mouse.y) - onClicked: { - const pinKey = root.getScreenPinKey(); - if (pinKey.length > 0 && modelData.name !== currentDeviceName) { - const pins = JSON.parse(JSON.stringify(SettingsData.brightnessDevicePins || {})); - if (pins[pinKey]) { - delete pins[pinKey]; - SettingsData.set("brightnessDevicePins", pins); - } - } - currentDeviceName = modelData.name; - deviceNameChanged(modelData.name); - } + onClicked: root.selectDevice(modelData.name) } } } diff --git a/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml b/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml index f2921234..4d5eb008 100644 --- a/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml +++ b/quickshell/Modules/ControlCenter/Widgets/BrightnessSliderRow.qml @@ -1,4 +1,5 @@ import QtQuick +import Quickshell import qs.Common import qs.Services import qs.Widgets @@ -31,8 +32,10 @@ Row { } if (screenName && screenName.length > 0) { + const screen = Quickshell.screens.find(s => s.name === screenName); + const pinKey = screen ? SettingsData.getScreenDisplayName(screen) : screenName; const pins = SettingsData.brightnessDevicePins || {}; - const pinnedDevice = pins[screenName]; + const pinnedDevice = pins[pinKey]; if (pinnedDevice && pinnedDevice.length > 0) { const found = DisplayService.devices.find(dev => dev.name === pinnedDevice); if (found) { diff --git a/quickshell/Widgets/DankPopoutConnected.qml b/quickshell/Widgets/DankPopoutConnected.qml index 57f0c322..11da3c9c 100644 --- a/quickshell/Widgets/DankPopoutConnected.qml +++ b/quickshell/Widgets/DankPopoutConnected.qml @@ -357,14 +357,19 @@ Item { animationsEnabled = false; _primeContent = true; - if (_lastOpenedScreen !== null && _lastOpenedScreen !== screen) { + const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen; + if (screenChanged) { + // Hide on this tick so Qt actually tears down the wl_surface; the show + // gets deferred below so the unmap is processed before the remap. contentWindow.visible = false; } _lastOpenedScreen = screen; if (contentContainer) { - // animationsEnabled is false here, so this snaps to closed without animating. - morph.openProgress = 0; + // Snap morph closed only on a fresh open; on screen-change re-open we stay at 1 + // because shouldBeVisible doesn't change and won't drive morph back to 1. + if (!shouldBeVisible) + morph.openProgress = 0; _captureChromeAnimTravel(); } @@ -375,12 +380,25 @@ Item { _chromeClaimId = ""; } - contentWindow.visible = true; + if (screenChanged) { + // Defer the show one event-loop tick. Qt coalesces a synchronous + // false→true visibility flip into a no-op, leaving WindowBlur committed + // to the previous screen's wl_surface. Splitting the flip across ticks + // forces a real surface destroy+create so BackgroundEffect.surfaceCreated + // fires and the blur region republishes on the new surface. + Qt.callLater(() => { + if (!root.shouldBeVisible) + return; + contentWindow.visible = true; + popoutBlur.kick(); + }); + } else { + contentWindow.visible = true; + } animationsEnabled = true; shouldBeVisible = true; if (shouldBeVisible && screen) { - contentWindow.visible = true; PopoutManager.showPopout(popoutHandle); opened(); } @@ -1081,7 +1099,9 @@ Item { Connections { target: contentWindow function onVisibleChanged() { - if (!contentWindow.visible) + // open() flips contentWindow.visible to rebind the layer surface to + // a new screen; don't deactivate the wrapper while still open. + if (!contentWindow.visible && !root.shouldBeVisible) contentWrapper._renderActive = false; } } diff --git a/quickshell/Widgets/DankPopoutStandalone.qml b/quickshell/Widgets/DankPopoutStandalone.qml index 349581c9..45b756b8 100644 --- a/quickshell/Widgets/DankPopoutStandalone.qml +++ b/quickshell/Widgets/DankPopoutStandalone.qml @@ -287,21 +287,43 @@ Item { _frozenMaskWidth = maskWidth; _frozenMaskHeight = maskHeight; - if (_lastOpenedScreen !== null && _lastOpenedScreen !== screen) { + const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen; + if (screenChanged) { + // Hide on this tick so Qt actually tears down the wl_surface; the show + // gets deferred below so the unmap is processed before the remap. contentWindow.visible = false; backgroundWindow.visible = false; } _lastOpenedScreen = screen; - if (contentContainer) { - // animationsEnabled is false here, so this snaps to closed without animating. + if (contentContainer && !shouldBeVisible) { + // Snap morph closed only on a fresh open; on screen-change re-open we stay at 1 + // because shouldBeVisible doesn't change and won't drive morph back to 1. morph.openProgress = 0; } _setSurfaceGeometry(alignedX, alignedY, alignedWidth, alignedHeight); - if (backgroundWindowRequired) - backgroundWindow.visible = true; - contentWindow.visible = true; + if (screenChanged) { + // Defer the show one event-loop tick. Qt coalesces a synchronous + // false→true visibility flip into a no-op, leaving WindowBlur committed + // to the previous screen's wl_surface. Splitting the flip across ticks + // forces a real surface destroy+create so BackgroundEffect.surfaceCreated + // fires and the blur region republishes on the new surface. + Qt.callLater(() => { + if (!root.shouldBeVisible) + return; + if (root.backgroundWindowRequired) + backgroundWindow.visible = true; + contentWindow.visible = true; + popoutBlur.kick(); + _bgCommitWindow = true; + bgCommitSettleTimer.restart(); + }); + } else { + if (backgroundWindowRequired) + backgroundWindow.visible = true; + contentWindow.visible = true; + } animationsEnabled = true; shouldBeVisible = true; @@ -840,7 +862,9 @@ Item { Connections { target: contentWindow function onVisibleChanged() { - if (!contentWindow.visible) + // open() flips contentWindow.visible to rebind the layer surface to + // a new screen; don't deactivate the wrapper while still open. + if (!contentWindow.visible && !root.shouldBeVisible) contentWrapper._renderActive = false; } } diff --git a/quickshell/Widgets/WindowBlur.qml b/quickshell/Widgets/WindowBlur.qml index 5f1c9227..2b894658 100644 --- a/quickshell/Widgets/WindowBlur.qml +++ b/quickshell/Widgets/WindowBlur.qml @@ -34,6 +34,17 @@ Item { targetWindow.BackgroundEffect.blurRegion = _active ? blurRegion : null; } + // Force BackgroundEffect to re-publish the blur region on the current wl_surface. + // Clearing first bypasses Quickshell's same-Region dedup in BackgroundEffect::setBlurRegion, + // setting pendingBlurRegion=true so the next polish actually ships the region — needed + // when the underlying surface has been remapped (e.g. PanelWindow.screen change). + function kick() { + if (!targetWindow) + return; + targetWindow.BackgroundEffect.blurRegion = null; + targetWindow.BackgroundEffect.blurRegion = _active ? blurRegion : null; + } + on_ActiveChanged: _apply() onTargetWindowChanged: _apply()