Skip to content

Commit

Permalink
✨ Ability to trigger Loop using double-click on trigger key
Browse files Browse the repository at this point in the history
  • Loading branch information
MrKai77 authored Sep 15, 2023
2 parents d147fb1 + ae0d4de commit 9783d06
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 25 deletions.
1 change: 1 addition & 0 deletions Loop/Extensions/Defaults+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ extension Defaults.Keys {
static let gradientColor = Key<Color>("gradientColor", default: Color(.black))

static let triggerKey = Key<TriggerKey>("trigger", default: TriggerKey.options[0])
static let doubleClickToTrigger = Key<Bool>("doubleClickToTrigger", default: false)
static let triggerDelay = Key<Float>("triggerDelay", default: 0)
static let radialMenuCornerRadius = Key<CGFloat>("radialMenuCornerRadius", default: 50)
static let radialMenuThickness = Key<CGFloat>("radialMenuThickness", default: 22)
Expand Down
61 changes: 46 additions & 15 deletions Loop/Managers/LoopManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,43 @@ class LoopManager {
private var frontmostWindow: Window?
private var screenWithMouse: NSScreen?

private var timer: DispatchSourceTimer? // Used when user has configured a trigger delay
private var triggerDelayTimer: DispatchSourceTimer?
private var lastTriggerKeyClick: Date = Date.now

func startObservingKeys() {
NSEvent.addGlobalMonitorForEvents(matching: NSEvent.EventTypeMask.flagsChanged) { event -> Void in
NSEvent.addGlobalMonitorForEvents(matching: NSEvent.EventTypeMask.flagsChanged) { event in
if event.keyCode == Defaults[.triggerKey].keycode {
if event.modifierFlags.rawValue == 256 {
if self.timer != nil {
self.timer?.cancel()
self.timer = nil
} else {
self.closeLoop()

let useTriggerDelay = Defaults[.triggerDelay] > 0.1
let useDoubleClickTrigger = Defaults[.doubleClickToTrigger]

if event.modifierFlags.rawValue == 256 {
if useTriggerDelay {
self.cancelTriggerDelayTimer()
}
self.closeLoop()
} else {
if self.timer == nil {
self.timer = DispatchSource.makeTimerSource(queue: .main)
self.timer!.schedule(deadline: .now() + .milliseconds(Int(Defaults[.triggerDelay]*1000)))
self.timer!.setEventHandler {
self.openLoop()
self.timer = nil
if useDoubleClickTrigger {
if abs(self.lastTriggerKeyClick.timeIntervalSinceNow) < NSEvent.doubleClickInterval {
if useTriggerDelay {
if self.triggerDelayTimer == nil {
self.startTriggerDelayTimer(seconds: Defaults[.triggerDelay]) {
self.openLoop()
}
}
} else {
self.openLoop()
}
}
self.timer!.resume()
self.lastTriggerKeyClick = Date.now
} else if useTriggerDelay {
if self.triggerDelayTimer == nil {
self.startTriggerDelayTimer(seconds: Defaults[.triggerDelay]) {
self.openLoop()
}
}
} else {
self.openLoop()
}
}
}
Expand Down Expand Up @@ -70,6 +86,21 @@ class LoopManager {
)
}

private func cancelTriggerDelayTimer() {
self.triggerDelayTimer?.cancel()
self.triggerDelayTimer = nil
}

private func startTriggerDelayTimer(seconds: Float, handler: @escaping () -> Void) {
self.triggerDelayTimer = DispatchSource.makeTimerSource(queue: .main)
self.triggerDelayTimer!.schedule(deadline: .now() + .milliseconds(Int(seconds*1000)))
self.triggerDelayTimer!.setEventHandler {
handler()
self.triggerDelayTimer = nil
}
self.triggerDelayTimer!.resume()
}

@objc private func currentWindowDirectionChanged(notification: Notification) {
if let direction = notification.userInfo?["direction"] as? WindowDirection {
currentResizingDirection = direction
Expand Down
15 changes: 10 additions & 5 deletions Loop/Settings/KeybindingSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Defaults
struct KeybindingSettingsView: View {

@Default(.triggerKey) var triggerKey
@Default(.doubleClickToTrigger) var doubleClickToTrigger
@Default(.triggerDelay) var triggerDelay
@Default(.useSystemAccentColor) var useSystemAccentColor
@Default(.customAccentColor) var customAccentColor
Expand All @@ -36,23 +37,27 @@ struct KeybindingSettingsView: View {
}
}
.onChange(of: self.triggerKey) { _ in
if self.triggerKey.triggerDelayRecommended &&
self.triggerDelay < 0.1 {
if self.triggerKey.doubleClickRecommended &&
!self.doubleClickToTrigger {
self.suggestAddingTriggerDelay.toggle()
}
}
.alert(
"The \(self.triggerKey.name.lowercased()) key is frequently used in other apps.",
isPresented: self.$suggestAddingTriggerDelay, actions: {
Button("OK") {
self.triggerDelay = 0.5
self.doubleClickToTrigger = true
}
Button("Cancel", role: .cancel) {
return
}
}, message: {
Text("Would you like to add a trigger delay? You can always change this later.")
})
Text("Would you like to enable \"Double-click to trigger Loop\"? "
+ "You can always change this later.")
}
)

Toggle("Double-click to trigger Loop", isOn: $doubleClickToTrigger)

HStack {
Stepper(
Expand Down
10 changes: 5 additions & 5 deletions Loop/Utilities/TriggerKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct TriggerKey: Codable, Hashable, Defaults.Serializable {
var symbol: String
var keySymbol: String
var keycode: CGKeyCode
var triggerDelayRecommended: Bool = false
var doubleClickRecommended: Bool = false

static let options: [TriggerKey] = [
TriggerKey(
Expand All @@ -28,7 +28,7 @@ struct TriggerKey: Codable, Hashable, Defaults.Serializable {
symbol: "control",
keySymbol: "custom.control.rectangle.fill",
keycode: .kVK_Control,
triggerDelayRecommended: true
doubleClickRecommended: true
),
TriggerKey(
name: "Right Control",
Expand All @@ -41,7 +41,7 @@ struct TriggerKey: Codable, Hashable, Defaults.Serializable {
symbol: "option",
keySymbol: "custom.option.rectangle.fill",
keycode: .kVK_Option,
triggerDelayRecommended: true
doubleClickRecommended: true
),
TriggerKey(
name: "Right Option",
Expand All @@ -54,7 +54,7 @@ struct TriggerKey: Codable, Hashable, Defaults.Serializable {
symbol: "command",
keySymbol: "custom.command.rectangle.fill",
keycode: .kVK_Command,
triggerDelayRecommended: true
doubleClickRecommended: true
),
TriggerKey(
name: "Right Command",
Expand All @@ -67,7 +67,7 @@ struct TriggerKey: Codable, Hashable, Defaults.Serializable {
symbol: "shift",
keySymbol: "custom.shift.rectangle.fill",
keycode: .kVK_Shift,
triggerDelayRecommended: true
doubleClickRecommended: true
),
TriggerKey(
name: "Right Shift",
Expand Down

0 comments on commit 9783d06

Please sign in to comment.