frame: keep the frame on Hyprland special workspaces

fixes #3156
This commit is contained in:
bbedward committed 2026-08-27 09:49:31 -04:00
1 parent fc42786fec
commit 80c85bf929
3 files changed
+15 -112

No files matched your search

+3 -98
View File
@@ -177,7 +177,6 @@ Singleton {
// Avoid reassigning (and invalidating bindings) when contents are equivalent.
if (!_toplevelListEquivalent(next, sortedToplevels))
sortedToplevels = next;
_recomputeFrameBlocked();
toplevelsChanged();
}
}
@@ -231,10 +230,8 @@ Singleton {
if (event.name === "workspace" || event.name === "workspacev2" || event.name === "focusedmon" || event.name === "focusedmonv2" || event.name === "activespecial")
Hyprland.refreshMonitors();
} catch (e) {}
if (event.name === "activespecial") {
if (event.name === "activespecial")
root.updateHyprlandVisibleSpecialWorkspaces(event);
root._recomputeFrameBlocked();
}
root.scheduleSort();
}
}
@@ -244,10 +241,6 @@ Singleton {
function onWindowsChanged() {
root.scheduleSort();
}
// Workspace switches affect the fullscreen check's active workspace.
function onAllWorkspacesChanged() {
root._recomputeFrameBlocked();
}
}
Component.onCompleted: {
@@ -255,7 +248,6 @@ Singleton {
detectCompositor();
updateHyprlandVisibleSpecialWorkspaces(null);
scheduleSort();
_recomputeFrameBlocked();
Qt.callLater(() => {
NiriService.generateNiriLayoutConfig();
HyprlandService.generateLayoutConfig();
@@ -608,92 +600,9 @@ Singleton {
}
}
function hyprlandSpecialWorkspaceBlocksConnectedFrame(screenOrName) {
const screenName = _screenName(screenOrName);
if (!isHyprland || !screenName || !Hyprland.toplevels?.values)
return false;
const visibleSpecialWorkspace = hyprlandVisibleSpecialWorkspaceOnScreen(screenName);
if (!visibleSpecialWorkspace)
return false;
try {
for (const t of Hyprland.toplevels.values) {
const monName = t.monitor?.name ?? t.lastIpcObject?.monitor ?? "";
if (monName !== screenName)
continue;
const wsName = _normalizeSpecialWorkspaceName(t.workspace?.name ?? t.lastIpcObject?.workspace?.name ?? "");
if (!wsName || wsName !== visibleSpecialWorkspace)
continue;
if (_hyprlandToplevelMapped(t))
return true;
}
} catch (e) {
log.warn("hyprlandSpecialWorkspaceBlocksConnectedFrame failed:", e);
}
return false;
}
// Per-screen cache for connectedFrameBlockedOnScreen to avoid recomputing on every consumer binding.
property var frameBlockedByScreen: ({})
function _recomputeFrameBlocked() {
const screens = Quickshell.screens || [];
const next = {};
let changed = false;
for (let i = 0; i < screens.length; i++) {
const name = screens[i]?.name;
if (!name)
continue;
const blocked = hyprlandSpecialWorkspaceBlocksConnectedFrame(name);
next[name] = blocked;
if (frameBlockedByScreen[name] !== blocked)
changed = true;
}
if (!changed) {
for (const name in frameBlockedByScreen) {
if (!(name in next)) {
changed = true;
break;
}
}
}
if (changed)
frameBlockedByScreen = next;
}
function connectedFrameBlockedOnScreen(screenOrName) {
const screenName = _screenName(screenOrName);
if (!screenName)
return false;
const cached = frameBlockedByScreen[screenName];
if (cached !== undefined)
return cached;
return hyprlandSpecialWorkspaceBlocksConnectedFrame(screenName);
}
Connections {
target: ToplevelManager
function onActiveToplevelChanged() {
root._recomputeFrameBlocked();
}
}
// Track active toplevel's fullscreen/activated state directly (no per-property signals from ToplevelManager).
Connections {
target: ToplevelManager.activeToplevel
ignoreUnknownSignals: true
function onFullscreenChanged() {
root._recomputeFrameBlocked();
}
function onActivatedChanged() {
root._recomputeFrameBlocked();
}
}
Connections {
target: Quickshell
function onScreensChanged() {
root._recomputeFrameBlocked();
root.refreshHyprlandMonitorLayout();
}
}
@@ -731,18 +640,14 @@ Singleton {
}
function frameWindowVisibleForScreen(screenOrName) {
if (!frameConfiguredForScreen(screenOrName))
return false;
return !connectedFrameBlockedOnScreen(screenOrName);
return frameConfiguredForScreen(screenOrName);
}
function usesConnectedFrameChromeForScreen(screenOrName) {
return FrameTransitionState.effectiveConnectedFrameModeActive && frameWindowVisibleForScreen(screenOrName);
}
// Connected mode renders the bar inside the frame surface. True whenever connected
// chrome is configured for the screen, independent of the fullscreen block, so the
// standalone bar surface stays suppressed while the frame-hosted bar hides with the frame.
// Connected mode renders the bar inside the frame surface.
function frameHostsSurfacesForScreen(screenOrName) {
return FrameTransitionState.effectiveConnectedFrameModeActive && frameConfiguredForScreen(screenOrName);
}
-2
View File
@@ -137,8 +137,6 @@ Item {
}
}
// Backend re-resolution on toplevel activity is covered by CompositorService.frameBlockedByScreen.
function _usesConnectedBackendForScreen(targetScreen) {
return CompositorService.usesConnectedFrameChromeForScreen(targetScreen);
}
+12 -12
View File
@@ -113,30 +113,30 @@ Item {
active: contentWindow.visible || root.shouldBeVisible
presented: contentWindow.visible || root.shouldBeVisible
renewTokenOnRecovery: false
isCurrentOwner: function(name) {
isCurrentOwner: function (name) {
return PopoutManager.isCurrentPopout(root.popoutHandle, name);
}
hasOwner: function(_name, ownerId) {
hasOwner: function (_name, ownerId) {
return ConnectedModeState.hasPopoutOwner(ownerId);
}
statePresent: function(name, ownerId) {
statePresent: function (name, ownerId) {
return ConnectedModeState.hasPopoutOwner(ownerId) && ConnectedModeState.hasSurfaceDescriptor(name, "popout", ownerId);
}
claimState: function(_name, state, ownerId) {
claimState: function (_name, state, ownerId) {
return ConnectedModeState.claimPopout(ownerId, state);
}
ensureState: function(_name, state, ownerId) {
ensureState: function (_name, state, ownerId) {
if (!ConnectedModeState.hasPopoutOwner(ownerId))
return false;
return ConnectedModeState.updatePopout(ownerId, state);
}
releaseState: function(_name, ownerId) {
releaseState: function (_name, ownerId) {
return ConnectedModeState.releasePopout(ownerId);
}
updateAnimationState: function(_name, ownerId, animX, animY) {
updateAnimationState: function (_name, ownerId, animX, animY) {
return ConnectedModeState.setPopoutAnim(ownerId, animX, animY);
}
updateBodyState: function(_name, ownerId, bodyX, bodyY, bodyW, bodyH) {
updateBodyState: function (_name, ownerId, bodyX, bodyY, bodyW, bodyH) {
return ConnectedModeState.setPopoutBody(ownerId, bodyX, bodyY, bodyW, bodyH);
}
onClaimIdChanged: root._resetPublishedBody()
@@ -425,7 +425,7 @@ Item {
}
readonly property bool frameOwnsConnectedChrome: CompositorService.usesConnectedFrameChromeForScreen(root.screen)
readonly property bool usesConnectedSurfaceChrome: Theme.isConnectedEffect && !CompositorService.connectedFrameBlockedOnScreen(root.screen)
readonly property bool usesConnectedSurfaceChrome: Theme.isConnectedEffect
readonly property bool usesLocalConnectedSurfaceChrome: usesConnectedSurfaceChrome && !frameOwnsConnectedChrome
onFrameOwnsConnectedChromeChanged: _syncPopoutChromeState()
@@ -708,8 +708,8 @@ Item {
readonly property bool _settlingToOpen: fullHeightSurface && shouldBeVisible && morph.running
onAlignedYChanged: {
geometryYSpring.retarget(root.alignedY)
_queueFullSync()
geometryYSpring.retarget(root.alignedY);
_queueFullSync();
}
// Morph transition coordinates to animate travel between popouts during switch.
@@ -836,7 +836,7 @@ Item {
}
onAlignedHeightChanged: {
geometryHSpring.retarget(root.alignedHeight)
geometryHSpring.retarget(root.alignedHeight);
_queueFullSync();
if (!suspendShadowWhileResizing || !shouldBeVisible)
return;