diff --git a/Loop/Core/WindowDragManager.swift b/Loop/Core/WindowDragManager.swift index 55914315..37b7e676 100644 --- a/Loop/Core/WindowDragManager.swift +++ b/Loop/Core/WindowDragManager.swift @@ -9,6 +9,7 @@ import Defaults import Scribe import SwiftUI +@MainActor final class WindowDragManager { static let shared = WindowDragManager() private init() {} @@ -42,7 +43,6 @@ final class WindowDragManager { !Defaults[.stashManagerStashedWindows].isEmpty } - @MainActor func addObservers() { accessibilityCheckerTask = Task(priority: .background) { [weak self] in for await status in AccessibilityManager.shared.stream(initial: true) { @@ -90,7 +90,7 @@ final class WindowDragManager { return } - Task { @MainActor in + Task { guard let initialMousePosition else { initialMousePosition = currentMousePosition return @@ -110,9 +110,6 @@ final class WindowDragManager { } if let window = draggingWindow, let initialFrame = initialWindowFrame, hasWindowResized(window.frame, initialFrame) { - StashManager.shared.onWindowDragged(window.cgWindowID) - WindowRecords.eraseRecords(for: window) - if hasWindowMoved(window.frame, initialFrame) { if Defaults[.restoreWindowFrameOnDrag] { restoreInitialWindowSize(window) @@ -131,28 +128,33 @@ final class WindowDragManager { processSnapAction() } } + + StashManager.shared.onWindowDragged(window.cgWindowID) + WindowRecords.eraseRecords(for: window) } } } private func leftMouseUp(_: CGEvent) { - Task { @MainActor in + guard Defaults[.windowSnapping] else { + return + } + + Task { + previewController.close() + if let window = draggingWindow, + let screen = NSScreen.screenWithMouse, let initialFrame = initialWindowFrame, hasWindowMoved(window.frame, initialFrame) { - if Defaults[.windowSnapping] { - attemptWindowSnap(window) - } + WindowEngine.resize(window, to: .init(direction), on: screen) } - previewController.close() draggingWindow = nil - resetDragState() } } - @MainActor private func setCurrentDraggingWindow() { guard determineDraggedWindowTask == nil else { return @@ -163,9 +165,8 @@ final class WindowDragManager { determineDraggedWindowTask = nil } - guard - let draggingWindow = try? WindowUtility.windowAtPosition(currentMousePosition), - !draggingWindow.isAppExcluded + guard let draggingWindow = try? WindowUtility.windowAtPosition(currentMousePosition), + !draggingWindow.isAppExcluded else { didFailToResolveDraggedWindow = true return @@ -263,41 +264,29 @@ final class WindowDragManager { await AccentColorController.shared.refresh() } - direction = WindowDirection.getSnapDirection( + let newDirection = WindowDirection.getSnapDirection( mouseLocation: currentMousePosition, - currentDirection: direction, + currentDirection: oldDirection, screenFrame: screenFrame, ignoredFrame: ignoredFrame ) - Log.info("Window snapping direction changed: \(direction.debugDescription)", category: .windowDragManager) + // Only update if direction actually changed + if newDirection != oldDirection { + direction = newDirection - previewController.open(screen: screen, window: draggingWindow, startingAction: nil) - previewController.setAction(to: WindowAction(direction)) - } else { - direction = .noAction - previewController.close() - } - - if direction != oldDirection { - if Defaults[.hapticFeedback] { - NSHapticFeedbackManager.defaultPerformer.perform( - NSHapticFeedbackManager.FeedbackPattern.alignment, - performanceTime: NSHapticFeedbackManager.PerformanceTime.now - ) - } - } - } + Log.info("Window snapping direction changed: \(newDirection.debugDescription)", category: .windowDragManager) - private func attemptWindowSnap(_ window: Window) { - guard let screen = NSScreen.screenWithMouse else { - return - } + previewController.open(screen: screen, window: draggingWindow, startingAction: nil) + previewController.setAction(to: WindowAction(newDirection)) - let snapDirection = direction - DispatchQueue.main.async { - WindowEngine.resize(window, to: .init(snapDirection), on: screen) - self.direction = .noAction + if newDirection != .noAction, Defaults[.hapticFeedback] { + NSHapticFeedbackManager.defaultPerformer.perform(.alignment, performanceTime: .now) + } + } + } else if !(oldDirection == .noAction || oldDirection == .noSelection) { + direction = .noAction + previewController.close() } } } diff --git a/Loop/Window Management/Window/WindowUtility.swift b/Loop/Window Management/Window/WindowUtility.swift index 6e9ea0e1..623d37d3 100644 --- a/Loop/Window Management/Window/WindowUtility.swift +++ b/Loop/Window Management/Window/WindowUtility.swift @@ -10,31 +10,28 @@ import Defaults import Scribe /// This enum is in charge of fetching windows in the user's workspace, which will be used by Loop. +@Loggable(style: .static) enum WindowUtility { /// Get the target window, depending on the user's preferences. This could be the frontmost window, or the window under the cursor. /// - Returns: The target window static func userDefinedTargetWindow() -> Window? { var result: Window? - do { - Log.info("Getting window at cursor...", category: .windowUtility) + log.info("Getting window at cursor...") - if Defaults[.resizeWindowUnderCursor], - let mouseLocation = CGEvent.mouseLocation, - let window = try windowAtPosition(mouseLocation) { - result = window - } - } catch { - Log.warn("Failed to get window at cursor: \(error.localizedDescription)", category: .windowUtility) + if Defaults[.resizeWindowUnderCursor], + let mouseLocation = CGEvent.mouseLocation, + let window = windowAtPosition(mouseLocation) { + result = window } if result == nil { do { - Log.info("Getting frontmost window...", category: .windowUtility) + log.info("Getting frontmost window...") result = try frontmostWindow() } catch { - Log.warn("Failed to get frontmost window: \(error.localizedDescription)", category: .windowUtility) + log.warn("Failed to get frontmost window: \(error.localizedDescription)") } } @@ -53,11 +50,15 @@ enum WindowUtility { /// Get the Window at a given position. /// - Parameter position: The position to check for /// - Returns: The window at the given position, if any - static func windowAtPosition(_ position: CGPoint) throws -> Window? { - // If we can find the window at a point using the Accessibility API, return it - if let element = try AXUIElement.systemWide.getElementAtPosition(position), - let windowElement: AXUIElement = try element.getValue(.window) { - return try Window(element: windowElement) + static func windowAtPosition(_ position: CGPoint) -> Window? { + do { + // If we can find the window at a point using the Accessibility API, return it + if let element = try AXUIElement.systemWide.getElementAtPosition(position), + let windowElement: AXUIElement = try element.getValue(.window) { + return try Window(element: windowElement) + } + } catch { + log.warn("Failed to determine element at position: \(error.localizedDescription)") } // If the previous method didn't work, loop through all windows on-screen and return the first one that contains the desired point