diff --git a/Loop/Core/Observers/KeybindTrigger.swift b/Loop/Core/Observers/KeybindTrigger.swift index 52cb4634..1617291f 100644 --- a/Loop/Core/Observers/KeybindTrigger.swift +++ b/Loop/Core/Observers/KeybindTrigger.swift @@ -103,6 +103,7 @@ final class KeybindTrigger { // If this is a valid event, don't passthrough let result = performKeybind( + keyCode: keyCode, type: event.type, isARepeat: event.getIntegerValueField(.keyboardEventAutorepeat) == 1, flags: filteredFlags, @@ -148,7 +149,7 @@ final class KeybindTrigger { /// - flags: modifier flags associated with this event. /// - isLoopOpen: whether Loop is currently open. /// - Returns: whether this event was processed by Loop. - private func performKeybind(type: CGEventType, isARepeat: Bool, flags: CGEventFlags, isLoopOpen: Bool) -> PerformKeybindResult { + private func performKeybind(keyCode: CGKeyCode, type: CGEventType, isARepeat: Bool, flags: CGEventFlags, isLoopOpen: Bool) -> PerformKeybindResult { let flagKeys = sideDependentTriggerKey ? flags.keyCodes : flags.keyCodes.baseModifiers let allPressedKeys: Set = pressedKeys.union(flagKeys) let actionKeys: Set = Set(allPressedKeys.subtracting(triggerKey).map(\.baseModifier)) @@ -172,7 +173,12 @@ final class KeybindTrigger { if type != .keyUp { // keyDown for flagsChanged if containsTrigger { - if let action = windowActionCache.actionsByKeybind[actionKeys] { + // Try an match directly with the action keys first, then fallback to just the key code. + // This prevents failures when the user is tapping the keys in rapid succession. + let initalMatch = windowActionCache.actionsByKeybind[actionKeys] + let fallbackMatch = windowActionCache.actionsByKeybind[[keyCode]] + + if let action = initalMatch ?? fallbackMatch { if !isARepeat || action.canRepeat { openLoop(startingAction: action, overrideExistingTriggerDelayTimerAction: true) } diff --git a/Loop/Window Management/Window Manipulation/WindowEngine.swift b/Loop/Window Management/Window Manipulation/WindowEngine.swift index c495af0d..6c564de5 100644 --- a/Loop/Window Management/Window Manipulation/WindowEngine.swift +++ b/Loop/Window Management/Window Manipulation/WindowEngine.swift @@ -11,6 +11,7 @@ import SwiftUI /// Handles execution of `WindowAction`s on windows within the user's workspace enum WindowEngine { + static var currentTask: Task<(), any Error>? /// Resize a Window /// - Parameters: /// - window: Window to be resized @@ -23,13 +24,16 @@ enum WindowEngine { on screen: NSScreen, completion: @escaping () -> () = {} ) { - Task.detached(priority: .userInitiated) { + currentTask?.cancel() + currentTask = Task.detached(priority: .userInitiated) { await resize( window, to: action, on: screen ) + try Task.checkCancellation() + completion() } } @@ -222,10 +226,12 @@ enum WindowEngine { try await window.setFrameAnimated(targetFrame, bounds: bounds) } else { window.setFrame(targetFrame, sizeFirst: willChangeScreens) + try Task.checkCancellation() } if !animate, !window.frame.approximatelyEqual(to: targetFrame) { window.setFrame(targetFrame) + try Task.checkCancellation() } handleSizeConstrainedWindow(window: window, bounds: bounds) diff --git a/Loop/Window Management/Window/Window.swift b/Loop/Window Management/Window/Window.swift index e13ceffd..349ee287 100644 --- a/Loop/Window Management/Window/Window.swift +++ b/Loop/Window Management/Window/Window.swift @@ -402,6 +402,7 @@ final class Window { try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<(), Error>) in Task { + try Task.checkCancellation() let animation = WindowTransformAnimation( rect, window: self,