From 5e558660c3c05c895630cfac1ff63af6eb2cbb59 Mon Sep 17 00:00:00 2001 From: sima Date: Mon, 18 May 2026 20:21:39 +0800 Subject: [PATCH] Fix Hyprland scrolling overview geometry (#2442) --- .../WorkspaceOverlays/OverviewWidget.qml | 24 ++++++++++++++++- .../WorkspaceOverlays/OverviewWindow.qml | 27 ++++++++++++++----- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml b/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml index ae9c969a..b8f242a4 100644 --- a/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml +++ b/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml @@ -107,6 +107,24 @@ Item { } } + function getWorkspaceViewportBounds(workspaceId) { + const workspace = allWorkspaces?.find(ws => ws?.id === workspaceId); + const mon = workspace?.monitor?.lastIpcObject || monitor?.lastIpcObject || {}; + const reserved = mon.reserved || [0, 0, 0, 0]; + + const x = (mon.x ?? 0) + (reserved[0] ?? 0); + const y = (mon.y ?? 0) + (reserved[1] ?? 0); + const width = Math.max((mon.width ?? monitorPhysicalWidth) - (reserved[0] ?? 0) - (reserved[2] ?? 0), 1); + const height = Math.max((mon.height ?? monitorPhysicalHeight) - (reserved[1] ?? 0) - (reserved[3] ?? 0), 1); + const scale = Math.min(root.workspaceImplicitWidth / width, root.workspaceImplicitHeight / height); + + return { + "x": x, + "y": y, + "scale": scale + }; + } + property bool monitorIsFocused: monitor?.focused ?? false property real scale: SettingsData.overviewScale property color activeBorderColor: Theme.primary @@ -223,7 +241,7 @@ Item { onClicked: { if (root.draggingTargetWorkspace === -1) { root.overviewOpen = false; - HyplandService.focusWorkspace(workspaceValue); + HyprlandService.focusWorkspace(workspaceValue); } } } @@ -309,12 +327,16 @@ Item { readonly property int workspaceIndex: getWorkspaceIndex() readonly property int workspaceColIndex: workspaceIndex % root.effectiveColumns readonly property int workspaceRowIndex: Math.floor(workspaceIndex / root.effectiveColumns) + readonly property var workspaceBounds: root.getWorkspaceViewportBounds(windowWorkspaceId) toplevel: modelData scale: root.scale monitorDpr: root.dpr availableWorkspaceWidth: root.workspaceImplicitWidth availableWorkspaceHeight: root.workspaceImplicitHeight + contentOriginX: workspaceBounds.x + contentOriginY: workspaceBounds.y + contentScale: workspaceBounds.scale widgetMonitorId: root.monitor.id xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex diff --git a/quickshell/Modules/WorkspaceOverlays/OverviewWindow.qml b/quickshell/Modules/WorkspaceOverlays/OverviewWindow.qml index 2a4d7f52..aea653d5 100644 --- a/quickshell/Modules/WorkspaceOverlays/OverviewWindow.qml +++ b/quickshell/Modules/WorkspaceOverlays/OverviewWindow.qml @@ -14,20 +14,34 @@ Item { property var availableWorkspaceHeight property bool restrictToWorkspace: true property real monitorDpr: 1 + property real contentOriginX: 0 + property real contentOriginY: 0 + property real contentScale: 0 readonly property var windowData: toplevel?.lastIpcObject || null readonly property var monitorObj: toplevel?.monitor readonly property var monitorData: monitorObj?.lastIpcObject || null readonly property real effectiveScale: root.scale / root.monitorDpr + readonly property real overviewScale: root.contentScale > 0 ? root.contentScale : root.effectiveScale - property real initX: Math.max(((windowData?.at?.[0] ?? 0) - (monitorData?.x ?? 0) - (monitorData?.reserved?.[0] ?? 0)) * effectiveScale, 0) + xOffset - property real initY: Math.max(((windowData?.at?.[1] ?? 0) - (monitorData?.y ?? 0) - (monitorData?.reserved?.[1] ?? 0)) * effectiveScale, 0) + yOffset + readonly property real rawX: ((windowData?.at?.[0] ?? 0) - contentOriginX) * overviewScale + readonly property real rawY: ((windowData?.at?.[1] ?? 0) - contentOriginY) * overviewScale + readonly property real rawWidth: (windowData?.size?.[0] ?? 100) * overviewScale + readonly property real rawHeight: (windowData?.size?.[1] ?? 100) * overviewScale + readonly property real clipLeft: Math.max(0, rawX) + readonly property real clipTop: Math.max(0, rawY) + readonly property real clipRight: Math.min(availableWorkspaceWidth, rawX + rawWidth) + readonly property real clipBottom: Math.min(availableWorkspaceHeight, rawY + rawHeight) + readonly property bool intersectsViewport: clipRight > clipLeft && clipBottom > clipTop + + property real initX: clipLeft + xOffset + property real initY: clipTop + yOffset property real xOffset: 0 property real yOffset: 0 property int widgetMonitorId: 0 - property var targetWindowWidth: (windowData?.size?.[0] ?? 100) * effectiveScale - property var targetWindowHeight: (windowData?.size?.[1] ?? 100) * effectiveScale + property var targetWindowWidth: Math.max(clipRight - clipLeft, 0) + property var targetWindowHeight: Math.max(clipBottom - clipTop, 0) property bool hovered: false property bool pressed: false @@ -39,8 +53,9 @@ Item { x: initX y: initY - width: Math.min((windowData?.size?.[0] ?? 100) * effectiveScale, availableWorkspaceWidth) - height: Math.min((windowData?.size?.[1] ?? 100) * effectiveScale, availableWorkspaceHeight) + width: targetWindowWidth + height: targetWindowHeight + visible: intersectsViewport opacity: (monitorObj?.id ?? -1) == widgetMonitorId ? 1 : 0.4 Rectangle {