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
50 changes: 36 additions & 14 deletions Loop/Core/LoopManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ final class LoopManager: ObservableObject {
private var screenToResizeOn: NSScreen?
var isShiftKeyPressed: Bool = false

@Published var currentAction: WindowAction = .init(.noAction)
@Published var currentAction: WindowAction = .init(.noSelection)
private var parentCycleAction: WindowAction?
private(set) var initialMousePosition: CGPoint = .zero

Expand Down Expand Up @@ -123,11 +123,11 @@ extension LoopManager {
await AccentColorController.shared.refresh()
}

currentAction = .init(.noAction)
currentAction = .init(.noSelection)
targetWindow = window
parentCycleAction = nil
initialMousePosition = NSEvent.mouseLocation
screenToResizeOn = Defaults[.useScreenWithCursor] ? NSScreen.screenWithMouse : NSScreen.main
screenToResizeOn = nil // Screen to resize on will be determined by the first action.
isShiftKeyPressed = false

if !Defaults[.disableCursorInteraction] {
Expand Down Expand Up @@ -168,7 +168,7 @@ extension LoopManager {
if let targetWindow,
let screenToResizeOn,
forceClose == false,
currentAction.direction != .noAction, !currentAction.direction.willFocusWindow {
!currentAction.direction.willFocusWindow {
if Defaults[.previewVisibility] {
WindowEngine.resize(
targetWindow,
Expand Down Expand Up @@ -231,9 +231,12 @@ extension LoopManager {
canAdvanceCycle: Bool = true
) {
guard
currentAction.id != newAction.id || newAction.shouldImmediatelyExecuteAction,
isLoopActive,
let currentScreen = screenToResizeOn
currentAction.id != newAction.id || newAction.shouldImmediatelyExecuteAction,
let currentScreen = screenToResizeOn ?? resolveAndStoreTargetScreen(
action: newAction,
window: targetWindow
)
else {
return
}
Expand Down Expand Up @@ -308,7 +311,7 @@ extension LoopManager {
newScreen = bottomScreen
}

if currentAction.direction == .noAction {
if currentAction.direction == .noSelection {
if let targetWindow {
let screenSwitchingCustomActionName = "autogenerated_screen_switching_action"

Expand Down Expand Up @@ -360,8 +363,7 @@ extension LoopManager {
currentAction = newAction
changeAction(parentCycleAction, triggeredFromScreenChange: true)
} else {
if let screenToResizeOn,
let window = targetWindow,
if let window = targetWindow,
!Defaults[.previewVisibility] {
if !disableHapticFeedback {
performHapticFeedback()
Expand All @@ -370,7 +372,7 @@ extension LoopManager {
WindowEngine.resize(
window,
to: currentAction,
on: screenToResizeOn,
on: newScreen,
shouldRecord: false
)
}
Expand Down Expand Up @@ -453,14 +455,13 @@ extension LoopManager {
var currentIndex: Int? = nil

if Defaults[.cycleModeRestartEnabled],
currentAction.direction == .noAction ||
!currentCycle.contains(currentAction) {
currentAction.direction == .noSelection || !currentCycle.contains(currentAction) {
return currentCycle[0]
}

// If the current action is noAction, we can preserve the index from the last action.
// If the current action is noSelection, we can preserve the index from the last action.
// This would initially be done by reading the window's records, then would continue by finding the next index from the currentAction.
if currentAction.direction == .noAction,
if currentAction.direction == .noSelection,
!currentCycle.contains(currentAction),
let window = targetWindow,
let latestRecord = WindowRecords.getCurrentAction(for: window) {
Expand Down Expand Up @@ -495,4 +496,25 @@ extension LoopManager {
)
}
}

/// Resolves the target screen for `screenToResizeOn`.
///
/// By default, this uses the user's `useScreenWithCursor` setting.
/// For actions that move windows between screens, the screen containing the window is preferred to ensure deterministic behavior.
/// - Parameters:
/// - action: The window action being performed.
/// - window: The window to be resized, if any.
/// - Returns: The screen the window should be on after the action.
private func resolveAndStoreTargetScreen(action: WindowAction, window: Window?) -> NSScreen? {
var targetScreen = Defaults[.useScreenWithCursor] ? NSScreen.screenWithMouse : NSScreen.main

if action.direction.willChangeScreen,
let window,
let screen = ScreenUtility.screenContaining(window) {
targetScreen = screen
}

screenToResizeOn = targetScreen
return targetScreen
}
}
10 changes: 6 additions & 4 deletions Loop/Stashing/StashManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ final class StashManager {
/// - Returns: `true` if the action is handled by the StashManager and the normal flow should be bypassed; otherwise, `false`.
@discardableResult
func handleIfStashed(_ action: WindowAction, screen: NSScreen) -> Bool {
guard action.direction == .stash else { return false }
guard let stashedWindow = store.stashedWindow(for: action, on: screen) else { return false }
guard !stashedWindow.window.isWindowHidden, !stashedWindow.window.isApplicationHidden else { return false }
guard stashedWindow.screen.isSameScreen(screen) else { return false }
guard action.direction == .stash,
let stashedWindow = store.stashedWindow(for: action, on: screen),
!stashedWindow.window.isWindowHidden, !stashedWindow.window.isApplicationHidden
else {
return false
}

Log.info("Intercepting window action for stashed window \(stashedWindow.window.description)", category: .stashManager)

Expand Down
7 changes: 1 addition & 6 deletions Loop/Stashing/StashedWindowStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,7 @@ final class StashedWindowsStore {

/// Return the stashed window that match the given `action` and `screen`
func stashedWindow(for action: WindowAction, on screen: NSScreen) -> StashedWindow? {
for stashedWindow in stashed.values {
if stashedWindow.action.id == action.id, stashedWindow.screen.isSameScreen(screen) {
return stashedWindow
}
}
return nil
stashed.values.first { $0.action.id == action.id && $0.screen.isSameScreen(screen) }
}

// MARK: Private methods
Expand Down