Skip to content

Commit bc80b3c

Browse files
committed
Accommodate for the target workspace's bounds when layoutFloatingWindow and unhideFromCorner calculates the coordinates for the top-left corner of the window.
Issue: When moving a floating window to another workspace (potentially on a different monitor with different resolution), the window could end up placed almost completely outside the bounds of the target workspace. This happens because the proportional repositioning logic only remaps the window's top-left corner without considering the window's size, so a window near the edge of a larger monitor gets its top-left mapped to the edge of a smaller monitor, and the window extends almost completely off the screen. Root Cause: When layoutFloatingWindow and unhideFromCorner calculates the coordinates for the top-left corner it does not accommodate for the target workspace's bounds. Fix: layoutFloatingWindow: - Replaced two separate AX calls of `getCenter()` and `getAxTopLeftCorner()` with a single call to `getAxRect()` to get both position and size of the window. - After computing the proportional position on the target workspace, restrict the position using `coerceIn` so the window stays within `workspace.workspaceMonitor.visibleRect`, accounting for the window's width and height unhideFromCorner: - After computing the restored proportional position on the workspace, clamp it within `workspaceRect` using `lastFloatingSize` to account for window dimensions. The restrict logic uses `max(rect.minX, rect.maxX - windowWidth)` as the upper bound, which handles the edge case where the window is larger than the target workspace — in that case, the range collapses to `minX...minX` and the window is pinned to the top-left corner of the workspace (which is the most reasonable behavior). Related discussion: #1875 (with my fix the screen no longer disappear, but it gets re positioned in the screen) Possible related issue: #1519
1 parent 18545c2 commit bc80b3c

File tree

2 files changed

+24
-12
lines changed

2 files changed

+24
-12
lines changed

Sources/AppBundle/layout/layoutRecursive.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,23 @@ extension Window {
6969
@MainActor
7070
fileprivate func layoutFloatingWindow(_ context: LayoutContext) async throws {
7171
let workspace = context.workspace
72-
let currentMonitor = try await getCenter()?.monitorApproximation // Probably not idempotent
73-
if let currentMonitor, let windowTopLeftCorner = try await getAxTopLeftCorner(), workspace != currentMonitor.activeWorkspace {
72+
let windowRect = try await getAxRect() // Probably not idempotent
73+
let currentMonitor = windowRect?.center.monitorApproximation
74+
if let currentMonitor, let windowRect, workspace != currentMonitor.activeWorkspace {
75+
let windowTopLeftCorner = windowRect.topLeftCorner
7476
let xProportion = (windowTopLeftCorner.x - currentMonitor.visibleRect.topLeftX) / currentMonitor.visibleRect.width
7577
let yProportion = (windowTopLeftCorner.y - currentMonitor.visibleRect.topLeftY) / currentMonitor.visibleRect.height
7678

77-
let moveTo = workspace.workspaceMonitor
78-
setAxFrame(CGPoint(
79-
x: moveTo.visibleRect.topLeftX + xProportion * moveTo.visibleRect.width,
80-
y: moveTo.visibleRect.topLeftY + yProportion * moveTo.visibleRect.height,
81-
), nil)
79+
let workspaceRect = workspace.workspaceMonitor.visibleRect
80+
var newX = workspaceRect.topLeftX + xProportion * workspaceRect.width
81+
var newY = workspaceRect.topLeftY + yProportion * workspaceRect.height
82+
83+
let windowWidth = windowRect.width
84+
let windowHeight = windowRect.height
85+
newX = newX.coerceIn(workspaceRect.minX ... max(workspaceRect.minX, workspaceRect.maxX - windowWidth))
86+
newY = newY.coerceIn(workspaceRect.minY ... max(workspaceRect.minY, workspaceRect.maxY - windowHeight))
87+
88+
setAxFrame(CGPoint(x: newX, y: newY), nil)
8289
}
8390
if isFullscreen {
8491
layoutFullscreen(context)

Sources/AppBundle/tree/MacWindow.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,16 @@ final class MacWindow: Window {
163163
// Tiling windows should be unhidden with layoutRecursive anyway
164164
case .floatingWindow:
165165
let workspaceRect = nodeWorkspace.workspaceMonitor.rect
166-
let pointInsideWorkspace = CGPoint(
167-
x: workspaceRect.width * prevUnhiddenProportionalPositionInsideWorkspaceRect.x,
168-
y: workspaceRect.height * prevUnhiddenProportionalPositionInsideWorkspaceRect.y,
169-
)
170-
setAxFrame(workspaceRect.topLeftCorner + pointInsideWorkspace, nil)
166+
var newX = workspaceRect.topLeftX + workspaceRect.width * prevUnhiddenProportionalPositionInsideWorkspaceRect.x
167+
var newY = workspaceRect.topLeftY + workspaceRect.height * prevUnhiddenProportionalPositionInsideWorkspaceRect.y
168+
// todo we probably should replace lastFloatingSize with proper floating window sizing
169+
// https://github.com/nikitabobko/AeroSpace/issues/1519
170+
let windowWidth = lastFloatingSize?.width ?? 0
171+
let windowHeight = lastFloatingSize?.height ?? 0
172+
newX = newX.coerceIn(workspaceRect.minX ... max(workspaceRect.minX, workspaceRect.maxX - windowWidth))
173+
newY = newY.coerceIn(workspaceRect.minY ... max(workspaceRect.minY, workspaceRect.maxY - windowHeight))
174+
175+
setAxFrame(CGPoint(x: newX, y: newY), nil)
171176
case .macosNativeFullscreenWindow, .macosNativeHiddenAppWindow, .macosNativeMinimizedWindow,
172177
.macosPopupWindow, .tiling, .rootTilingContainer, .shimContainerRelation: break
173178
}

0 commit comments

Comments
 (0)