diff --git a/modules/services/AxctlService.qml b/modules/services/AxctlService.qml index bade7b38..79683d67 100644 --- a/modules/services/AxctlService.qml +++ b/modules/services/AxctlService.qml @@ -141,6 +141,9 @@ Singleton { height: mon.height, refreshRate: mon.refresh_rate, scale: mon.scale, + x: parseInt(mon.metadata ? mon.metadata.x : 0) || 0, + y: parseInt(mon.metadata ? mon.metadata.y : 0) || 0, + transform: parseInt(mon.metadata ? mon.metadata.transform : 0) || 0, activeWorkspace: { id: parseInt(mon.metadata ? mon.metadata.active_workspace : 0) || 0, name: mon.metadata ? mon.metadata.active_workspace : "" } })); root.monitors.values = mappedMonitors; diff --git a/modules/widgets/overview/Overview.qml b/modules/widgets/overview/Overview.qml index 6dad0e66..ded832b4 100644 --- a/modules/widgets/overview/Overview.qml +++ b/modules/widgets/overview/Overview.qml @@ -303,16 +303,19 @@ Item { implicitWidth: workspaceColumnLayout.implicitWidth implicitHeight: workspaceColumnLayout.implicitHeight - // Pre-filter windows for this monitor and workspace group + // Pre-filter windows for the visible workspace group. + // Show windows from ALL monitors in every overview instance — the user can + // then drag windows from any monitor's overview to any workspace cell. + // Each window uses its OWN monitor's metadata for position math (looked up + // per-delegate below) so cross-monitor windows render at correct local coords. readonly property var filteredWindowData: { const minWs = overviewRoot.workspaceGroup * overviewRoot.workspacesShown; const maxWs = (overviewRoot.workspaceGroup + 1) * overviewRoot.workspacesShown; - const monId = overviewRoot.monitorId; const toplevels = ToplevelManager.toplevels.values; return overviewRoot.windowList.filter(win => { const wsId = win?.workspace?.id; - return wsId > minWs && wsId <= maxWs && win.monitor === monId; + return wsId > minWs && wsId <= maxWs; }).map(win => ({ windowData: win, toplevel: (() => { @@ -331,12 +334,19 @@ Item { delegate: OverviewWindow { id: window required property var modelData + // Resolve window's own monitor (may differ from overview's monitor when + // showing cross-monitor windows). Used both for position offset AND scale + // so windows always fit the cell regardless of source monitor size. + readonly property var winMonitor: overviewRoot.monitors.find(m => m.id === modelData.windowData.monitor) ?? overviewRoot.monitorData windowData: modelData.windowData toplevel: modelData.toplevel - scale: overviewRoot.scale + // Per-window scale = cell_width / window_monitor_width — keeps each window + // sized as a thumbnail of its OWN monitor, so a DP-2 window in a DP-1 overview + // fills the cell properly instead of rendering at "0.15 * 1920 in a 2560-sized cell". + scale: (winMonitor && winMonitor.width > 0) ? (overviewRoot.workspaceImplicitWidth / winMonitor.width) : overviewRoot.scale availableWorkspaceWidth: overviewRoot.workspaceImplicitWidth availableWorkspaceHeight: overviewRoot.workspaceImplicitHeight - monitorData: overviewRoot.monitorData + monitorData: winMonitor barPosition: overviewRoot.barPosition barReserved: overviewRoot.barReserved diff --git a/modules/widgets/overview/OverviewWindow.qml b/modules/widgets/overview/OverviewWindow.qml index 8943a6ee..4fb266d3 100644 --- a/modules/widgets/overview/OverviewWindow.qml +++ b/modules/widgets/overview/OverviewWindow.qml @@ -6,6 +6,7 @@ import Quickshell.Wayland import qs.modules.globals import qs.modules.theme import qs.modules.services +import qs.modules.bar.workspaces // For CompositorData import qs.modules.components import qs.config @@ -299,6 +300,11 @@ Item { // Check if moving to different workspace if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { + // The monitor whose overview received the drop — pin the target workspace there + // so windows land on the monitor the user dragged within, not on whatever + // monitor the workspace happened to be bound to previously. + const dropMonitorName = overviewRoot.monitor ? overviewRoot.monitor.name : ""; + // Moving to different workspace if (windowData?.floating && (root.x !== root.initX || root.y !== root.initY)) { // Calculate position in the target workspace @@ -307,29 +313,35 @@ Item { const targetRowIndex = Math.floor((targetWorkspace - 1) % overviewRoot.workspacesShown / overviewRoot.columns); const targetXOffset = Math.round((overviewRoot.workspaceImplicitWidth + overviewRoot.workspacePadding + overviewRoot.workspaceSpacing) * targetColIndex + overviewRoot.workspacePadding / 2); const targetYOffset = Math.round((overviewRoot.workspaceImplicitHeight + overviewRoot.workspacePadding + overviewRoot.workspaceSpacing) * targetRowIndex + overviewRoot.workspacePadding / 2); - + // Calculate relative position in target workspace const relativeX = root.x - targetXOffset; const relativeY = root.y - targetYOffset; - + // Convert to percentage const percentageX = Math.round((relativeX / root.availableWorkspaceWidth) * 100); const percentageY = Math.round((relativeY / root.availableWorkspaceHeight) * 100); - + // Move to workspace and set position AxctlService.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${windowData?.address}`); + if (dropMonitorName) { + AxctlService.dispatch(`moveworkspacetomonitor ${targetWorkspace} ${dropMonitorName}`); + } AxctlService.dispatch(`movewindowpixel exact ${percentageX}% ${percentageY}%, address:${windowData?.address}`); - + // Force immediate window data update CompositorData.updateWindowList(); } else { // Just move workspace without repositioning AxctlService.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${windowData?.address}`); - + if (dropMonitorName) { + AxctlService.dispatch(`moveworkspacetomonitor ${targetWorkspace} ${dropMonitorName}`); + } + // Force immediate window data update CompositorData.updateWindowList(); } - + // Reset position in overview root.x = root.initX; root.y = root.initY;