diff --git a/quickshell/Common/ConnectorGeometry.js b/quickshell/Common/ConnectorGeometry.js index 9116f451..c24bd7db 100644 --- a/quickshell/Common/ConnectorGeometry.js +++ b/quickshell/Common/ConnectorGeometry.js @@ -10,6 +10,10 @@ function isVertical(barSide) { return barSide === "left" || barSide === "right"; } +function isHorizontal(barSide) { + return barSide === "top" || barSide === "bottom"; +} + function connectorWidth(barSide, spacing, radius) { return isVertical(barSide) ? (spacing + radius) : radius; } diff --git a/quickshell/Common/Theme.qml b/quickshell/Common/Theme.qml index 1116c344..0ce378d0 100644 --- a/quickshell/Common/Theme.qml +++ b/quickshell/Common/Theme.qml @@ -985,7 +985,11 @@ Singleton { "expressiveEffects": [0.34, 0.8, 0.34, 1, 1, 1] } - // Delegates to AnimVariants.qml for curves, timing, scale, and offsets. + // ─── Animation variant proxy ────────────────────────────────────────────── + // Theme is the canonical access point for animation variant state. The + // aliases below forward to AnimVariants.qml so consumers don't need two + // imports. ~200 call sites read through Theme.variantEnterCurve / + // Theme.isConnectedEffect / etc. — do NOT migrate to AnimVariants directly. readonly property list variantEnterCurve: AnimVariants.variantEnterCurve readonly property list variantExitCurve: AnimVariants.variantExitCurve readonly property list variantModalEnterCurve: AnimVariants.variantModalEnterCurve @@ -999,25 +1003,28 @@ Singleton { readonly property bool isDepthEffect: AnimVariants.isDepthEffect readonly property bool isConnectedEffect: AnimVariants.isConnectedEffect readonly property real connectedCornerRadius: { - if (typeof SettingsData === "undefined") return 12; + if (typeof SettingsData === "undefined") + return 12; return SettingsData.connectedFrameModeActive ? SettingsData.frameRounding : cornerRadius; } readonly property color connectedSurfaceColor: { if (typeof SettingsData === "undefined") return withAlpha(surfaceContainer, popupTransparency); - return isConnectedEffect - ? Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity) - : withAlpha(surfaceContainer, popupTransparency); + return isConnectedEffect ? Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity) : withAlpha(surfaceContainer, popupTransparency); } readonly property real connectedSurfaceRadius: isConnectedEffect ? connectedCornerRadius : cornerRadius - readonly property bool connectedSurfaceBlurEnabled: (typeof SettingsData === "undefined") - ? true - : (!isConnectedEffect || SettingsData.frameBlurEnabled) + readonly property bool connectedSurfaceBlurEnabled: (typeof SettingsData === "undefined") ? true : (!isConnectedEffect || SettingsData.frameBlurEnabled) readonly property real effectScaleCollapsed: AnimVariants.effectScaleCollapsed readonly property real effectAnimOffset: AnimVariants.effectAnimOffset - function variantDuration(baseDuration, entering) { return AnimVariants.variantDuration(baseDuration, entering); } - function variantExitCleanupPadding() { return AnimVariants.variantExitCleanupPadding(); } - function variantCloseInterval(baseDuration) { return AnimVariants.variantCloseInterval(baseDuration); } + function variantDuration(baseDuration, entering) { + return AnimVariants.variantDuration(baseDuration, entering); + } + function variantExitCleanupPadding() { + return AnimVariants.variantExitCleanupPadding(); + } + function variantCloseInterval(baseDuration) { + return AnimVariants.variantCloseInterval(baseDuration); + } readonly property var animationPresetDurations: { "none": 0, diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml index fcd12c92..cf6f045f 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalConnected.qml @@ -96,8 +96,13 @@ Item { return SettingsData.frameEdgeInsetForSide(effectiveScreen, side); } - // frameEdgeInsetForSide is the full inset; do not add frameBarSize - readonly property real _connectedModalX: { + // frameEdgeInsetForSide is the full inset; do not add frameBarSize. + // Positions the modal flush to the emerge side, centered on the cross axis. + readonly property var _connectedModalPos: { + const fallback = { + "x": (screenWidth - modalWidth) / 2, + "y": (screenHeight - modalHeight) / 2 + }; switch (resolvedConnectedBarSide) { case "top": case "bottom": @@ -105,36 +110,28 @@ Item { const insetL = _frameEdgeInset("left"); const insetR = _frameEdgeInset("right"); const usable = Math.max(0, screenWidth - insetL - insetR); - return insetL + Math.max(0, (usable - modalWidth) / 2); + return { + "x": insetL + Math.max(0, (usable - modalWidth) / 2), + "y": resolvedConnectedBarSide === "top" ? _frameEdgeInset("top") : screenHeight - modalHeight - _frameEdgeInset("bottom") + }; } - case "left": - return _frameEdgeInset("left"); - case "right": - return screenWidth - modalWidth - _frameEdgeInset("right"); - } - return (screenWidth - modalWidth) / 2; - } - - readonly property real _connectedModalY: { - switch (resolvedConnectedBarSide) { - case "top": - return _frameEdgeInset("top"); - case "bottom": - return screenHeight - modalHeight - _frameEdgeInset("bottom"); case "left": case "right": { const insetT = _frameEdgeInset("top"); const insetB = _frameEdgeInset("bottom"); const usable = Math.max(0, screenHeight - insetT - insetB); - return insetT + Math.max(0, (usable - modalHeight) / 2); + return { + "x": resolvedConnectedBarSide === "left" ? _frameEdgeInset("left") : screenWidth - modalWidth - _frameEdgeInset("right"), + "y": insetT + Math.max(0, (usable - modalHeight) / 2) + }; } } - return (screenHeight - modalHeight) / 2; + return fallback; } - readonly property real modalX: frameOwnsConnectedChrome ? _connectedModalX : ((screenWidth - modalWidth) / 2) - readonly property real modalY: frameOwnsConnectedChrome ? _connectedModalY : ((screenHeight - modalHeight) / 2) + readonly property real modalX: frameOwnsConnectedChrome ? _connectedModalPos.x : ((screenWidth - modalWidth) / 2) + readonly property real modalY: frameOwnsConnectedChrome ? _connectedModalPos.y : ((screenHeight - modalHeight) / 2) readonly property bool connectedSurfaceOverride: Theme.isConnectedEffect readonly property int launcherAnimationDuration: Theme.isConnectedEffect ? Theme.popoutAnimationDuration : Theme.modalAnimationDuration diff --git a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml index 0bb2fe81..237c967b 100644 --- a/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml +++ b/quickshell/Modals/DankLauncherV2/DankLauncherV2ModalStandalone.qml @@ -153,34 +153,27 @@ Item { _ensureContentLoadedAndInitialize(query || "", mode || ""); } - function show() { + function _openCommon(query, mode) { closeCleanupTimer.stop(); - - var focusedScreen = CompositorService.getFocusedScreen(); + const focusedScreen = CompositorService.getFocusedScreen(); if (focusedScreen && launcherWindow.screen !== focusedScreen) { spotlightOpen = false; isClosing = false; launcherWindow.screen = focusedScreen; - Qt.callLater(() => root._finishShow("", "")); + Qt.callLater(() => root._finishShow(query, mode)); return; } - - _finishShow("", ""); + _finishShow(query, mode); } + function show() { + _openCommon("", ""); + } function showWithQuery(query) { - closeCleanupTimer.stop(); - - var focusedScreen = CompositorService.getFocusedScreen(); - if (focusedScreen && launcherWindow.screen !== focusedScreen) { - spotlightOpen = false; - isClosing = false; - launcherWindow.screen = focusedScreen; - Qt.callLater(() => root._finishShow(query, "")); - return; - } - - _finishShow(query, ""); + _openCommon(query, ""); + } + function showWithMode(mode) { + _openCommon("", mode); } function hide() { @@ -202,30 +195,6 @@ Item { spotlightOpen ? hide() : show(); } - function showWithMode(mode) { - closeCleanupTimer.stop(); - - var focusedScreen = CompositorService.getFocusedScreen(); - if (focusedScreen && launcherWindow.screen !== focusedScreen) { - spotlightOpen = false; - isClosing = false; - launcherWindow.screen = focusedScreen; - Qt.callLater(() => root._finishShow("", mode)); - return; - } - - spotlightOpen = true; - isClosing = false; - openedFromOverview = false; - - keyboardActive = true; - ModalManager.openModal(modalHandle); - if (useHyprlandFocusGrab) - focusGrab.active = true; - - _ensureContentLoadedAndInitialize("", mode); - } - function toggleWithMode(mode) { if (spotlightOpen) { hide(); diff --git a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml index 5909536b..609f8661 100644 --- a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml +++ b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml @@ -44,43 +44,6 @@ Item { property int __volumeHoverCount: 0 - readonly property bool directionalEffect: Theme.isDirectionalEffect - readonly property bool depthEffect: Theme.isDepthEffect - - function panelMotionX(panelWidth, active) { - if (active) - return 0; - if (directionalEffect) { - const travel = Math.max(Theme.effectAnimOffset, panelWidth * 0.85); - return isRightEdge ? -travel : travel; - } - if (depthEffect) { - const travel = Math.max(Theme.effectAnimOffset * 0.7, panelWidth * 0.32); - return isRightEdge ? -travel : travel; - } - return 0; - } - - function panelMotionY(panelType, panelHeight, active) { - if (active) - return 0; - if (directionalEffect) { - if (panelType === 2) - return panelHeight * 0.08; - if (panelType === 3) - return -panelHeight * 0.08; - return 0; - } - if (depthEffect) { - if (panelType === 2) - return panelHeight * 0.04; - if (panelType === 3) - return -panelHeight * 0.04; - return 0; - } - return 0; - } - function volumeAreaEntered() { __volumeHoverCount++; panelEntered(); @@ -123,8 +86,8 @@ Item { visible: dropdownType === 1 && volumeAvailable width: 60 height: 180 - x: (isRightEdge ? anchorPos.x : anchorPos.x - width) + panelMotionX(width, dropdownType === 1) - y: anchorPos.y - height / 2 + panelMotionY(1, height, dropdownType === 1) + x: isRightEdge ? anchorPos.x : anchorPos.x - width + y: anchorPos.y - height / 2 radius: Theme.cornerRadius * 2 color: Theme.floatingSurface border.color: Theme.outlineStrong @@ -151,22 +114,6 @@ Item { } } - Behavior on x { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1) - easing.type: Easing.BezierSpline - easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve - } - } - - Behavior on y { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1) - easing.type: Easing.BezierSpline - easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve - } - } - ElevationShadow { id: volumeShadowLayer anchors.fill: parent @@ -277,11 +224,11 @@ Item { Rectangle { id: audioDevicesPanel - visible: dropdownType === 2 && activePlayer !== null + visible: dropdownType === 2 width: 280 height: Math.max(200, Math.min(280, availableDevices.length * 50 + 100)) - x: (isRightEdge ? anchorPos.x : anchorPos.x - width) + panelMotionX(width, dropdownType === 2) - y: anchorPos.y - height / 2 + panelMotionY(2, height, dropdownType === 2) + x: isRightEdge ? anchorPos.x : anchorPos.x - width + y: anchorPos.y - height / 2 radius: Theme.cornerRadius * 2 color: Theme.floatingSurface border.color: Theme.outlineStrong @@ -308,22 +255,6 @@ Item { } } - Behavior on x { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2) - easing.type: Easing.BezierSpline - easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve - } - } - - Behavior on y { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2) - easing.type: Easing.BezierSpline - easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve - } - } - ElevationShadow { id: audioDevicesShadowLayer anchors.fill: parent @@ -451,8 +382,8 @@ Item { visible: dropdownType === 3 width: 240 height: Math.max(180, Math.min(240, (allPlayers?.length || 0) * 50 + 80)) - x: (isRightEdge ? anchorPos.x : anchorPos.x - width) + panelMotionX(width, dropdownType === 3) - y: anchorPos.y - height / 2 + panelMotionY(3, height, dropdownType === 3) + x: isRightEdge ? anchorPos.x : anchorPos.x - width + y: anchorPos.y - height / 2 radius: Theme.cornerRadius * 2 color: Theme.floatingSurface border.color: Theme.outlineStrong @@ -479,22 +410,6 @@ Item { } } - Behavior on x { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3) - easing.type: Easing.BezierSpline - easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve - } - } - - Behavior on y { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3) - easing.type: Easing.BezierSpline - easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve - } - } - ElevationShadow { id: playersShadowLayer anchors.fill: parent diff --git a/quickshell/Modules/Frame/FrameWindow.qml b/quickshell/Modules/Frame/FrameWindow.qml index 67eca814..7ab49cce 100644 --- a/quickshell/Modules/Frame/FrameWindow.qml +++ b/quickshell/Modules/Frame/FrameWindow.qml @@ -80,13 +80,13 @@ PanelWindow { readonly property real _effectivePopoutMaxCcr: Math.max(win._effectivePopoutStartCcr, win._effectivePopoutEndCcr) readonly property real _effectivePopoutFarExtent: Math.max(win._effectivePopoutFarStartCcr, win._effectivePopoutFarEndCcr) readonly property real _effectiveNotifCcr: { - const isHoriz = win._notifState.barSide === "top" || win._notifState.barSide === "bottom"; + const isHoriz = ConnectorGeometry.isHorizontal(win._notifState.barSide); const crossSize = isHoriz ? _notifBodyBlurAnchor.width : _notifBodyBlurAnchor.height; const extent = isHoriz ? _notifBodyBlurAnchor.height : _notifBodyBlurAnchor.width; return Theme.snap(Math.max(0, Math.min(win._ccr, win._surfaceRadius, extent, crossSize / 2)), win._dpr); } readonly property real _effectiveNotifFarCcr: { - const isHoriz = win._notifState.barSide === "top" || win._notifState.barSide === "bottom"; + const isHoriz = ConnectorGeometry.isHorizontal(win._notifState.barSide); const crossSize = isHoriz ? _notifBodySceneBlurAnchor.width : _notifBodySceneBlurAnchor.height; return Theme.snap(Math.max(0, Math.min(win._ccr, win._surfaceRadius, crossSize / 2)), win._dpr); } @@ -97,13 +97,13 @@ PanelWindow { readonly property real _effectiveNotifMaxCcr: Math.max(win._effectiveNotifStartCcr, win._effectiveNotifEndCcr) readonly property real _effectiveNotifFarExtent: Math.max(win._effectiveNotifFarStartCcr, win._effectiveNotifFarEndCcr) readonly property real _effectiveModalCcr: { - const isHoriz = win._modalState.barSide === "top" || win._modalState.barSide === "bottom"; + const isHoriz = ConnectorGeometry.isHorizontal(win._modalState.barSide); const crossSize = isHoriz ? _modalBodyBlurAnchor.width : _modalBodyBlurAnchor.height; const extent = isHoriz ? _modalBodyBlurAnchor.height : _modalBodyBlurAnchor.width; return Theme.snap(Math.max(0, Math.min(win._ccr, win._surfaceRadius, extent, crossSize / 2)), win._dpr); } readonly property real _effectiveModalFarCcr: { - const isHoriz = win._modalState.barSide === "top" || win._modalState.barSide === "bottom"; + const isHoriz = ConnectorGeometry.isHorizontal(win._modalState.barSide); const crossSize = isHoriz ? _modalBodyBlurAnchor.width : _modalBodyBlurAnchor.height; return Theme.snap(Math.max(0, Math.min(win._ccr, win._surfaceRadius, crossSize / 2)), win._dpr); } @@ -428,7 +428,7 @@ PanelWindow { readonly property bool _active: win._frameActive && win._modalState.visible && win._modalState.bodyW > 0 && win._modalState.bodyH > 0 // Clamp animX/Y so the blur body shrinks toward the bar edge (same as _popoutBodyBlurAnchor). - readonly property real _dyClamp: (win._modalState.barSide === "top" || win._modalState.barSide === "bottom") ? Math.max(-win._modalState.bodyH, Math.min(win._modalState.animY, win._modalState.bodyH)) : 0 + readonly property real _dyClamp: ConnectorGeometry.isHorizontal(win._modalState.barSide) ? Math.max(-win._modalState.bodyH, Math.min(win._modalState.animY, win._modalState.bodyH)) : 0 readonly property real _dxClamp: (win._modalState.barSide === "left" || win._modalState.barSide === "right") ? Math.max(-win._modalState.bodyW, Math.min(win._modalState.animX, win._modalState.bodyW)) : 0 x: _active ? Theme.snap(win._modalState.bodyX + (win._modalState.barSide === "right" ? _dxClamp : 0), win._dpr) : 0 @@ -600,11 +600,12 @@ PanelWindow { visible: false readonly property bool _active: _notifBodyBlurAnchor._active + readonly property var _scene: _active ? win._notifBodyScene() : null - x: _active ? Theme.snap(win._notifBodySceneX(), win._dpr) : 0 - y: _active ? Theme.snap(win._notifBodySceneY(), win._dpr) : 0 - width: _active ? Theme.snap(win._notifBodySceneWidth(), win._dpr) : 0 - height: _active ? Theme.snap(win._notifBodySceneHeight(), win._dpr) : 0 + x: _scene ? Theme.snap(_scene.x, win._dpr) : 0 + y: _scene ? Theme.snap(_scene.y, win._dpr) : 0 + width: _scene ? Theme.snap(_scene.width, win._dpr) : 0 + height: _scene ? Theme.snap(_scene.height, win._dpr) : 0 } Item { @@ -963,7 +964,7 @@ PanelWindow { function _notifSideUnderlap() { const side = win._notifState.barSide; - return (side === "left" || side === "right") ? win._seamOverlap : 0; + return ConnectorGeometry.isVertical(side) ? win._seamOverlap : 0; } function _notifStartUnderlap() { @@ -974,36 +975,26 @@ PanelWindow { return win._notifState.omitEndConnector ? win._seamOverlap : 0; } - function _notifBodySceneX() { - const side = win._notifState.barSide; - const isHoriz = side === "top" || side === "bottom"; - if (isHoriz) - return _notifBodyBlurAnchor.x - win._notifStartUnderlap(); - return _notifBodyBlurAnchor.x - (side === "left" ? win._notifSideUnderlap() : 0); - } - - function _notifBodySceneY() { - const side = win._notifState.barSide; - const isHoriz = side === "top" || side === "bottom"; - if (isHoriz) - return _notifBodyBlurAnchor.y; - return _notifBodyBlurAnchor.y - win._notifStartUnderlap(); - } - - function _notifBodySceneWidth() { - const side = win._notifState.barSide; - const isHoriz = side === "top" || side === "bottom"; - if (isHoriz) - return _notifBodyBlurAnchor.width + win._notifStartUnderlap() + win._notifEndUnderlap(); - return _notifBodyBlurAnchor.width + win._notifSideUnderlap(); - } - - function _notifBodySceneHeight() { - const side = win._notifState.barSide; - const isHoriz = side === "top" || side === "bottom"; - if (isHoriz) - return _notifBodyBlurAnchor.height; - return _notifBodyBlurAnchor.height + win._notifStartUnderlap() + win._notifEndUnderlap(); + // Notif body scene rect, accounting for start/end/side underlaps per bar orientation. + function _notifBodyScene() { + const isHoriz = ConnectorGeometry.isHorizontal(win._notifState.barSide); + const start = win._notifStartUnderlap(); + const end = win._notifEndUnderlap(); + const side = win._notifSideUnderlap(); + if (isHoriz) { + return { + "x": _notifBodyBlurAnchor.x - start, + "y": _notifBodyBlurAnchor.y, + "width": _notifBodyBlurAnchor.width + start + end, + "height": _notifBodyBlurAnchor.height + }; + } + return { + "x": _notifBodyBlurAnchor.x - (win._notifState.barSide === "left" ? side : 0), + "y": _notifBodyBlurAnchor.y - start, + "width": _notifBodyBlurAnchor.width + side, + "height": _notifBodyBlurAnchor.height + start + end + }; } function _notifConnectorRadius(placement) { @@ -1027,7 +1018,7 @@ PanelWindow { } function _dockFillOverlapX() { - return (win._dockState.barSide === "top" || win._dockState.barSide === "bottom") ? win._seamOverlap : 0; + return ConnectorGeometry.isHorizontal(win._dockState.barSide) ? win._seamOverlap : 0; } function _dockFillOverlapY() { @@ -1035,7 +1026,7 @@ PanelWindow { } function _modalArcExtent() { - return (win._modalState.barSide === "top" || win._modalState.barSide === "bottom") ? _modalBodyBlurAnchor.height : _modalBodyBlurAnchor.width; + return ConnectorGeometry.isHorizontal(win._modalState.barSide) ? _modalBodyBlurAnchor.height : _modalBodyBlurAnchor.width; } function _modalBlurCapThickness() { @@ -1100,28 +1091,28 @@ PanelWindow { function _popoutShapeBodyOffsetX() { const side = ConnectedModeState.popoutBarSide; - if (side === "top" || side === "bottom") + if (ConnectorGeometry.isHorizontal(side)) return win._effectivePopoutStartCcr; return side === "right" ? win._effectivePopoutFarExtent : 0; } function _popoutShapeBodyOffsetY() { const side = ConnectedModeState.popoutBarSide; - if (side === "top" || side === "bottom") + if (ConnectorGeometry.isHorizontal(side)) return side === "bottom" ? win._effectivePopoutFarExtent : 0; return win._effectivePopoutStartCcr; } function _popoutShapeWidth() { const side = ConnectedModeState.popoutBarSide; - if (side === "top" || side === "bottom") + if (ConnectorGeometry.isHorizontal(side)) return win._popoutClipWidth() + win._effectivePopoutStartCcr + win._effectivePopoutEndCcr; return win._popoutClipWidth() + win._effectivePopoutFarExtent; } function _popoutShapeHeight() { const side = ConnectedModeState.popoutBarSide; - if (side === "top" || side === "bottom") + if (ConnectorGeometry.isHorizontal(side)) return win._popoutClipHeight() + win._effectivePopoutFarExtent; return win._popoutClipHeight() + win._effectivePopoutStartCcr + win._effectivePopoutEndCcr; } @@ -1163,7 +1154,7 @@ PanelWindow { } function _dockBodyXInChrome() { - return ((win._dockState.barSide === "top" || win._dockState.barSide === "bottom") ? win._dockConnectorRadius() : 0) - win._dockFillOverlapX(); + return (ConnectorGeometry.isHorizontal(win._dockState.barSide) ? win._dockConnectorRadius() : 0) - win._dockFillOverlapX(); } function _dockBodyYInChrome() { diff --git a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml index 9af1ba6f..4eefe1ad 100644 --- a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml +++ b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml @@ -564,11 +564,16 @@ PanelWindow { readonly property real dpr: screenValid ? CompositorService.getScreenScale(win.screen) : 1 readonly property real alignedWidth: Theme.px(Math.max(0, implicitWidth - (windowShadowPad * 2)), dpr) readonly property real alignedHeight: renderedAlignedHeight - onScreenYChanged: popupChromeGeometryChanged() - onScreenChanged: popupChromeGeometryChanged() + onScreenYChanged: if (connectedFrameMode) + popupChromeGeometryChanged() + onScreenChanged: if (connectedFrameMode) + popupChromeGeometryChanged() + // Intentionally unconditional: Manager needs the signal when frame mode toggles off onConnectedFrameModeChanged: popupChromeGeometryChanged() - onAlignedWidthChanged: popupChromeGeometryChanged() - onAlignedHeightChanged: popupChromeGeometryChanged() + onAlignedWidthChanged: if (connectedFrameMode) + popupChromeGeometryChanged() + onAlignedHeightChanged: if (connectedFrameMode) + popupChromeGeometryChanged() Item { id: content diff --git a/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml b/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml index 087171b6..6f59db49 100644 --- a/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml +++ b/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml @@ -127,6 +127,8 @@ Scope { WindowBlur { targetWindow: overlayWindow + // Track the container's scale so blur shrinks with the content + // during exit — otherwise blur pops away one frame after content. readonly property real s: Math.min(1, spotlightContainer.scale) readonly property bool active: spotlightContainer.visible && spotlightContainer.opacity > 0 blurX: spotlightContainer.x + spotlightContainer.width * (1 - s) * 0.5 @@ -214,18 +216,8 @@ Scope { Item { id: spotlightContainer - readonly property bool directionalEffect: Theme.isDirectionalEffect - readonly property bool depthEffect: Theme.isDepthEffect - readonly property real collapsedMotionX: depthEffect ? Theme.effectAnimOffset * 0.25 : 0 - readonly property real collapsedMotionY: { - if (directionalEffect) - return Math.max(height * 0.85, Theme.effectAnimOffset * 1.1); - if (depthEffect) - return Math.max(Theme.effectAnimOffset * 0.8, 30); - return 0; - } - x: Theme.snap((parent.width - width) / 2 + (overlayWindow.shouldShowSpotlight ? 0 : collapsedMotionX), overlayWindow.dpr) - y: Theme.snap((parent.height - height) / 2 + (overlayWindow.shouldShowSpotlight ? 0 : collapsedMotionY), overlayWindow.dpr) + x: Theme.snap((parent.width - width) / 2, overlayWindow.dpr) + y: Theme.snap((parent.height - height) / 2, overlayWindow.dpr) readonly property int baseWidth: { switch (SettingsData.dankLauncherV2Size) { @@ -256,8 +248,8 @@ Scope { readonly property bool animatingOut: niriOverviewScope.isClosing && overlayWindow.isSpotlightScreen - scale: Theme.isDirectionalEffect ? 1 : (overlayWindow.shouldShowSpotlight ? 1.0 : Theme.effectScaleCollapsed) - opacity: Theme.isDirectionalEffect ? 1 : (overlayWindow.shouldShowSpotlight ? 1 : 0) + scale: overlayWindow.shouldShowSpotlight ? 1.0 : 0.96 + opacity: overlayWindow.shouldShowSpotlight ? 1 : 0 visible: overlayWindow.shouldShowSpotlight || animatingOut enabled: overlayWindow.shouldShowSpotlight @@ -267,11 +259,10 @@ Scope { Behavior on scale { id: scaleAnimation - enabled: !Theme.isDirectionalEffect NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight) + duration: Theme.expressiveDurations.fast easing.type: Easing.BezierSpline - easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + easing.bezierCurve: spotlightContainer.visible ? Theme.expressiveCurves.expressiveFastSpatial : Theme.expressiveCurves.standardAccel onRunningChanged: { if (running || !spotlightContainer.animatingOut) return; @@ -281,27 +272,10 @@ Scope { } Behavior on opacity { - enabled: !Theme.isDirectionalEffect NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight) + duration: Theme.expressiveDurations.fast easing.type: Easing.BezierSpline - easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve - } - } - - Behavior on x { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight) - easing.type: Easing.BezierSpline - easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve - } - } - - Behavior on y { - NumberAnimation { - duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight) - easing.type: Easing.BezierSpline - easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve + easing.bezierCurve: spotlightContainer.visible ? Theme.expressiveCurves.expressiveFastSpatial : Theme.expressiveCurves.standardAccel } } diff --git a/quickshell/Widgets/DankPopoutStandalone.qml b/quickshell/Widgets/DankPopoutStandalone.qml index 98e72ce9..a179ba00 100644 --- a/quickshell/Widgets/DankPopoutStandalone.qml +++ b/quickshell/Widgets/DankPopoutStandalone.qml @@ -338,10 +338,9 @@ Item { screen: root.screen visible: false color: "transparent" - Component.onCompleted: { - if (typeof updatesEnabled !== "undefined" && !root.overlayContent) - updatesEnabled = false; - } + // When there's no overlay to render, skip buffer updates. Re-evaluates if + // overlayContent is assigned later (e.g., via a dispatcher forwarding it). + updatesEnabled: root.overlayContent !== null WlrLayershell.namespace: root.layerNamespace + ":background" WlrLayershell.layer: WlrLayershell.Top