Skip to content
Merged
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
28 changes: 25 additions & 3 deletions macos/Sources/Features/Terminal/TerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
// of each other.
private static var lastCascadePoint = NSPoint(x: 0, y: 0)

private static func applyCascade(to window: NSWindow, hasFixedPos: Bool) {
if hasFixedPos { return }

if all.count > 1 {
lastCascadePoint = window.cascadeTopLeft(from: lastCascadePoint)
} else {
lastCascadePoint = window.cascadeTopLeft(from: NSPoint(x: window.frame.minX, y: window.frame.maxY))
}
}

// The preferred parent terminal controller.
static var preferredParent: TerminalController? {
all.first {
Expand Down Expand Up @@ -253,7 +263,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
// Only cascade if we aren't fullscreen.
if let window = c.window {
if !window.styleMask.contains(.fullScreen) {
Self.lastCascadePoint = window.cascadeTopLeft(from: Self.lastCascadePoint)
let hasFixedPos = c.derivedConfig.windowPositionX != nil && c.derivedConfig.windowPositionY != nil
Self.applyCascade(to: window, hasFixedPos: hasFixedPos)
}
}

Expand Down Expand Up @@ -323,7 +334,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
window.setFrameTopLeftPoint(position)
window.constrainToScreen()
} else {
Self.lastCascadePoint = window.cascadeTopLeft(from: Self.lastCascadePoint)
let hasFixedPos = c.derivedConfig.windowPositionX != nil && c.derivedConfig.windowPositionY != nil
Self.applyCascade(to: window, hasFixedPos: hasFixedPos)
}
}
}
Expand Down Expand Up @@ -429,7 +441,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
// Only cascade if we aren't fullscreen and are alone in the tab group.
if !window.styleMask.contains(.fullScreen) &&
window.tabGroup?.windows.count ?? 1 == 1 {
Self.lastCascadePoint = window.cascadeTopLeft(from: Self.lastCascadePoint)
let hasFixedPos = controller.derivedConfig.windowPositionX != nil && controller.derivedConfig.windowPositionY != nil
Self.applyCascade(to: window, hasFixedPos: hasFixedPos)
}

controller.showWindow(self)
Expand Down Expand Up @@ -1165,6 +1178,15 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
}
}

override func windowDidResize(_ notification: Notification) {
super.windowDidResize(notification)

// Whenever we resize save our last position and size for the next start.
if let window {
LastWindowPosition.shared.save(window)
}
}

func windowDidBecomeMain(_ notification: Notification) {
// Whenever we get focused, use that as our last window position for
// restart. This differs from Terminal.app but matches iTerm2 behavior
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ class TerminalWindow: NSWindow {

private func setInitialWindowPosition(x: Int16?, y: Int16?) {
// If we don't have an X/Y then we try to use the previously saved window pos.
guard x != nil, y != nil else {
guard let x = x, let y = y else {
if !LastWindowPosition.shared.restore(self) {
center()
}
Expand All @@ -552,14 +552,19 @@ class TerminalWindow: NSWindow {
return
}

// We have an X/Y, use our controller function to set it up.
guard let terminalController else {
center()
return
}
// Convert top-left coordinates to bottom-left origin using our utility extension
let origin = screen.origin(
fromTopLeftOffsetX: CGFloat(x),
offsetY: CGFloat(y),
windowSize: frame.size)

// Clamp the origin to ensure the window stays fully visible on screen
var safeOrigin = origin
let vf = screen.visibleFrame
safeOrigin.x = min(max(safeOrigin.x, vf.minX), vf.maxX - frame.width)
safeOrigin.y = min(max(safeOrigin.y, vf.minY), vf.maxY - frame.height)

let frame = terminalController.adjustForWindowPosition(frame: frame, on: screen)
setFrameOrigin(frame.origin)
setFrameOrigin(safeOrigin)
}

private func hideWindowButtons() {
Expand Down
18 changes: 12 additions & 6 deletions macos/Sources/Helpers/LastWindowPosition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,28 @@ class LastWindowPosition {
private let positionKey = "NSWindowLastPosition"

func save(_ window: NSWindow) {
let origin = window.frame.origin
let point = [origin.x, origin.y]
UserDefaults.standard.set(point, forKey: positionKey)
let frame = window.frame
let rect = [frame.origin.x, frame.origin.y, frame.size.width, frame.size.height]
UserDefaults.standard.set(rect, forKey: positionKey)
}

func restore(_ window: NSWindow) -> Bool {
guard let points = UserDefaults.standard.array(forKey: positionKey) as? [Double],
points.count == 2 else { return false }
guard let values = UserDefaults.standard.array(forKey: positionKey) as? [Double],
values.count >= 2 else { return false }

let lastPosition = CGPoint(x: points[0], y: points[1])
let lastPosition = CGPoint(x: values[0], y: values[1])

guard let screen = window.screen ?? NSScreen.main else { return false }
let visibleFrame = screen.visibleFrame

var newFrame = window.frame
newFrame.origin = lastPosition

if values.count >= 4 {
newFrame.size.width = min(values[2], visibleFrame.width)
newFrame.size.height = min(values[3], visibleFrame.height)
}

if !visibleFrame.contains(newFrame.origin) {
newFrame.origin.x = max(visibleFrame.minX, min(visibleFrame.maxX - newFrame.width, newFrame.origin.x))
newFrame.origin.y = max(visibleFrame.minY, min(visibleFrame.maxY - newFrame.height, newFrame.origin.y))
Expand Down
Loading