From 2af0845c68962ca175d850247025b04e6a93cedc Mon Sep 17 00:00:00 2001 From: Pablo P Varela Date: Thu, 6 Jun 2024 22:39:04 +0200 Subject: [PATCH 1/6] feat: resize quadrants --- Swift Shift.xcodeproj/project.pbxproj | 12 ++ Swift Shift/src/mouse/MouseTracker.swift | 149 ++++++++++++++---- Swift Shift/src/preferences/Preferences.swift | 1 + .../src/preferences/PreferencesView.swift | 6 + Swift Shift/src/utils/DrawCircle.swift | 39 +++++ Swift Shift/src/windows/WindowManager.swift | 142 ++++++++--------- 6 files changed, 243 insertions(+), 106 deletions(-) create mode 100644 Swift Shift/src/utils/DrawCircle.swift diff --git a/Swift Shift.xcodeproj/project.pbxproj b/Swift Shift.xcodeproj/project.pbxproj index 5c65221..37f40d8 100644 --- a/Swift Shift.xcodeproj/project.pbxproj +++ b/Swift Shift.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 172E0BD52B32F36300D5CDA7 /* MouseTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E0BD42B32F36300D5CDA7 /* MouseTracker.swift */; }; + 173C0E832C123CF700654527 /* DrawCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173C0E822C123CF700654527 /* DrawCircle.swift */; }; 174652CE2B33345200241CE6 /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174652CD2B33345200241CE6 /* WindowManager.swift */; }; 177FB2412B31F9B900B11BA3 /* PermissionsRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177FB2402B31F9B900B11BA3 /* PermissionsRequestView.swift */; }; 177FB2482B31FE7F00B11BA3 /* ShortcutRecorder in Frameworks */ = {isa = PBXBuildFile; productRef = 177FB2472B31FE7F00B11BA3 /* ShortcutRecorder */; }; @@ -33,6 +34,7 @@ /* Begin PBXFileReference section */ 172E0BD42B32F36300D5CDA7 /* MouseTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MouseTracker.swift; sourceTree = ""; }; + 173C0E822C123CF700654527 /* DrawCircle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawCircle.swift; sourceTree = ""; }; 174652CD2B33345200241CE6 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = ""; }; 177FB2402B31F9B900B11BA3 /* PermissionsRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsRequestView.swift; sourceTree = ""; }; 177FB24B2B31FF1E00B11BA3 /* ShortcutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutView.swift; sourceTree = ""; }; @@ -69,6 +71,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 173C0E812C123CE700654527 /* utils */ = { + isa = PBXGroup; + children = ( + 173C0E822C123CF700654527 /* DrawCircle.swift */, + ); + path = utils; + sourceTree = ""; + }; 17550A652B3351000058A94F /* windows */ = { isa = PBXGroup; children = ( @@ -118,6 +128,7 @@ 17550A6A2B3351A80058A94F /* src */ = { isa = PBXGroup; children = ( + 173C0E812C123CE700654527 /* utils */, E2CABBA62B447B0A00D66D43 /* updates */, E236331C2B44668300C0E61F /* preferences */, 17550A692B3351600058A94F /* app */, @@ -271,6 +282,7 @@ 177FB24C2B31FF1E00B11BA3 /* ShortcutView.swift in Sources */, 172E0BD52B32F36300D5CDA7 /* MouseTracker.swift in Sources */, 1781A5E42B31EE4A00F27910 /* AppView.swift in Sources */, + 173C0E832C123CF700654527 /* DrawCircle.swift in Sources */, E2CABBA52B447AEA00D66D43 /* UpdatesView.swift in Sources */, 177FB2532B321D0500B11BA3 /* ShortcutsManager.swift in Sources */, E2E6F5EA2B3EF03C005B0D96 /* Constants.swift in Sources */, diff --git a/Swift Shift/src/mouse/MouseTracker.swift b/Swift Shift/src/mouse/MouseTracker.swift index 2d839ba..96c2644 100644 --- a/Swift Shift/src/mouse/MouseTracker.swift +++ b/Swift Shift/src/mouse/MouseTracker.swift @@ -7,6 +7,10 @@ enum MouseAction { case none } +enum Quadrant { + case topLeft, topRight, bottomLeft, bottomRight +} + class MouseTracker { static let shared = MouseTracker() private var mouseEventMonitor: Any? @@ -18,37 +22,48 @@ class MouseTracker { private var currentAction: MouseAction = .none private var trackingTimer: Timer? private let trackingTimeout: TimeInterval = 4 // in seconds - + private var shouldUseQuadrants: Bool = false + private var quadrant: Quadrant? + private var windowSize: CGSize? + private init() {} - + func startTracking(for action: MouseAction) { prepareTracking(for: action) registerMouseEventMonitor() startTrackingTimer() } - + func stopTracking(for action: MouseAction) { guard currentAction == action else { return } invalidateTrackingTimer() removeMouseEventMonitor() resetTrackingVariables() } - + + private func prepareTracking(for action: MouseAction) { guard let currentWindow = WindowManager.getCurrentWindow(), - !shouldIgnore(window: currentWindow) else { + !shouldIgnore(window: currentWindow) else { trackedWindow = nil return } - + shouldFocusWindow = PreferencesManager.loadBool(for: .focusOnApp) + shouldUseQuadrants = PreferencesManager.loadBool(for: .useQuadrants) trackedWindowIsFocused = false currentAction = action initialMouseLocation = NSEvent.mouseLocation trackedWindow = currentWindow initialWindowLocation = WindowManager.getPosition(window: currentWindow) + windowSize = WindowManager.getSize(window: currentWindow) + + if action == .resize && shouldUseQuadrants, let initialMouseLocation = initialMouseLocation, let initialWindowLocation = initialWindowLocation, let windowSize = windowSize { + quadrant = determineQuadrant(mouseLocation: initialMouseLocation, windowSize: windowSize, windowLocation: initialWindowLocation) + print(quadrant!) + } } - + private func shouldIgnore(window: AXUIElement) -> Bool { guard let app = WindowManager.getNSApplication(from: window), let bundleIdentifier = app.bundleIdentifier, @@ -58,32 +73,53 @@ class MouseTracker { print("Ignoring", bundleIdentifier) return true } - + + private func determineQuadrant(mouseLocation: NSPoint, windowSize: CGSize, windowLocation: NSPoint) -> Quadrant { + let bounds = WindowManager.getWindowBounds(windowLocation: windowLocation, windowSize: windowSize) + + let midX = (bounds.topLeft.x + bounds.topRight.x) / 2 + let midY = (bounds.topLeft.y + bounds.bottomLeft.y) / 2 + + let isLeft = mouseLocation.x < midX + let isTop = mouseLocation.y > midY + + switch (isLeft, isTop) { + case (true, true): + return .topLeft + case (false, true): + return .topRight + case (true, false): + return .bottomLeft + case (false, false): + return .bottomRight + } + } + private func registerMouseEventMonitor() { mouseEventMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved]) { [weak self] event in self?.handleMouseMoved(event) } } - + private func startTrackingTimer() { trackingTimer?.invalidate() trackingTimer = Timer.scheduledTimer(withTimeInterval: trackingTimeout, repeats: false) { [weak self] _ in self?.stopTracking(for: self!.currentAction) } } - + private func handleMouseMoved(_ event: NSEvent) { guard let _ = initialMouseLocation, let _ = initialWindowLocation, let _ = trackedWindow else { return } - + if shouldFocusWindow && !trackedWindowIsFocused { WindowManager.focus(window: trackedWindow!) trackedWindowIsFocused = true } - + switch currentAction { case .move: moveWindowBasedOnMouseLocation(event) @@ -93,41 +129,98 @@ class MouseTracker { break } } - + private func moveWindowBasedOnMouseLocation(_ event: NSEvent) { let currentMouseLocation = NSEvent.mouseLocation let deltaX = currentMouseLocation.x - initialMouseLocation!.x let deltaY = currentMouseLocation.y - initialMouseLocation!.y let newOrigin = NSPoint(x: initialWindowLocation!.x + deltaX, y: initialWindowLocation!.y - deltaY) - + WindowManager.move(window: trackedWindow!, to: newOrigin) } - + + private func convertToWindowCoordinates(_ mouseLocation: NSPoint, windowOrigin: NSPoint) -> NSPoint { + return NSPoint(x: mouseLocation.x - windowOrigin.x, y: mouseLocation.y - windowOrigin.y) + } + private func resizeWindowBasedOnMouseLocation(_ event: NSEvent) { - let currentMouseLocation = NSEvent.mouseLocation - - // Calculate the change in mouse location since tracking started - let deltaX = currentMouseLocation.x - initialMouseLocation!.x - let deltaY = currentMouseLocation.y - initialMouseLocation!.y - - WindowManager.resize(window: trackedWindow!, deltaX: deltaX, deltaY: deltaY) - - // Update initial mouse location for the next event - initialMouseLocation = currentMouseLocation + if (shouldUseQuadrants) { + guard let quadrant = quadrant, + let windowSize = windowSize, + let initialMouseLocation = initialMouseLocation, + let initialWindowLocation = initialWindowLocation else { + return + } + + let currentMouseLocation = NSEvent.mouseLocation + let windowRelativeCurrentMouseLocation = convertToWindowCoordinates(currentMouseLocation, windowOrigin: initialWindowLocation) + + let deltaX = windowRelativeCurrentMouseLocation.x - (initialMouseLocation.x - initialWindowLocation.x) + let deltaY = windowRelativeCurrentMouseLocation.y - (initialMouseLocation.y - initialWindowLocation.y) + + var newWidth = windowSize.width + var newHeight = windowSize.height + var newOrigin = initialWindowLocation + + switch quadrant { + case .topLeft: + newWidth -= deltaX + newHeight += deltaY + newOrigin.x += deltaX + newOrigin.y -= deltaY + case .topRight: + newWidth += deltaX + newHeight += deltaY + newOrigin.y -= deltaY + case .bottomLeft: + newWidth -= deltaX + newHeight -= deltaY + newOrigin.x += deltaX + case .bottomRight: + newWidth += deltaX + newHeight -= deltaY + } + + // Ensure the new width and height are not negative + newWidth = max(newWidth, 1) + newHeight = max(newHeight, 1) + + let newSize = CGSize(width: newWidth, height: newHeight) + WindowManager.resize(window: trackedWindow!, to: newSize, from: newOrigin) + } else { + guard let windowSize = windowSize, + let initialMouseLocation = initialMouseLocation, + let initialWindowLocation = initialWindowLocation else { + return + } + + let currentMouseLocation = NSEvent.mouseLocation + + // Calculate the change in mouse location since tracking started + let deltaX = currentMouseLocation.x - initialMouseLocation.x + let deltaY = currentMouseLocation.y - initialMouseLocation.y + + let newWidth = windowSize.width + deltaX + let newHeight = windowSize.height - deltaY + let newSize = CGSize(width: newWidth, height: newHeight) + let newOrigin = initialWindowLocation + + WindowManager.resize(window: trackedWindow!, to: newSize, from: newOrigin) + } } - + private func invalidateTrackingTimer() { trackingTimer?.invalidate() trackingTimer = nil } - + private func removeMouseEventMonitor() { if let monitor = mouseEventMonitor { NSEvent.removeMonitor(monitor) mouseEventMonitor = nil } } - + private func resetTrackingVariables() { trackedWindow = nil initialMouseLocation = nil diff --git a/Swift Shift/src/preferences/Preferences.swift b/Swift Shift/src/preferences/Preferences.swift index d546f5e..7697724 100644 --- a/Swift Shift/src/preferences/Preferences.swift +++ b/Swift Shift/src/preferences/Preferences.swift @@ -3,6 +3,7 @@ import Foundation enum PreferenceKey: String { case focusOnApp = "focusOnApp" case showMenuBarIcon = "showMenuBarIcon" + case useQuadrants = "useQuadrants" } class PreferencesManager { diff --git a/Swift Shift/src/preferences/PreferencesView.swift b/Swift Shift/src/preferences/PreferencesView.swift index 89c4753..96d6695 100644 --- a/Swift Shift/src/preferences/PreferencesView.swift +++ b/Swift Shift/src/preferences/PreferencesView.swift @@ -4,6 +4,7 @@ import LaunchAtLogin struct PreferencesView: View { @AppStorage(PreferenceKey.showMenuBarIcon.rawValue) var showMenuBarIcon = true @AppStorage(PreferenceKey.focusOnApp.rawValue) var focusOnApp = true + @AppStorage(PreferenceKey.useQuadrants.rawValue) var useQuadrants = false var body: some View { VStack(alignment: .leading) { @@ -19,6 +20,11 @@ struct PreferencesView: View { Text("Focus on window") Text("The window you're interacting with will gain focus") } + + Toggle(isOn: $useQuadrants) { + Text("Use quadrants") + Text("The resize action will happen from the corner that's closer to your mouse") + } } } } diff --git a/Swift Shift/src/utils/DrawCircle.swift b/Swift Shift/src/utils/DrawCircle.swift new file mode 100644 index 0000000..20769d8 --- /dev/null +++ b/Swift Shift/src/utils/DrawCircle.swift @@ -0,0 +1,39 @@ +import Cocoa + +class CircleView: NSView { + var fillColor: NSColor = .green // Default color is green + + init(frame frameRect: NSRect, color: NSColor) { + super.init(frame: frameRect) + self.fillColor = color + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func draw(_ dirtyRect: NSRect) { + super.draw(dirtyRect) + fillColor.setFill() + let circlePath = NSBezierPath(ovalIn: dirtyRect) + circlePath.fill() + } +} + +func drawCircleAt(x: CGFloat, y: CGFloat, diameter: CGFloat, color: NSColor) { + let circleWindow = NSWindow(contentRect: NSRect(x: x, y: y, width: diameter, height: diameter), + styleMask: .borderless, + backing: .buffered, + defer: false) + circleWindow.backgroundColor = .clear + circleWindow.isOpaque = false + circleWindow.hasShadow = false + circleWindow.ignoresMouseEvents = true + circleWindow.level = .floating + + let circleView = CircleView(frame: NSRect(x: 0, y: 0, width: diameter, height: diameter), color: color) + circleWindow.contentView = circleView + + circleWindow.makeKeyAndOrderFront(nil) + circleWindow.orderFrontRegardless() // Makes the window visible at all times +} diff --git a/Swift Shift/src/windows/WindowManager.swift b/Swift Shift/src/windows/WindowManager.swift index 6dbff2c..b38e7d6 100644 --- a/Swift Shift/src/windows/WindowManager.swift +++ b/Swift Shift/src/windows/WindowManager.swift @@ -1,6 +1,13 @@ import Cocoa import Accessibility +struct WindowBounds { + let topLeft: NSPoint + let topRight: NSPoint + let bottomLeft: NSPoint + let bottomRight: NSPoint +} + class WindowManager { // Function to move a specified window to a new location static func move(window: AXUIElement, to point: NSPoint) { @@ -8,40 +15,40 @@ class WindowManager { let pointValue = AXValueCreate(AXValueType.cgPoint, &mutablePoint)! AXUIElementSetAttributeValue(window, kAXPositionAttribute as CFString, pointValue) } - - // Function to resize a specified window to a new size - static func resize(window: AXUIElement, deltaX: CGFloat, deltaY: CGFloat) { - // Get the current window size + + // Function to resize a specified window to a new size from a specific origin + static func resize(window: AXUIElement, to newSize: CGSize, from origin: NSPoint) { + move(window: window, to: origin) + var mutableSize = newSize + let sizeValue = AXValueCreate(AXValueType.cgSize, &mutableSize)! + AXUIElementSetAttributeValue(window, kAXSizeAttribute as CFString, sizeValue) + } + + // Function to get the window size + static func getSize(window: AXUIElement) -> NSSize? { var sizeRef: CFTypeRef? - AXUIElementCopyAttributeValue(window, kAXSizeAttribute as CFString, &sizeRef) - var windowSize: CGSize = .zero - AXValueGetValue(sizeRef as! AXValue, AXValueType.cgSize, &windowSize) - - // Calculate the new size based on mouse movement - // Adjust these calculations if you want different resize behavior - let newWidth = max(windowSize.width + deltaX, 0) // Ensure new width is not negative - let newHeight = max(windowSize.height - deltaY, 0) // Ensure new height is not negative - var newSize = CGSize(width: newWidth, height: newHeight) - - // Create an AXValue representing the new size - if let newSizeValue = AXValueCreate(AXValueType.cgSize, &newSize) { - AXUIElementSetAttributeValue(window , kAXSizeAttribute as CFString, newSizeValue) + let result = AXUIElementCopyAttributeValue(window, kAXSizeAttribute as CFString, &sizeRef) + guard result == .success, let sizeValue = sizeRef else { + return nil } + var windowSize: CGSize = .zero + AXValueGetValue(sizeValue as! AXValue, AXValueType.cgSize, &windowSize) + return NSSize(width: windowSize.width, height: windowSize.height) } - + // Function to get the window under the cursor (even if it's not focused) static func getCurrentWindow() -> AXUIElement? { // Use CGEvent to get the current mouse location guard let event = CGEvent(source: nil) else { return nil } let mouseLocation = event.location - + // Create a system-wide accessibility object let systemWideElement = AXUIElementCreateSystemWide() - + // Perform a hit test to find the element under the mouse var element: AXUIElement? let error = AXUIElementCopyElementAtPosition(systemWideElement, Float(mouseLocation.x), Float(mouseLocation.y), &element) - + if error == .success, let element = element, let window = getWindow(from: element) { var pid: pid_t = 0 AXUIElementGetPid(window, &pid) @@ -50,77 +57,40 @@ class WindowManager { return window } } else { -// switch error { -// case .apiDisabled: -// print("AXError.apiDisabled") -// case .actionUnsupported: -// print("AXError.actionUnsupported") -// case .attributeUnsupported: -// print("AXError.attributeUnsupported") -// case .cannotComplete: -// print("AXError.cannotComplete") -// case .failure: -// print("AXError.failure") -// case .illegalArgument: -// print("AXError.illegalArgument") -// case .invalidUIElement: -// print("AXError.invalidUIElement") -// case .invalidUIElementObserver: -// print("AXError.invalidUIElementObserver") -// case .noValue: -// print("AXError.noValue") -// case .notEnoughPrecision: -// print("AXError.notEnoughPrecision") -// case .notImplemented: -// print("AXError.notImplemented") -// case .notificationAlreadyRegistered: -// print("AXError.notificationAlreadyRegistered") -// case .notificationNotRegistered: -// print("AXError.notificationNotRegistered") -// case .notificationUnsupported: -// print("AXError.notificationUnsupported") -// case .parameterizedAttributeUnsupported: -// print("AXError.parameterizedAttributeUnsupported") -// case .success: -// print("AXError.success") -// @unknown default: -// print("Unknown AXError.") -// } - // Fallback using CGWindowListCopyWindowInfo return getTopWindowAtCursorUsingCGWindowList(mouseLocation: mouseLocation) } - + return nil } - + // Fallback function using CGWindowListCopyWindowInfo private static func getTopWindowAtCursorUsingCGWindowList(mouseLocation: NSPoint) -> AXUIElement? { let windowListInfo = CGWindowListCopyWindowInfo([.excludeDesktopElements, .optionOnScreenOnly], kCGNullWindowID) as NSArray? as? [[String: AnyObject]] - + guard let windowList = windowListInfo else { return nil } - + // Sort the windows based on their layer. Lower layer numbers are closer to the front let sortedWindows = windowList.sorted { ($0[kCGWindowLayer as String] as? Int ?? 0) < ($1[kCGWindowLayer as String] as? Int ?? 0) } - + for entry in sortedWindows { if let boundsDict = entry[kCGWindowBounds as String] as? [String: CGFloat], let windowBounds = CGRect(dictionaryRepresentation: boundsDict as CFDictionary) { - + if windowBounds.contains(mouseLocation), let pid = entry[kCGWindowOwnerPID as String] as? pid_t { let appAXUIElement = AXUIElementCreateApplication(pid) var value: AnyObject? - + if let app = getNSApplication(from: appAXUIElement) { if (IGNORE_APP_BUNDLE_ID.contains(app.bundleIdentifier!)) { print("ignoring", app.bundleIdentifier! as String) continue } } - + if AXUIElementCopyAttributeValue(appAXUIElement, kAXWindowsAttribute as CFString, &value) == .success, let windowList = value as? [AXUIElement], let window = windowList.first { @@ -129,10 +99,10 @@ class WindowManager { } } } - + return nil } - + // Helper function to find the window containing a given accessibility element private static func getWindow(from element: AXUIElement) -> AXUIElement? { var role: AnyObject? @@ -148,24 +118,24 @@ class WindowManager { } return nil } - + static func focus(window: AXUIElement) { // Bring the window to the foreground let result = AXUIElementPerformAction(window, kAXRaiseAction as CFString) if result != .success { print("Error: Unable to focus window") } - + if let app = getNSApplication(from: window) { app.activate() } } - + static func getNSApplication(from element: AXUIElement) -> NSRunningApplication? { // Get the PID of the application that owns the window var pid: pid_t = 0 AXUIElementGetPid(element, &pid) - + // Activate the application with the obtained PID if let app = NSRunningApplication(processIdentifier: pid) { return app @@ -174,18 +144,34 @@ class WindowManager { return nil } } + + static func convertYCoordinateBecauseTheAreTwoFuckingCoordinateSystems(point: NSPoint) -> NSPoint { + return NSPoint(x: point.x, y: CGDisplayBounds(CGMainDisplayID()).height - point.y) + } + static func getPosition(window: AXUIElement) -> NSPoint? { var positionRef: CFTypeRef? - + let result = AXUIElementCopyAttributeValue(window, kAXPositionAttribute as CFString, &positionRef) - guard result == .success else { - return nil - } - + guard result == .success else { return nil } + var windowPosition: CGPoint = .zero AXValueGetValue(positionRef as! AXValue, AXValueType.cgPoint, &windowPosition) - + return NSPoint(x: windowPosition.x, y: windowPosition.y) } + + static func getWindowBounds(windowLocation: NSPoint, windowSize: CGSize) -> WindowBounds { + let windowLocationWithFixedCoordinates = WindowManager.convertYCoordinateBecauseTheAreTwoFuckingCoordinateSystems(point: windowLocation) +// drawCircleAt(x: windowLocationWithFixedCoordinates.x, y: windowLocationWithFixedCoordinates.y, diameter: 10, color: .blue) +// drawCircleAt(x: windowLocationWithFixedCoordinates.x + windowSize.width, y:windowLocationWithFixedCoordinates.y-windowSize.height, diameter: 10, color: .green) + let topLeft = NSPoint(x: windowLocationWithFixedCoordinates.x, y: windowLocationWithFixedCoordinates.y) + let topRight = NSPoint(x: windowLocationWithFixedCoordinates.x + windowSize.width, y: windowLocationWithFixedCoordinates.y) + let bottomLeft = NSPoint(x: windowLocationWithFixedCoordinates.x, y: windowLocationWithFixedCoordinates.y - windowSize.height) + let bottomRight = NSPoint(x: windowLocationWithFixedCoordinates.x + windowSize.width, y: windowLocationWithFixedCoordinates.y - windowSize.height) + + return WindowBounds(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight) + } } + From a64e802a94fd31193cc3fe083730bd1faee42d10 Mon Sep 17 00:00:00 2001 From: Pablo P Varela Date: Thu, 6 Jun 2024 22:55:16 +0200 Subject: [PATCH 2/6] remove duplicated code --- Swift Shift/src/mouse/MouseTracker.swift | 50 ++++++++++-------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/Swift Shift/src/mouse/MouseTracker.swift b/Swift Shift/src/mouse/MouseTracker.swift index 96c2644..013ec23 100644 --- a/Swift Shift/src/mouse/MouseTracker.swift +++ b/Swift Shift/src/mouse/MouseTracker.swift @@ -144,13 +144,17 @@ class MouseTracker { } private func resizeWindowBasedOnMouseLocation(_ event: NSEvent) { + + guard let windowSize = windowSize, + let initialMouseLocation = initialMouseLocation, + let initialWindowLocation = initialWindowLocation else { + return + } + + var newWidth: CGFloat, newHeight: CGFloat, newOrigin: NSPoint + if (shouldUseQuadrants) { - guard let quadrant = quadrant, - let windowSize = windowSize, - let initialMouseLocation = initialMouseLocation, - let initialWindowLocation = initialWindowLocation else { - return - } + guard let quadrant = quadrant else { return } let currentMouseLocation = NSEvent.mouseLocation let windowRelativeCurrentMouseLocation = convertToWindowCoordinates(currentMouseLocation, windowOrigin: initialWindowLocation) @@ -158,9 +162,9 @@ class MouseTracker { let deltaX = windowRelativeCurrentMouseLocation.x - (initialMouseLocation.x - initialWindowLocation.x) let deltaY = windowRelativeCurrentMouseLocation.y - (initialMouseLocation.y - initialWindowLocation.y) - var newWidth = windowSize.width - var newHeight = windowSize.height - var newOrigin = initialWindowLocation + newWidth = windowSize.width + newHeight = windowSize.height + newOrigin = initialWindowLocation switch quadrant { case .topLeft: @@ -180,33 +184,21 @@ class MouseTracker { newWidth += deltaX newHeight -= deltaY } - // Ensure the new width and height are not negative - newWidth = max(newWidth, 1) - newHeight = max(newHeight, 1) - let newSize = CGSize(width: newWidth, height: newHeight) - WindowManager.resize(window: trackedWindow!, to: newSize, from: newOrigin) } else { - guard let windowSize = windowSize, - let initialMouseLocation = initialMouseLocation, - let initialWindowLocation = initialWindowLocation else { - return - } - let currentMouseLocation = NSEvent.mouseLocation - - // Calculate the change in mouse location since tracking started let deltaX = currentMouseLocation.x - initialMouseLocation.x let deltaY = currentMouseLocation.y - initialMouseLocation.y - - let newWidth = windowSize.width + deltaX - let newHeight = windowSize.height - deltaY - let newSize = CGSize(width: newWidth, height: newHeight) - let newOrigin = initialWindowLocation - - WindowManager.resize(window: trackedWindow!, to: newSize, from: newOrigin) + newWidth = windowSize.width + deltaX + newHeight = windowSize.height - deltaY + newOrigin = initialWindowLocation } + + newWidth = max(newWidth, 1) + newHeight = max(newHeight, 1) + let newSize = CGSize(width: newWidth, height: newHeight) + WindowManager.resize(window: trackedWindow!, to: newSize, from: newOrigin) } private func invalidateTrackingTimer() { From 73cd13e0390045526a61a2496d0a48f4898b5588 Mon Sep 17 00:00:00 2001 From: Pablo P Varela Date: Thu, 6 Jun 2024 22:55:33 +0200 Subject: [PATCH 3/6] move comment --- Swift Shift/src/mouse/MouseTracker.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Swift Shift/src/mouse/MouseTracker.swift b/Swift Shift/src/mouse/MouseTracker.swift index 013ec23..271a8cd 100644 --- a/Swift Shift/src/mouse/MouseTracker.swift +++ b/Swift Shift/src/mouse/MouseTracker.swift @@ -184,8 +184,6 @@ class MouseTracker { newWidth += deltaX newHeight -= deltaY } - // Ensure the new width and height are not negative - } else { let currentMouseLocation = NSEvent.mouseLocation let deltaX = currentMouseLocation.x - initialMouseLocation.x @@ -195,6 +193,7 @@ class MouseTracker { newOrigin = initialWindowLocation } + // Ensure the new width and height are not negative newWidth = max(newWidth, 1) newHeight = max(newHeight, 1) let newSize = CGSize(width: newWidth, height: newHeight) From ab3f3e1fa5aedfe94e734dab1354aec681fcc797 Mon Sep 17 00:00:00 2001 From: Pablo P Varela Date: Thu, 6 Jun 2024 22:57:00 +0200 Subject: [PATCH 4/6] remove print --- Swift Shift/src/mouse/MouseTracker.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Swift Shift/src/mouse/MouseTracker.swift b/Swift Shift/src/mouse/MouseTracker.swift index 271a8cd..4ae78c5 100644 --- a/Swift Shift/src/mouse/MouseTracker.swift +++ b/Swift Shift/src/mouse/MouseTracker.swift @@ -60,7 +60,6 @@ class MouseTracker { if action == .resize && shouldUseQuadrants, let initialMouseLocation = initialMouseLocation, let initialWindowLocation = initialWindowLocation, let windowSize = windowSize { quadrant = determineQuadrant(mouseLocation: initialMouseLocation, windowSize: windowSize, windowLocation: initialWindowLocation) - print(quadrant!) } } From 59c89afc78c791d3507bbd1a3f1ab3393ab25423 Mon Sep 17 00:00:00 2001 From: Pablo P Varela Date: Thu, 6 Jun 2024 23:00:44 +0200 Subject: [PATCH 5/6] move comment and add more circles --- Swift Shift/src/windows/WindowManager.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Swift Shift/src/windows/WindowManager.swift b/Swift Shift/src/windows/WindowManager.swift index b38e7d6..0171e08 100644 --- a/Swift Shift/src/windows/WindowManager.swift +++ b/Swift Shift/src/windows/WindowManager.swift @@ -148,7 +148,7 @@ class WindowManager { static func convertYCoordinateBecauseTheAreTwoFuckingCoordinateSystems(point: NSPoint) -> NSPoint { return NSPoint(x: point.x, y: CGDisplayBounds(CGMainDisplayID()).height - point.y) } - + static func getPosition(window: AXUIElement) -> NSPoint? { var positionRef: CFTypeRef? @@ -164,13 +164,16 @@ class WindowManager { static func getWindowBounds(windowLocation: NSPoint, windowSize: CGSize) -> WindowBounds { let windowLocationWithFixedCoordinates = WindowManager.convertYCoordinateBecauseTheAreTwoFuckingCoordinateSystems(point: windowLocation) -// drawCircleAt(x: windowLocationWithFixedCoordinates.x, y: windowLocationWithFixedCoordinates.y, diameter: 10, color: .blue) -// drawCircleAt(x: windowLocationWithFixedCoordinates.x + windowSize.width, y:windowLocationWithFixedCoordinates.y-windowSize.height, diameter: 10, color: .green) let topLeft = NSPoint(x: windowLocationWithFixedCoordinates.x, y: windowLocationWithFixedCoordinates.y) let topRight = NSPoint(x: windowLocationWithFixedCoordinates.x + windowSize.width, y: windowLocationWithFixedCoordinates.y) let bottomLeft = NSPoint(x: windowLocationWithFixedCoordinates.x, y: windowLocationWithFixedCoordinates.y - windowSize.height) let bottomRight = NSPoint(x: windowLocationWithFixedCoordinates.x + windowSize.width, y: windowLocationWithFixedCoordinates.y - windowSize.height) + // drawCircleAt(x: topLeft.x, y: topLeft.y, diameter: 10, color: .blue) + // drawCircleAt(x: topRight.x, y: topRight.y, diameter: 10, color: .blue) + // drawCircleAt(x: bottomLeft.x, y: bottomLeft.y, diameter: 10, color: .blue) + // drawCircleAt(x: bottomRight.x, y: bottomRight.y, diameter: 10, color: .blue) + return WindowBounds(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight) } } From e2e7338471b9f83a8f1fa109212f730afcfffd82 Mon Sep 17 00:00:00 2001 From: Pablo P Varela Date: Thu, 6 Jun 2024 23:06:47 +0200 Subject: [PATCH 6/6] 0.24.0 --- Swift Shift.xcodeproj/project.pbxproj | 8 ++++---- appcast.xml | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Swift Shift.xcodeproj/project.pbxproj b/Swift Shift.xcodeproj/project.pbxproj index 37f40d8..835e5e8 100644 --- a/Swift Shift.xcodeproj/project.pbxproj +++ b/Swift Shift.xcodeproj/project.pbxproj @@ -426,7 +426,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 0.23.0; + CURRENT_PROJECT_VERSION = 0.24.0; DEVELOPMENT_ASSET_PATHS = "\"Swift Shift/Preview Content\""; DEVELOPMENT_TEAM = 2TZ4Q825M7; ENABLE_HARDENED_RUNTIME = YES; @@ -442,7 +442,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 0.23.0; + MARKETING_VERSION = 0.24.0; PRODUCT_BUNDLE_IDENTIFIER = "com.pablopunk.Swift-Shift"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -460,7 +460,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 0.23.0; + CURRENT_PROJECT_VERSION = 0.24.0; DEVELOPMENT_ASSET_PATHS = "\"Swift Shift/Preview Content\""; DEVELOPMENT_TEAM = 2TZ4Q825M7; ENABLE_HARDENED_RUNTIME = YES; @@ -476,7 +476,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 0.23.0; + MARKETING_VERSION = 0.24.0; PRODUCT_BUNDLE_IDENTIFIER = "com.pablopunk.Swift-Shift"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/appcast.xml b/appcast.xml index 97bf1af..443d5eb 100644 --- a/appcast.xml +++ b/appcast.xml @@ -2,6 +2,14 @@ Swift Shift + + 0.24.0 + Thu, 06 Jun 2024 23:06:29 +0200 + 0.24.0 + 0.24.0 + 13.0 + + 0.23.0 Mon, 26 Feb 2024 23:42:53 +0100 @@ -18,13 +26,5 @@ 13.0 - - 0.21.0 - Mon, 26 Feb 2024 20:22:31 +0100 - 0.21.0 - 0.21.0 - 13.0 - - \ No newline at end of file