From e876ddc0b930831b4794f6c38b1e589dd1986283 Mon Sep 17 00:00:00 2001 From: purian23 Date: Fri, 1 May 2026 16:31:35 -0400 Subject: [PATCH] frame(Connected): Fix connected regression & Ran Performance pass --- .../Modals/Common/DankModalConnected.qml | 37 +++++++----- .../Modals/Common/DankModalStandalone.qml | 15 +---- .../DankLauncherV2ModalConnected.qml | 37 +++++++----- .../DankLauncherV2ModalStandalone.qml | 15 +---- quickshell/Modules/Frame/FrameWindow.qml | 16 ++--- quickshell/Widgets/DankPopout.qml | 60 ++++++++++++++++--- quickshell/Widgets/DankPopoutConnected.qml | 58 ++++++++++++------ quickshell/Widgets/DankPopoutStandalone.qml | 18 ++++-- 8 files changed, 166 insertions(+), 90 deletions(-) diff --git a/quickshell/Modals/Common/DankModalConnected.qml b/quickshell/Modals/Common/DankModalConnected.qml index 4329e7a4..7c4f2880 100644 --- a/quickshell/Modals/Common/DankModalConnected.qml +++ b/quickshell/Modals/Common/DankModalConnected.qml @@ -93,6 +93,24 @@ Item { signal dialogClosed signal backgroundClicked + Timer { + id: _fullSyncTimer + interval: 0 + onTriggered: root._flushFullSync() + } + + Timer { + id: _animSyncTimer + interval: 0 + onTriggered: root._flushAnimSync() + } + + Timer { + id: _bodySyncTimer + interval: 0 + onTriggered: root._flushBodySync() + } + property bool animationsEnabled: true property string _chromeClaimId: "" @@ -147,10 +165,7 @@ Item { if (_fullSyncPending) return; _fullSyncPending = true; - Qt.callLater(() => { - if (root && typeof root._flushFullSync === "function") - root._flushFullSync(); - }); + _fullSyncTimer.restart(); } property bool _animSyncQueued: false @@ -158,10 +173,7 @@ Item { if (_animSyncQueued) return; _animSyncQueued = true; - Qt.callLater(() => { - if (root && typeof root._flushAnimSync === "function") - root._flushAnimSync(); - }); + _animSyncTimer.restart(); } function _flushAnimSync() { _animSyncQueued = false; @@ -173,10 +185,7 @@ Item { if (_bodySyncQueued) return; _bodySyncQueued = true; - Qt.callLater(() => { - if (root && typeof root._flushBodySync === "function") - root._flushBodySync(); - }); + _bodySyncTimer.restart(); } function _flushBodySync() { _bodySyncQueued = false; @@ -483,8 +492,8 @@ Item { readonly property real s: Math.min(1, modalContainer.scaleValue) blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 + Theme.snap(modalContainer.animX, root.dpr) blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr) - blurWidth: (root.shouldBeVisible && animatedContent.publishedOpacity > 0 && !root.frameOwnsConnectedChrome) ? modalContainer.width * s : 0 - blurHeight: (root.shouldBeVisible && animatedContent.publishedOpacity > 0 && !root.frameOwnsConnectedChrome) ? modalContainer.height * s : 0 + blurWidth: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.width * s : 0 + blurHeight: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.height * s : 0 blurRadius: root.effectiveCornerRadius } diff --git a/quickshell/Modals/Common/DankModalStandalone.qml b/quickshell/Modals/Common/DankModalStandalone.qml index a035eb98..c4ba7380 100644 --- a/quickshell/Modals/Common/DankModalStandalone.qml +++ b/quickshell/Modals/Common/DankModalStandalone.qml @@ -241,8 +241,8 @@ Item { readonly property real s: Math.min(1, modalContainer.scaleValue) blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 + Theme.snap(modalContainer.animX, root.dpr) blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr) - blurWidth: (shouldBeVisible && animatedContent.publishedOpacity > 0) ? modalContainer.width * s : 0 - blurHeight: (shouldBeVisible && animatedContent.publishedOpacity > 0) ? modalContainer.height * s : 0 + blurWidth: shouldBeVisible ? modalContainer.width * s : 0 + blurHeight: shouldBeVisible ? modalContainer.height * s : 0 blurRadius: root.cornerRadius } @@ -400,8 +400,6 @@ Item { anchors.fill: parent clip: false - property real publishedOpacity: root.shouldBeVisible ? 1 : 0 - opacity: root.shouldBeVisible ? 1 : 0 scale: modalContainer.scaleValue x: Theme.snap(modalContainer.animX, root.dpr) + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5 @@ -416,15 +414,6 @@ Item { } } - Behavior on publishedOpacity { - enabled: root.animationsEnabled - NumberAnimation { - duration: animationDuration - easing.type: Easing.BezierSpline - easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve - } - } - ElevationShadow { id: modalShadowLayer anchors.fill: parent diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml index ad07037c..929e0e68 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml @@ -191,6 +191,24 @@ Item { signal dialogClosed + Timer { + id: _fullSyncTimer + interval: 0 + onTriggered: root._flushFullSync() + } + + Timer { + id: _animSyncTimer + interval: 0 + onTriggered: root._flushAnimSync() + } + + Timer { + id: _bodySyncTimer + interval: 0 + onTriggered: root._flushBodySync() + } + property string _chromeClaimId: "" property bool _fullSyncPending: false @@ -243,10 +261,7 @@ Item { if (_fullSyncPending) return; _fullSyncPending = true; - Qt.callLater(() => { - if (root && typeof root._flushFullSync === "function") - root._flushFullSync(); - }); + _fullSyncTimer.restart(); } property bool _animSyncQueued: false @@ -254,10 +269,7 @@ Item { if (_animSyncQueued) return; _animSyncQueued = true; - Qt.callLater(() => { - if (root && typeof root._flushAnimSync === "function") - root._flushAnimSync(); - }); + _animSyncTimer.restart(); } function _flushAnimSync() { _animSyncQueued = false; @@ -269,10 +281,7 @@ Item { if (_bodySyncQueued) return; _bodySyncQueued = true; - Qt.callLater(() => { - if (root && typeof root._flushBodySync === "function") - root._flushBodySync(); - }); + _bodySyncTimer.restart(); } function _flushBodySync() { _bodySyncQueued = false; @@ -625,8 +634,8 @@ Item { readonly property real s: Math.min(1, contentContainer.scaleValue) blurX: root._ccX + root.alignedWidth * (1 - s) * 0.5 + Theme.snap(contentContainer.animX, root.dpr) blurY: root._ccY + root.alignedHeight * (1 - s) * 0.5 + Theme.snap(contentContainer.animY, root.dpr) - blurWidth: (root.spotlightOpen || root.isClosing) && contentWrapper.publishedOpacity > 0 && !root.frameOwnsConnectedChrome ? root.alignedWidth * s : 0 - blurHeight: (root.spotlightOpen || root.isClosing) && contentWrapper.publishedOpacity > 0 && !root.frameOwnsConnectedChrome ? root.alignedHeight * s : 0 + blurWidth: (root.spotlightOpen || root.isClosing) && !root.frameOwnsConnectedChrome ? root.alignedWidth * s : 0 + blurHeight: (root.spotlightOpen || root.isClosing) && !root.frameOwnsConnectedChrome ? root.alignedHeight * s : 0 blurRadius: root.cornerRadius } diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml index a8a2dff6..6b1b1647 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml @@ -353,8 +353,8 @@ Item { readonly property real s: Math.min(1, modalContainer.publishedScale) blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 - blurWidth: (contentVisible && modalContainer.publishedOpacity > 0) ? modalContainer.width * s : 0 - blurHeight: (contentVisible && modalContainer.publishedOpacity > 0) ? modalContainer.height * s : 0 + blurWidth: contentVisible ? modalContainer.width * s : 0 + blurHeight: contentVisible ? modalContainer.height * s : 0 blurRadius: root.cornerRadius } @@ -414,7 +414,6 @@ Item { visible: _renderActive property bool _renderActive: contentVisible - property real publishedOpacity: contentVisible ? 1 : 0 property real publishedScale: contentVisible ? 1 : 0.96 opacity: contentVisible ? 1 : 0 @@ -426,15 +425,7 @@ Item { easing.type: Easing.BezierSpline duration: Theme.modalAnimationDuration easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized - } - } - - Behavior on publishedOpacity { - NumberAnimation { - easing.type: Easing.BezierSpline - duration: Theme.modalAnimationDuration - easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized - onRunningChanged: if (!running && modalContainer.publishedOpacity === 0) + onRunningChanged: if (!running && !root.contentVisible) modalContainer._renderActive = false } } diff --git a/quickshell/Modules/Frame/FrameWindow.qml b/quickshell/Modules/Frame/FrameWindow.qml index a7aec771..be2523da 100644 --- a/quickshell/Modules/Frame/FrameWindow.qml +++ b/quickshell/Modules/Frame/FrameWindow.qml @@ -15,7 +15,7 @@ PanelWindow { required property var targetScreen screen: targetScreen - visible: true + visible: _frameActive WlrLayershell.namespace: "dms:frame" WlrLayershell.layer: WlrLayer.Top @@ -33,14 +33,14 @@ PanelWindow { readonly property var barEdges: { SettingsData.barConfigs; - return SettingsData.getActiveBarEdgesForScreen(win.screen); + return SettingsData.getActiveBarEdgesForScreen(win.targetScreen); } - readonly property real _dpr: CompositorService.getScreenScale(win.screen) - readonly property bool _frameActive: SettingsData.frameEnabled && SettingsData.isScreenInPreferences(win.screen, SettingsData.frameScreenPreferences) + readonly property real _dpr: CompositorService.getScreenScale(win.targetScreen) + readonly property bool _frameActive: SettingsData.frameEnabled && SettingsData.isScreenInPreferences(win.targetScreen, SettingsData.frameScreenPreferences) readonly property int _windowRegionWidth: win._regionInt(win.width) readonly property int _windowRegionHeight: win._regionInt(win.height) - readonly property string _screenName: win.screen ? win.screen.name : "" + readonly property string _screenName: win.targetScreen ? win.targetScreen.name : "" readonly property var _dockState: ConnectedModeState.dockStates[win._screenName] || ConnectedModeState.emptyDockState readonly property var _dockSlide: ConnectedModeState.dockSlides[win._screenName] || ({ "x": 0, @@ -1286,7 +1286,7 @@ PanelWindow { } } - Component.onCompleted: Qt.callLater(() => win._buildBlur()) + Component.onCompleted: _blurRebuildTimer.restart() Component.onDestruction: win._teardownBlur() FrameBorder { @@ -1304,8 +1304,8 @@ PanelWindow { anchors.fill: parent visible: win._connectedActive opacity: win._surfaceOpacity - // Skip FBO when disabled, or when neither elevation nor alpha blend is active - layer.enabled: !win._disableLayer && (Theme.elevationEnabled || win._surfaceOpacity < 1) + // Skip FBO when disabled, invisible, or when neither elevation nor alpha blend is active + layer.enabled: win._connectedActive && !win._disableLayer && (Theme.elevationEnabled || win._surfaceOpacity < 1) layer.smooth: false layer.effect: MultiEffect { diff --git a/quickshell/Widgets/DankPopout.qml b/quickshell/Widgets/DankPopout.qml index ed6279d7..fb8a64d5 100644 --- a/quickshell/Widgets/DankPopout.qml +++ b/quickshell/Widgets/DankPopout.qml @@ -74,35 +74,76 @@ Item { readonly property real barY: impl.item ? impl.item.barY : 0 readonly property real barWidth: impl.item ? impl.item.barWidth : 0 readonly property real barHeight: impl.item ? impl.item.barHeight : 0 - readonly property bool useConnectedBackend: SettingsData.connectedFrameModeActive && !!screen && SettingsData.isScreenInPreferences(screen, SettingsData.frameScreenPreferences) - readonly property var _desiredBackend: useConnectedBackend ? connectedComp : standaloneComp + readonly property bool useConnectedBackend: _usesConnectedBackendForScreen(screen) property var _resolvedBackend: null + property bool _pendingOpen: false + + Timer { + id: _pendingOpenTimer + interval: 0 + onTriggered: { + if (!root._pendingOpen || !impl.item) + return; + root._pendingOpen = false; + impl.item.open(); + } + } onUseConnectedBackendChanged: _maybeResolveBackend() - Component.onCompleted: _resolvedBackend = _desiredBackend + Component.onCompleted: _resolvedBackend = _backendForScreen(screen) + + Connections { + target: SettingsData + function onConnectedFrameModeActiveChanged() { + root._maybeResolveBackend(); + } + function onFrameScreenPreferencesChanged() { + root._maybeResolveBackend(); + } + } + + function _usesConnectedBackendForScreen(targetScreen) { + return SettingsData.connectedFrameModeActive && !!targetScreen && SettingsData.isScreenInPreferences(targetScreen, SettingsData.frameScreenPreferences); + } + + function _backendForScreen(targetScreen) { + return _usesConnectedBackendForScreen(targetScreen) ? connectedComp : standaloneComp; + } // Defer Loader source-component swap until impl is fully closed; avoids // tearing down a popout mid-animation when frame mode is toggled. function _maybeResolveBackend() { - if (_resolvedBackend === _desiredBackend) + _resolveBackendForScreen(screen); + } + + function _resolveBackendForScreen(targetScreen) { + const backend = _backendForScreen(targetScreen); + if (_resolvedBackend === backend) return; if (impl.item && (impl.item.shouldBeVisible || impl.item.isClosing)) return; - _resolvedBackend = _desiredBackend; + _resolvedBackend = backend; } function open() { - if (impl.item) + _maybeResolveBackend(); + if (impl.item) { + _pendingOpen = false; impl.item.open(); + return; + } + _pendingOpen = true; } function close() { + _pendingOpen = false; + _pendingOpenTimer.stop(); if (impl.item) impl.item.close(); } function toggle() { - shouldBeVisible ? close() : open(); + (shouldBeVisible || _pendingOpen) ? close() : open(); } function setBarContext(position, bottomGap) { @@ -126,6 +167,7 @@ Item { adjacentBarInfo = SettingsData.getAdjacentBarInfo(targetScreen, pos, barConfig); setBarContext(pos, bottomGap); + _resolveBackendForScreen(targetScreen); } function updateSurfacePosition() { @@ -185,6 +227,10 @@ Item { it.effectiveBarBottomGap = Qt.binding(() => root.effectiveBarBottomGap); it.shouldBeVisible = root.shouldBeVisible; + if (root._primeContent && typeof it.primeContent === "function") + it.primeContent(); + if (_pendingOpen) + _pendingOpenTimer.restart(); } function primeContent() { diff --git a/quickshell/Widgets/DankPopoutConnected.qml b/quickshell/Widgets/DankPopoutConnected.qml index b61b42c5..3dad5209 100644 --- a/quickshell/Widgets/DankPopoutConnected.qml +++ b/quickshell/Widgets/DankPopoutConnected.qml @@ -84,6 +84,24 @@ Item { signal popoutClosed signal backgroundClicked + Timer { + id: _fullSyncTimer + interval: 0 + onTriggered: root._flushFullSync() + } + + Timer { + id: _animSyncTimer + interval: 0 + onTriggered: root._flushAnimSync() + } + + Timer { + id: _bodySyncTimer + interval: 0 + onTriggered: root._flushBodySync() + } + property var _lastOpenedScreen: null property bool isClosing: false @@ -266,11 +284,11 @@ Item { if (_fullSyncQueued) return; _fullSyncQueued = true; - Qt.callLater(() => { - root._fullSyncQueued = false; - if (typeof root._syncPopoutChromeState === "function") - root._syncPopoutChromeState(); - }); + _fullSyncTimer.restart(); + } + function _flushFullSync() { + _fullSyncQueued = false; + _syncPopoutChromeState(); } property bool _animSyncQueued: false @@ -278,10 +296,7 @@ Item { if (_animSyncQueued) return; _animSyncQueued = true; - Qt.callLater(() => { - if (root && typeof root._flushAnimSync === "function") - root._flushAnimSync(); - }); + _animSyncTimer.restart(); } function _flushAnimSync() { _animSyncQueued = false; @@ -294,10 +309,7 @@ Item { if (_bodySyncQueued) return; _bodySyncQueued = true; - Qt.callLater(() => { - if (root && typeof root._flushBodySync === "function") - root._flushBodySync(); - }); + _bodySyncTimer.restart(); } function _flushBodySync() { _bodySyncQueued = false; @@ -707,8 +719,8 @@ Item { blurX: trackBlurFromBarEdge ? contentContainer.x + (contentContainer.barRight ? _dxClamp : 0) : contentContainer.x + contentContainer.width * (1 - s) * 0.5 + Theme.snap(contentContainer.animX, root.dpr) - contentContainer.horizontalConnectorExtent * s blurY: trackBlurFromBarEdge ? contentContainer.y + (contentContainer.barBottom ? _dyClamp : 0) : contentContainer.y + contentContainer.height * (1 - s) * 0.5 + Theme.snap(contentContainer.animY, root.dpr) - contentContainer.verticalConnectorExtent * s - blurWidth: (shouldBeVisible && contentWrapper.publishedOpacity > 0) ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.width - Math.abs(_dxClamp)) : (contentContainer.width + contentContainer.horizontalConnectorExtent * 2) * s) : 0 - blurHeight: (shouldBeVisible && contentWrapper.publishedOpacity > 0) ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.height - Math.abs(_dyClamp)) : (contentContainer.height + contentContainer.verticalConnectorExtent * 2) * s) : 0 + blurWidth: shouldBeVisible ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.width - Math.abs(_dxClamp)) : (contentContainer.width + contentContainer.horizontalConnectorExtent * 2) * s) : 0 + blurHeight: shouldBeVisible ? (trackBlurFromBarEdge ? Math.max(0, contentContainer.height - Math.abs(_dyClamp)) : (contentContainer.height + contentContainer.verticalConnectorExtent * 2) * s) : 0 blurRadius: Theme.isConnectedEffect ? Theme.connectedCornerRadius : Theme.connectedSurfaceRadius } @@ -1077,7 +1089,11 @@ Item { duration: Math.round(Theme.variantDuration(animationDuration, shouldBeVisible) * Theme.variantOpacityDurationScale) easing.type: Easing.BezierSpline easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve - onRunningChanged: contentWrapper._animating = running + onRunningChanged: { + contentWrapper._animating = running; + if (!running && !root.shouldBeVisible) + contentWrapper._renderActive = false; + } } } @@ -1087,8 +1103,6 @@ Item { duration: Math.round(Theme.variantDuration(animationDuration, shouldBeVisible) * Theme.variantOpacityDurationScale) easing.type: Easing.BezierSpline easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve - onRunningChanged: if (!running && contentWrapper.publishedOpacity === 0) - contentWrapper._renderActive = false } } @@ -1100,6 +1114,14 @@ Item { } } + Connections { + target: contentWindow + function onVisibleChanged() { + if (!contentWindow.visible) + contentWrapper._renderActive = false; + } + } + Item { anchors.fill: parent clip: false diff --git a/quickshell/Widgets/DankPopoutStandalone.qml b/quickshell/Widgets/DankPopoutStandalone.qml index 220d6b00..77c6e65c 100644 --- a/quickshell/Widgets/DankPopoutStandalone.qml +++ b/quickshell/Widgets/DankPopoutStandalone.qml @@ -553,7 +553,7 @@ Item { targetWindow: contentWindow readonly property real s: Math.min(1, contentContainer.scaleValue) readonly property bool trackBlurFromBarEdge: root.fluidStandaloneActive - readonly property bool blurAlive: trackBlurFromBarEdge ? (contentContainer.revealWidth > 0 && contentContainer.revealHeight > 0) : (root.shouldBeVisible && contentWrapper.publishedOpacity > 0) + readonly property bool blurAlive: trackBlurFromBarEdge ? (contentContainer.revealWidth > 0 && contentContainer.revealHeight > 0) : root.shouldBeVisible blurX: trackBlurFromBarEdge ? contentContainer.x + contentContainer.revealX : contentContainer.x + contentContainer.width * (1 - s) * 0.5 + Theme.snap(contentContainer.animX, root.dpr) blurY: trackBlurFromBarEdge ? contentContainer.y + contentContainer.revealY : contentContainer.y + contentContainer.height * (1 - s) * 0.5 + Theme.snap(contentContainer.animY, root.dpr) @@ -825,7 +825,11 @@ Item { duration: Math.round(Theme.variantDuration(root.animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale) easing.type: Easing.BezierSpline easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve - onRunningChanged: contentWrapper._animating = running + onRunningChanged: { + contentWrapper._animating = running; + if (!running && !root.shouldBeVisible) + contentWrapper._renderActive = false; + } } } @@ -835,8 +839,6 @@ Item { duration: Math.round(Theme.variantDuration(root.animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale) easing.type: Easing.BezierSpline easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve - onRunningChanged: if (!running && contentWrapper.publishedOpacity === 0) - contentWrapper._renderActive = false } } @@ -848,6 +850,14 @@ Item { } } + Connections { + target: contentWindow + function onVisibleChanged() { + if (!contentWindow.visible) + contentWrapper._renderActive = false; + } + } + Loader { id: contentLoader anchors.fill: parent