1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-07 19:59:14 -04:00

refactor(frame): improve connected mode surface recovery

- share modal and launcher ownership handling
- recover missing background and blur layers
This commit is contained in:
purian23
2026-06-07 17:47:24 -04:00
parent 69f3dee25a
commit d08c7c5e55
9 changed files with 439 additions and 124 deletions
+125
View File
@@ -0,0 +1,125 @@
pragma ComponentBehavior: Bound
import QtQuick
Item {
id: root
required property var modalHandle
required property string claimPrefix
property string screenName: ""
property bool enabled: false
property bool active: false
property bool presented: false
property bool dockBlocked: false
property string dockSide: ""
property string claimId: ""
property string claimedScreenName: ""
signal recoveryRequested
visible: false
function _nextClaimId() {
return claimPrefix + ":" + (new Date()).getTime() + ":" + Math.floor(Math.random() * 1000);
}
function _isCurrentModal(name) {
return !!name && ModalManager.isCurrentModal(modalHandle, name);
}
function _shouldRecover() {
return active && enabled && _isCurrentModal(screenName);
}
function _requestRecovery() {
if (_shouldRecover())
recoveryRequested();
}
function publish(state) {
if (!enabled || !screenName || !state) {
release();
return false;
}
if (claimedScreenName && claimedScreenName !== screenName)
release();
const isCurrent = _isCurrentModal(screenName);
let isClaim = !claimId;
if (isClaim && !isCurrent)
return false;
if (isClaim)
claimId = _nextClaimId();
let published = isClaim ? ConnectedModeState.claimModalState(screenName, state, claimId) : ConnectedModeState.ensureModalState(screenName, state, claimId);
if (!published && !isClaim && isCurrent) {
ConnectedModeState.releaseDockRetract(claimId);
claimId = _nextClaimId();
published = ConnectedModeState.claimModalState(screenName, state, claimId);
}
if (!published)
return false;
claimedScreenName = screenName;
if (dockBlocked && presented)
ConnectedModeState.requestDockRetract(claimId, screenName, dockSide);
else
ConnectedModeState.releaseDockRetract(claimId);
return true;
}
function updateAnim(animX, animY) {
if (!enabled || !claimId || !claimedScreenName)
return false;
if (!ConnectedModeState.hasModalOwner(claimedScreenName, claimId)) {
_requestRecovery();
return false;
}
return ConnectedModeState.setModalAnim(claimedScreenName, animX, animY, claimId);
}
function updateBody(bodyX, bodyY, bodyW, bodyH) {
if (!enabled || !claimId || !claimedScreenName)
return false;
if (!ConnectedModeState.hasModalOwner(claimedScreenName, claimId)) {
_requestRecovery();
return false;
}
return ConnectedModeState.setModalBody(claimedScreenName, bodyX, bodyY, bodyW, bodyH, claimId);
}
function release() {
if (!claimId)
return;
ConnectedModeState.releaseDockRetract(claimId);
const releasedClaimId = claimId;
const releasedScreenName = claimedScreenName;
claimId = "";
claimedScreenName = "";
if (releasedScreenName)
ConnectedModeState.clearModalState(releasedScreenName, releasedClaimId);
}
Component.onDestruction: release()
Connections {
target: ModalManager
function onModalChanged() {
root._requestRecovery();
}
}
Connections {
target: ConnectedModeState
function onModalOwnersChanged() {
if (!ConnectedModeState.hasModalOwner(root.screenName, root.claimId))
root._requestRecovery();
}
function onModalStatesChanged() {
if (!ConnectedModeState.modalStates[root.screenName])
root._requestRecovery();
}
}
}
+69 -13
View File
@@ -38,6 +38,10 @@ Singleton {
// Dock slide offsets — hot-path updates separated from full geometry state
property var dockSlides: ({})
// Surfaces are keyed by screen.name. FrameWindow watches to refresh connected chrome
// after claim/release boundaries without tracking each animation frame
property var surfaceRevisions: ({})
function _cloneDict(src) {
const next = {};
for (const k in src)
@@ -45,16 +49,31 @@ Singleton {
return next;
}
function _bumpSurfaceRevision(screenName) {
if (!screenName)
return;
const next = _cloneDict(surfaceRevisions);
next[screenName] = Number(next[screenName] || 0) + 1;
surfaceRevisions = next;
}
function hasPopoutOwner(claimId) {
return !!claimId && popoutOwnerId === claimId;
}
function claimPopout(claimId, state) {
if (!claimId)
if (!claimId || !state)
return false;
const previousScreen = popoutScreen;
popoutOwnerId = claimId;
return updatePopout(claimId, state);
const ok = updatePopout(claimId, state);
if (ok) {
if (previousScreen && previousScreen !== popoutScreen)
_bumpSurfaceRevision(previousScreen);
_bumpSurfaceRevision(popoutScreen);
}
return ok;
}
function updatePopout(claimId, state) {
@@ -91,6 +110,7 @@ Singleton {
if (!hasPopoutOwner(claimId))
return false;
const releasedScreen = popoutScreen;
popoutOwnerId = "";
popoutVisible = false;
popoutBarSide = "top";
@@ -103,6 +123,7 @@ Singleton {
popoutScreen = "";
popoutOmitStartConnector = false;
popoutOmitEndConnector = false;
_bumpSurfaceRevision(releasedScreen);
return true;
}
@@ -174,10 +195,13 @@ Singleton {
const normalized = _normalizeDockState(state);
if (_sameDockState(dockStates[screenName], normalized))
return true;
const previous = dockStates[screenName] || emptyDockState;
const next = _cloneDict(dockStates);
next[screenName] = normalized;
dockStates = next;
if (!!previous.reveal !== !!normalized.reveal)
_bumpSurfaceRevision(screenName);
return true;
}
@@ -195,6 +219,7 @@ Singleton {
delete nextSlides[screenName];
dockSlides = nextSlides;
}
_bumpSurfaceRevision(screenName);
return true;
}
@@ -260,10 +285,13 @@ Singleton {
const normalized = _normalizeNotificationState(state);
if (_sameNotificationState(notificationStates[screenName], normalized))
return true;
const previous = notificationStates[screenName] || emptyNotificationState;
const next = _cloneDict(notificationStates);
next[screenName] = normalized;
notificationStates = next;
if (!!previous.visible !== !!normalized.visible)
_bumpSurfaceRevision(screenName);
return true;
}
@@ -274,6 +302,7 @@ Singleton {
const next = _cloneDict(notificationStates);
delete next[screenName];
notificationStates = next;
_bumpSurfaceRevision(screenName);
return true;
}
@@ -330,18 +359,17 @@ Singleton {
modalOwners = nextOwners;
}
const normalized = _normalizeModalState(state);
if (_sameModalState(modalStates[screenName], normalized))
return true;
const next = _cloneDict(modalStates);
next[screenName] = normalized;
modalStates = next;
_bumpSurfaceRevision(screenName);
return true;
}
function updateModalState(screenName, state, ownerId) {
if (!screenName || !state)
return false;
if (ownerId && modalOwners[screenName] && modalOwners[screenName] !== ownerId)
if (ownerId && modalOwners[screenName] !== ownerId)
return false;
const normalized = _normalizeModalState(state);
if (_sameModalState(modalStates[screenName], normalized))
@@ -352,30 +380,50 @@ Singleton {
return true;
}
function hasModalOwner(screenName, ownerId) {
return !!screenName && !!ownerId && modalOwners[screenName] === ownerId;
}
function ensureModalState(screenName, state, ownerId) {
if (!screenName || !state || !ownerId)
return false;
const currentOwner = modalOwners[screenName] || "";
if (currentOwner && currentOwner !== ownerId)
return false;
if (!currentOwner)
return claimModalState(screenName, state, ownerId);
return updateModalState(screenName, state, ownerId);
}
function setModalState(screenName, state) {
return updateModalState(screenName, state, null);
}
function clearModalState(screenName, ownerId) {
if (!screenName || !modalStates[screenName])
if (!screenName)
return false;
if (ownerId && modalOwners[screenName] && modalOwners[screenName] !== ownerId)
if (ownerId && modalOwners[screenName] !== ownerId)
return false;
if (!modalStates[screenName] && !modalOwners[screenName])
return false;
const next = _cloneDict(modalStates);
delete next[screenName];
modalStates = next;
if (modalStates[screenName]) {
const next = _cloneDict(modalStates);
delete next[screenName];
modalStates = next;
}
if (modalOwners[screenName]) {
const nextOwners = _cloneDict(modalOwners);
delete nextOwners[screenName];
modalOwners = nextOwners;
}
_bumpSurfaceRevision(screenName);
return true;
}
function setModalAnim(screenName, animX, animY, ownerId) {
if (ownerId && modalOwners[screenName] && modalOwners[screenName] !== ownerId)
if (ownerId && modalOwners[screenName] !== ownerId)
return false;
const cur = screenName ? modalStates[screenName] : null;
if (!cur)
@@ -394,7 +442,7 @@ Singleton {
}
function setModalBody(screenName, bodyX, bodyY, bodyW, bodyH, ownerId) {
if (ownerId && modalOwners[screenName] && modalOwners[screenName] !== ownerId)
if (ownerId && modalOwners[screenName] !== ownerId)
return false;
const cur = screenName ? modalStates[screenName] : null;
if (!cur)
@@ -492,6 +540,9 @@ Singleton {
const nextModalOwners = pruneKeyed(modalOwners);
if (nextModalOwners !== null)
modalOwners = nextModalOwners;
const nextSurfaceRevisions = pruneKeyed(surfaceRevisions);
if (nextSurfaceRevisions !== null)
surfaceRevisions = nextSurfaceRevisions;
let retractChanged = false;
const nextRetract = {};
@@ -512,7 +563,12 @@ Singleton {
Connections {
target: Quickshell
function onScreensChanged() {
root._pruneToLiveScreens();
screenPruneAction.schedule();
}
}
DeferredAction {
id: screenPruneAction
onTriggered: root._pruneToLiveScreens()
}
}
+5
View File
@@ -26,6 +26,11 @@ Singleton {
});
}
function isCurrentModal(modal, screenName) {
const name = screenName || modal?.effectiveScreen?.name || "unknown";
return currentModalsByScreen[name] === modal;
}
function closeModal(modal) {
const screenName = modal.effectiveScreen?.name ?? "unknown";
if (currentModalsByScreen[screenName] === modal) {
+5
View File
@@ -98,6 +98,11 @@ Singleton {
return currentPopoutsByScreen[screen.name] || null;
}
function isCurrentPopout(popout, screenName) {
const name = screenName || popout?.screen?.name || "";
return !!name && currentPopoutsByScreen[name] === popout;
}
function requestPopout(popout, tabIndex, triggerSource) {
if (!popout || !popout.screen)
return;
+28 -46
View File
@@ -105,21 +105,26 @@ Item {
property bool animationsEnabled: true
property string _chromeClaimId: ""
property bool _fullSyncPending: false
function _nextChromeClaimId() {
return layerNamespace + ":modal:" + (new Date()).getTime() + ":" + Math.floor(Math.random() * 1000);
}
function _currentScreenName() {
return effectiveScreen ? effectiveScreen.name : "";
}
function _publishModalChromeState(isClaim) {
const screenName = _currentScreenName();
if (!screenName)
return;
ConnectedModalChrome {
id: modalChrome
modalHandle: root.modalHandle
claimPrefix: root.layerNamespace + ":modal"
screenName: root._currentScreenName()
enabled: root.frameOwnsConnectedChrome
active: root.shouldBeVisible
presented: root.shouldBeVisible || contentWindow.visible
dockBlocked: root._dockBlocksEmergence
dockSide: root.resolvedConnectedBarSide
onRecoveryRequested: root._queueFullSync()
}
function _publishModalChromeState() {
const state = {
"visible": shouldBeVisible || contentWindow.visible,
"barSide": resolvedConnectedBarSide,
@@ -132,25 +137,11 @@ Item {
"omitStartConnector": false,
"omitEndConnector": false
};
if (isClaim)
ConnectedModeState.claimModalState(screenName, state, _chromeClaimId);
else
ConnectedModeState.updateModalState(screenName, state, _chromeClaimId);
return modalChrome.publish(state);
}
function _syncModalChromeState() {
if (!frameOwnsConnectedChrome) {
_releaseModalChrome();
return;
}
const isClaim = !_chromeClaimId;
if (!_chromeClaimId)
_chromeClaimId = _nextChromeClaimId();
_publishModalChromeState(isClaim);
if (_dockBlocksEmergence && (shouldBeVisible || contentWindow.visible))
ConnectedModeState.requestDockRetract(_chromeClaimId, _currentScreenName(), resolvedConnectedBarSide);
else
ConnectedModeState.releaseDockRetract(_chromeClaimId);
_publishModalChromeState();
}
property bool _animSyncQueued: false
@@ -187,32 +178,21 @@ Item {
}
function _syncModalAnim() {
if (!frameOwnsConnectedChrome || !_chromeClaimId)
if (!frameOwnsConnectedChrome)
return;
const screenName = _currentScreenName();
if (!screenName || !modalContainer)
if (!modalContainer)
return;
ConnectedModeState.setModalAnim(screenName, modalContainer.animX, modalContainer.animY, _chromeClaimId);
modalChrome.updateAnim(modalContainer.animX, modalContainer.animY);
}
function _syncModalBody() {
if (!frameOwnsConnectedChrome || !_chromeClaimId)
if (!frameOwnsConnectedChrome)
return;
const screenName = _currentScreenName();
if (!screenName)
return;
ConnectedModeState.setModalBody(screenName, alignedX, alignedY, alignedWidth, alignedHeight, _chromeClaimId);
modalChrome.updateBody(alignedX, alignedY, alignedWidth, alignedHeight);
}
function _releaseModalChrome() {
if (!_chromeClaimId)
return;
ConnectedModeState.releaseDockRetract(_chromeClaimId);
const claimId = _chromeClaimId;
_chromeClaimId = "";
const screenName = _currentScreenName();
if (screenName)
ConnectedModeState.clearModalState(screenName, claimId);
modalChrome.release();
}
onFrameOwnsConnectedChromeChanged: _syncModalChromeState()
@@ -223,8 +203,6 @@ Item {
onAlignedWidthChanged: _queueBodySync()
onAlignedHeightChanged: _queueBodySync()
Component.onDestruction: _releaseModalChrome()
Connections {
target: contentWindow
function onVisibleChanged() {
@@ -248,12 +226,12 @@ Item {
clickCatcher.screen = focusedScreen;
}
ModalManager.openModal(modalHandle);
if (Theme.isDirectionalEffect || root.useBackground) {
if (!useSingleWindow)
clickCatcher.visible = true;
contentWindow.visible = true;
}
ModalManager.openModal(modalHandle);
Qt.callLater(() => {
animationsEnabled = true;
@@ -317,8 +295,12 @@ Item {
break;
}
}
if (screenStillExists)
if (screenStillExists) {
if (root.shouldBeVisible)
root._queueFullSync();
return;
}
root._releaseModalChrome();
const newScreen = CompositorService.getFocusedScreen();
if (newScreen) {
contentWindow.screen = newScreen;
@@ -232,21 +232,26 @@ Item {
onTriggered: root._flushSync()
}
property string _chromeClaimId: ""
property bool _fullSyncPending: false
function _nextChromeClaimId() {
return "dms:launcher-v2:" + (new Date()).getTime() + ":" + Math.floor(Math.random() * 1000);
}
function _currentScreenName() {
return effectiveScreen ? effectiveScreen.name : "";
}
function _publishModalChromeState(isClaim) {
const screenName = _currentScreenName();
if (!screenName)
return;
ConnectedModalChrome {
id: modalChrome
modalHandle: root.modalHandle
claimPrefix: "dms:launcher-v2"
screenName: root._currentScreenName()
enabled: root.frameOwnsConnectedChrome
active: root.spotlightOpen
presented: root.spotlightOpen || contentWindow.visible
dockBlocked: root._dockBlocksEmergence
dockSide: root.resolvedConnectedBarSide
onRecoveryRequested: root._queueFullSync()
}
function _publishModalChromeState() {
const state = {
"visible": spotlightOpen || contentWindow.visible,
"barSide": resolvedConnectedBarSide,
@@ -259,25 +264,11 @@ Item {
"omitStartConnector": false,
"omitEndConnector": false
};
if (isClaim)
ConnectedModeState.claimModalState(screenName, state, _chromeClaimId);
else
ConnectedModeState.updateModalState(screenName, state, _chromeClaimId);
return modalChrome.publish(state);
}
function _syncModalChromeState() {
if (!frameOwnsConnectedChrome) {
_releaseModalChrome();
return;
}
const isClaim = !_chromeClaimId;
if (!_chromeClaimId)
_chromeClaimId = _nextChromeClaimId();
_publishModalChromeState(isClaim);
if (_dockBlocksEmergence && (spotlightOpen || contentWindow.visible))
ConnectedModeState.requestDockRetract(_chromeClaimId, _currentScreenName(), resolvedConnectedBarSide);
else
ConnectedModeState.releaseDockRetract(_chromeClaimId);
_publishModalChromeState();
}
property bool _animSyncQueued: false
@@ -314,32 +305,21 @@ Item {
}
function _syncModalAnim() {
if (!frameOwnsConnectedChrome || !_chromeClaimId)
if (!frameOwnsConnectedChrome)
return;
const screenName = _currentScreenName();
if (!screenName || !contentContainer)
if (!contentContainer)
return;
ConnectedModeState.setModalAnim(screenName, contentContainer.animX, contentContainer.animY, _chromeClaimId);
modalChrome.updateAnim(contentContainer.animX, contentContainer.animY);
}
function _syncModalBody() {
if (!frameOwnsConnectedChrome || !_chromeClaimId)
if (!frameOwnsConnectedChrome)
return;
const screenName = _currentScreenName();
if (!screenName)
return;
ConnectedModeState.setModalBody(screenName, _connectedChromeX, _connectedChromeY, _connectedChromeWidth, _connectedChromeHeight, _chromeClaimId);
modalChrome.updateBody(_connectedChromeX, _connectedChromeY, _connectedChromeWidth, _connectedChromeHeight);
}
function _releaseModalChrome() {
if (!_chromeClaimId)
return;
ConnectedModeState.releaseDockRetract(_chromeClaimId);
const claimId = _chromeClaimId;
_chromeClaimId = "";
const screenName = _currentScreenName();
if (screenName)
ConnectedModeState.clearModalState(screenName, claimId);
modalChrome.release();
}
onFrameOwnsConnectedChromeChanged: _syncModalChromeState()
@@ -351,8 +331,6 @@ Item {
onAlignedWidthChanged: _queueBodySync()
onAlignedHeightChanged: _queueBodySync()
Component.onDestruction: _releaseModalChrome()
Connections {
target: contentWindow
function onVisibleChanged() {
@@ -579,13 +557,17 @@ Item {
}
}
if (!needsReset)
if (!needsReset) {
if (root.spotlightOpen)
root._queueFullSync();
return;
}
const newScreen = CompositorService.getFocusedScreen() ?? Quickshell.screens[0];
if (!newScreen)
return;
root._releaseModalChrome();
root._windowEnabled = false;
backgroundWindow.screen = newScreen;
contentWindow.screen = newScreen;
+83 -4
View File
@@ -45,6 +45,7 @@ PanelWindow {
readonly property int _windowRegionWidth: win._regionInt(win.width)
readonly property int _windowRegionHeight: win._regionInt(win.height)
readonly property string _screenName: win.targetScreen ? win.targetScreen.name : ""
readonly property int _surfaceRevision: Number(ConnectedModeState.surfaceRevisions[win._screenName] || 0)
readonly property var _dockState: ConnectedModeState.dockStates[win._screenName] || ConnectedModeState.emptyDockState
readonly property var _dockSlide: ConnectedModeState.dockSlides[win._screenName] || ({
"x": 0,
@@ -150,6 +151,8 @@ PanelWindow {
readonly property real _surfaceRadius: Theme.connectedSurfaceRadius
readonly property real _seamOverlap: Theme.hairline(win._dpr)
readonly property bool _disableLayer: Quickshell.env("DMS_DISABLE_LAYER") === "true" || Quickshell.env("DMS_DISABLE_LAYER") === "1"
property bool _surfaceRefreshNeedsLayerRecreate: false
property bool _surfaceLayerRecoveryActive: false
function _regionInt(value) {
return Math.max(0, Math.round(Theme.px(value, win._dpr)));
@@ -1187,12 +1190,14 @@ PanelWindow {
return (arcCorner === "topLeft" || arcCorner === "topRight") ? connectorY - r : connectorY + connectorHeight - r;
}
function _buildBlur() {
function _buildBlur(forceRepublish) {
try {
if (!BlurService.enabled || !SettingsData.frameBlurEnabled || !win._frameActive || !win.visible) {
win.BackgroundEffect.blurRegion = null;
return;
}
if (forceRepublish)
win.BackgroundEffect.blurRegion = null;
win.BackgroundEffect.blurRegion = _staticBlurRegion;
} catch (e) {
win.log.warn("Failed to set blur region:", e);
@@ -1216,7 +1221,54 @@ PanelWindow {
blurRebuildAction.schedule();
}
function _runBlurRebuild() {
_buildBlur();
_buildBlur(false);
}
function _republishFrameBlur() {
_buildBlur(true);
}
function _requestContentUpdate() {
try {
if (win.contentItem && typeof win.contentItem.update === "function")
win.contentItem.update();
} catch (e) {}
}
function _scheduleSurfaceRefresh(recreateLayer) {
if (recreateLayer)
_surfaceRefreshNeedsLayerRecreate = true;
surfaceRefreshAction.restart();
}
function _runSurfaceRefresh() {
if (!win.visible)
return;
if (_surfaceRefreshNeedsLayerRecreate) {
_surfaceRefreshNeedsLayerRecreate = false;
if (win._connectedActive && !win._disableLayer && (Theme.elevationEnabled || win._surfaceOpacity < 1)) {
_surfaceLayerRecoveryActive = true;
surfaceLayerRestoreAction.restart();
}
}
_requestContentUpdate();
_republishFrameBlur();
}
function _finishSurfaceLayerRecovery() {
_surfaceLayerRecoveryActive = false;
_requestContentUpdate();
_republishFrameBlur();
}
DeferredAction {
id: surfaceRefreshAction
onTriggered: win._runSurfaceRefresh()
}
DeferredAction {
id: surfaceLayerRestoreAction
onTriggered: win._finishSurfaceLayerRecovery()
}
Connections {
@@ -1263,14 +1315,41 @@ PanelWindow {
onVisibleChanged: {
if (visible) {
win._scheduleBlurRebuild();
win._scheduleSurfaceRefresh(false);
} else {
surfaceRefreshAction.cancel();
surfaceLayerRestoreAction.cancel();
_surfaceLayerRecoveryActive = false;
_surfaceRefreshNeedsLayerRecreate = false;
_teardownBlur();
}
}
Component.onCompleted: win._scheduleBlurRebuild()
on_SurfaceRevisionChanged: win._scheduleSurfaceRefresh(false)
onResourcesLost: {
blurRebuildAction.cancel();
surfaceRefreshAction.cancel();
surfaceLayerRestoreAction.cancel();
_surfaceRefreshNeedsLayerRecreate = true;
if (win._connectedActive && !win._disableLayer && (Theme.elevationEnabled || win._surfaceOpacity < 1))
_surfaceLayerRecoveryActive = true;
win._teardownBlur();
}
onWindowConnected: {
win._scheduleSurfaceRefresh(true);
win._scheduleBlurRebuild();
}
Component.onCompleted: {
win._scheduleBlurRebuild();
win._scheduleSurfaceRefresh(false);
}
Component.onDestruction: {
blurRebuildAction.cancel();
surfaceRefreshAction.cancel();
surfaceLayerRestoreAction.cancel();
win._teardownBlur();
}
@@ -1290,7 +1369,7 @@ PanelWindow {
visible: win._connectedActive
opacity: win._surfaceOpacity
// 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.enabled: win._connectedActive && !win._surfaceLayerRecoveryActive && !win._disableLayer && (Theme.elevationEnabled || win._surfaceOpacity < 1)
layer.smooth: false
layer.effect: MultiEffect {
+52 -12
View File
@@ -220,14 +220,20 @@ Item {
function _publishConnectedChromeState(forceClaim, visibleOverride) {
if (!root.frameOwnsConnectedChrome || !root.screen || !_chromeClaimId)
return;
return false;
const screenName = root.screen.name;
const isCurrent = PopoutManager.isCurrentPopout(popoutHandle, screenName);
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
if (!isCurrent)
return false;
forceClaim = true;
} else if (forceClaim && !isCurrent) {
return false;
}
const state = _connectedChromeState(visibleOverride);
if (forceClaim || !ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
ConnectedModeState.claimPopout(_chromeClaimId, state);
} else {
ConnectedModeState.updatePopout(_chromeClaimId, state);
}
return forceClaim ? ConnectedModeState.claimPopout(_chromeClaimId, state) : ConnectedModeState.updatePopout(_chromeClaimId, state);
}
function _releaseConnectedChromeState() {
@@ -252,9 +258,12 @@ Item {
}
if (!contentWindow.visible && !shouldBeVisible)
return;
if (!_chromeClaimId)
if (!_chromeClaimId) {
if (!PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
return;
_chromeClaimId = _nextChromeClaimId();
_publishConnectedChromeState(contentWindow.visible && !ConnectedModeState.hasPopoutOwner(_chromeClaimId));
}
_publishConnectedChromeState(!ConnectedModeState.hasPopoutOwner(_chromeClaimId));
}
function _syncPopoutAnim(axis) {
@@ -267,6 +276,11 @@ Item {
const syncY = axis === "y" && (barSide === "top" || barSide === "bottom");
if (!syncX && !syncY)
return;
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
_queueFullSync();
return;
}
ConnectedModeState.setPopoutAnim(_chromeClaimId, syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
}
@@ -275,6 +289,11 @@ Item {
return;
if (!contentWindow.visible && !shouldBeVisible)
return;
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
_queueFullSync();
return;
}
ConnectedModeState.setPopoutBody(_chromeClaimId, root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
}
@@ -326,10 +345,13 @@ Item {
Connections {
target: contentWindow
function onVisibleChanged() {
if (contentWindow.visible)
if (contentWindow.visible) {
if (!root._chromeClaimId)
root._chromeClaimId = root._nextChromeClaimId();
root._publishConnectedChromeState(true);
else
} else {
root._releaseConnectedChromeState();
}
}
}
@@ -337,7 +359,7 @@ Item {
target: SettingsData
function onConnectedFrameModeActiveChanged() {
if (root.frameOwnsConnectedChrome) {
if (contentWindow.visible || root.shouldBeVisible) {
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name)) {
if (!root._chromeClaimId)
root._chromeClaimId = root._nextChromeClaimId();
root._publishConnectedChromeState(true);
@@ -351,6 +373,22 @@ Item {
}
}
Connections {
target: ConnectedModeState
function onPopoutOwnerIdChanged() {
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name) && !ConnectedModeState.hasPopoutOwner(root._chromeClaimId))
root._queueFullSync();
}
}
Connections {
target: PopoutManager
function onPopoutChanged() {
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name))
root._queueFullSync();
}
}
readonly property bool frameOwnsConnectedChrome: CompositorService.usesConnectedFrameChromeForScreen(root.screen)
readonly property bool usesConnectedSurfaceChrome: Theme.isConnectedEffect && !CompositorService.connectedFrameBlockedOnScreen(root.screen)
readonly property bool usesLocalConnectedSurfaceChrome: usesConnectedSurfaceChrome && !frameOwnsConnectedChrome
@@ -373,6 +411,7 @@ Item {
contentWindow.visible = false;
}
_lastOpenedScreen = screen;
PopoutManager.showPopout(popoutHandle);
if (contentContainer) {
// Snap morph closed only on a fresh open; on screen-change re-open we stay at 1
@@ -408,7 +447,6 @@ Item {
animationsEnabled = true;
shouldBeVisible = true;
if (shouldBeVisible && screen) {
PopoutManager.showPopout(popoutHandle);
opened();
}
}
@@ -440,6 +478,8 @@ Item {
}
if (!screenStillExists) {
close();
} else {
root._queueFullSync();
}
}
}
+45 -4
View File
@@ -34,6 +34,11 @@ Item {
targetWindow.BackgroundEffect.blurRegion = _active ? blurRegion : null;
}
function _clear() {
if (targetWindow)
targetWindow.BackgroundEffect.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
@@ -45,20 +50,56 @@ Item {
targetWindow.BackgroundEffect.blurRegion = _active ? blurRegion : null;
}
on_ActiveChanged: _apply()
onTargetWindowChanged: _apply()
function _scheduleLifecycleKick() {
lifecycleKickAction.restart();
}
function _runLifecycleKick() {
if (!targetWindow)
return;
if (targetWindow.visible)
kick();
else
_apply();
}
on_ActiveChanged: {
if (_active)
_scheduleLifecycleKick();
else
_clear();
}
onTargetWindowChanged: {
lifecycleKickAction.cancel();
_apply();
}
DeferredAction {
id: lifecycleKickAction
onTriggered: root._runLifecycleKick()
}
Connections {
target: root.targetWindow ?? null
ignoreUnknownSignals: true
function onVisibleChanged() {
if (root.targetWindow && root.targetWindow.visible)
root._apply();
root._scheduleLifecycleKick();
else
root._clear();
}
function onResourcesLost() {
lifecycleKickAction.cancel();
root._clear();
}
function onWindowConnected() {
root._scheduleLifecycleKick();
}
}
Component.onCompleted: _apply()
Component.onCompleted: _scheduleLifecycleKick()
Component.onDestruction: {
lifecycleKickAction.cancel();
if (targetWindow)
targetWindow.BackgroundEffect.blurRegion = null;
}