diff --git a/Modules/Dock/Dock.qml b/Modules/Dock/Dock.qml index 0dc725ff4..a72f3972c 100644 --- a/Modules/Dock/Dock.qml +++ b/Modules/Dock/Dock.qml @@ -741,7 +741,12 @@ Loader { onExited: { peekHovered = false; showTimer.stop(); - if (!hidden && !dockHovered && !anyAppHovered && !menuHovered) { + if (isStaticMode) { + // Start hideTimer which checks panel.isDockHovered before closing + if (!dockHovered && !anyAppHovered && !menuHovered) { + hideTimer.restart(); + } + } else if (!hidden && !dockHovered && !anyAppHovered && !menuHovered) { hideTimer.restart(); } } @@ -765,30 +770,8 @@ Loader { focusable: false color: "transparent" - property real targetIndicatorOffsetX: peekCenterOffsetX - property real targetIndicatorOffsetY: peekCenterOffsetY - property real animatedIndicatorOffsetX: targetIndicatorOffsetX - property real animatedIndicatorOffsetY: targetIndicatorOffsetY - - onTargetIndicatorOffsetXChanged: animatedIndicatorOffsetX = targetIndicatorOffsetX - onTargetIndicatorOffsetYChanged: animatedIndicatorOffsetY = targetIndicatorOffsetY - - Behavior on animatedIndicatorOffsetX { - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad - } - } - - Behavior on animatedIndicatorOffsetY { - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad - } - } - - margins.top: animatedIndicatorOffsetY - margins.left: animatedIndicatorOffsetX + margins.top: peekCenterOffsetY + margins.left: peekCenterOffsetX WlrLayershell.namespace: "noctalia-dock-indicator-" + (screen?.name || "unknown") WlrLayershell.layer: WlrLayer.Top @@ -797,19 +780,6 @@ Loader { implicitHeight: isVertical ? peekEdgeLength : indicatorThickness implicitWidth: isVertical ? indicatorThickness : peekEdgeLength - Behavior on implicitWidth { - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad - } - } - Behavior on implicitHeight { - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.InOutQuad - } - } - Rectangle { id: indicatorRect anchors.fill: parent @@ -863,21 +833,37 @@ Loader { WlrLayershell.namespace: "noctalia-dock-" + (screen?.name || "unknown") WlrLayershell.exclusionMode: exclusive ? ExclusionMode.Auto : ExclusionMode.Ignore - // Blur behind dock (User Interface → Blur behind) + // Slide animation: content slides inside a fixed window, no margin animation + property int slideDistance: (isVertical ? dockContainerWrapper.contentWidth : dockContainerWrapper.contentHeight) + floatingMargin + 10 + property real slideOffset: hidden ? slideDistance : 0 + + Behavior on slideOffset { + NumberAnimation { + duration: hidden ? hideAnimationDuration : showAnimationDuration + easing.type: hidden ? Easing.InCubic : Easing.OutCubic + } + } + + // Signed slide: positive pushes content toward its edge (off-screen) + readonly property real slideX: dockPosition === "left" ? -slideOffset : dockPosition === "right" ? slideOffset : 0 + readonly property real slideY: dockPosition === "top" ? -slideOffset : dockPosition === "bottom" ? slideOffset : 0 + + // Blur behind dock — offset by slide so it follows the content BackgroundEffect.blurRegion: Settings.data.general.enableBlurBehind ? dockBlurRegion : null Region { id: dockBlurRegion Region { - x: Math.round(dockContainerWrapper.mapFromItem(dockContent.dockContainer, 0, 0).x) - y: Math.round(dockContainerWrapper.mapFromItem(dockContent.dockContainer, 0, 0).y) + x: Math.round(dockContainerWrapper.x + dockContent.dockContainer.x + dockWindow.slideX) + y: Math.round(dockContainerWrapper.y + dockContent.dockContainer.y + dockWindow.slideY) width: Math.round(dockContent.dockContainer.width) height: Math.round(dockContent.dockContainer.height) radius: Style.radiusL } } - implicitWidth: dockContainerWrapper.width - implicitHeight: dockContainerWrapper.height + // Window sized to fit content + slide distance so content can slide off-edge + implicitWidth: dockContainerWrapper.width + (isVertical ? slideDistance : 0) + implicitHeight: dockContainerWrapper.height + (!isVertical ? slideDistance : 0) // Position based on dock setting anchors.top: dockPosition === "top" @@ -885,7 +871,7 @@ Loader { anchors.left: dockPosition === "left" anchors.right: dockPosition === "right" - // Offset past bar when at same edge (skip bar offset if dock is exclusive - exclusion zones stack) + // Static margins — no animation, window stays put margins.top: dockPosition === "top" ? (barAtSameEdge && !exclusive ? barHeight + (Settings.data.bar.floating ? Settings.data.bar.marginVertical : 0) + floatingMargin : floatingMargin) : 0 margins.bottom: dockPosition === "bottom" ? (barAtSameEdge && !exclusive ? barHeight + (Settings.data.bar.floating ? Settings.data.bar.marginVertical : 0) + floatingMargin : floatingMargin) : 0 margins.left: dockPosition === "left" ? (barAtSameEdge && !exclusive ? barHeight + (Settings.data.bar.floating ? Settings.data.bar.marginHorizontal : 0) + floatingMargin : floatingMargin) : 0 @@ -908,9 +894,13 @@ Loader { readonly property int extraLeft: (!isVertical && !exclusive && barOnLeft) ? barHeight : 0 readonly property int extraRight: (!isVertical && !exclusive && barOnRight) ? barHeight : 0 + // Expose content size for window sizing (before slide padding) + readonly property int contentWidth: dockContent.dockContainer.width + extraLeft + extraRight + 2 + readonly property int contentHeight: dockContent.dockContainer.height + extraTop + extraBottom + 2 + // Add +2 buffer for fractional scaling issues - width: dockContent.dockContainer.width + extraLeft + extraRight + 2 - height: dockContent.dockContainer.height + extraTop + extraBottom + 2 + width: contentWidth + height: contentHeight anchors.horizontalCenter: isVertical ? undefined : parent.horizontalCenter anchors.verticalCenter: isVertical ? parent.verticalCenter : undefined @@ -920,27 +910,15 @@ Loader { anchors.left: dockPosition === "left" ? parent.left : undefined anchors.right: dockPosition === "right" ? parent.right : undefined + // Slide content inside the fixed window + transform: Translate { + x: dockWindow.slideX + y: dockWindow.slideY + } + // Enable layer caching to reduce GPU usage from continuous animations layer.enabled: true - opacity: hidden ? 0 : 1 - scale: hidden ? 0.85 : 1 - - Behavior on opacity { - NumberAnimation { - duration: hidden ? hideAnimationDuration : showAnimationDuration - easing.type: Easing.InOutQuad - } - } - - Behavior on scale { - NumberAnimation { - duration: hidden ? hideAnimationDuration : showAnimationDuration - easing.type: hidden ? Easing.InQuad : Easing.OutBack - easing.overshoot: hidden ? 0 : 1.05 - } - } - DockContent { id: dockContent anchors.fill: parent diff --git a/Modules/Panels/Dock/StaticDockPanel.qml b/Modules/Panels/Dock/StaticDockPanel.qml index ea08ac1be..7e221d2e3 100644 --- a/Modules/Panels/Dock/StaticDockPanel.qml +++ b/Modules/Panels/Dock/StaticDockPanel.qml @@ -70,9 +70,9 @@ SmartPanel { } onOpened: { - if (!panelHovered && !menuHovered) { - hoverCloseTimer.restart(); - } + // Don't auto-start close timer here — the peek zone's onExited in Dock.qml + // starts hideTimer which checks panel.isDockHovered, giving the user time + // to move into the panel without it closing prematurely. } panelAnchorTop: dockPosition === "top" diff --git a/Modules/Panels/Launcher/LauncherOverlayWindow.qml b/Modules/Panels/Launcher/LauncherOverlayWindow.qml index 243d5d4ee..3b1b2c80c 100644 --- a/Modules/Panels/Launcher/LauncherOverlayWindow.qml +++ b/Modules/Panels/Launcher/LauncherOverlayWindow.qml @@ -255,7 +255,7 @@ Variants { ShapePath { strokeWidth: -1 - fillColor: Qt.alpha(Color.mSurfaceVariant, Color.panelBackgroundOpacity) + fillColor: Qt.alpha(Color.mSurface, Color.panelBackgroundOpacity) // Offset by radius to account for Shape's extended bounds startX: panelShape.radius + panelShape.radius * panelShape.tlMultX diff --git a/Services/Media/AudioService.qml b/Services/Media/AudioService.qml index d28437dee..3426dd3c0 100644 --- a/Services/Media/AudioService.qml +++ b/Services/Media/AudioService.qml @@ -318,20 +318,20 @@ Singleton { function setAppStreamVolume(appKey: string, volume: real): void { if (!appKey) - return; + return; var o = appVolumeOverrides; if (!o[appKey]) - o[appKey] = {}; + o[appKey] = {}; o[appKey].volume = volume; appVolumeOverrides = o; } function setAppStreamMuted(appKey: string, muted: bool): void { if (!appKey) - return; + return; var o = appVolumeOverrides; if (!o[appKey]) - o[appKey] = {}; + o[appKey] = {}; o[appKey].muted = muted; appVolumeOverrides = o; } @@ -343,18 +343,18 @@ Singleton { function _applyAppOverrides(): void { var streams = root.appStreams; if (!streams) - return; + return; var currentIds = {}; _isApplyingAppOverride = true; for (var i = 0; i < streams.length; i++) { var s = streams[i]; if (!s) - continue; + continue; currentIds[s.id] = true; var key = getAppKey(s); var ov = key ? appVolumeOverrides[key] : null; if (!ov || !s.audio) - continue; + continue; if (ov.volume !== undefined && Math.abs(s.audio.volume - ov.volume) > root.epsilon) { s.audio.volume = ov.volume; }