Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions modules/services/AxctlService.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 15 additions & 5 deletions modules/widgets/overview/Overview.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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: (() => {
Expand All @@ -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

Expand Down
24 changes: 18 additions & 6 deletions modules/widgets/overview/OverviewWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down