Skip to content

Commit 5752272

Browse files
authored
Add direct global shortcuts to 1st level groups (#201)
* Add shortcuts to 1st level groups * Reset group shortcut if key change
1 parent 7aecc13 commit 5752272

6 files changed

Lines changed: 75 additions & 35 deletions

File tree

Leader Key.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,26 +176,26 @@
176176
427C17E92BD311B400955B98 /* Leader Key */ = {
177177
isa = PBXGroup;
178178
children = (
179-
42454DDA2D71CB39004E1374 /* ConfigValidator.swift */,
180179
42B21FBB2D67566100F4A2C7 /* Alerts.swift */,
181180
427C181F2BD31C3D00955B98 /* AppDelegate.swift */,
182181
42F4CDCE2D46E2B300D0DD76 /* Cheatsheet.swift */,
183182
426E625A2D2E6A98009FD2F2 /* CommandRunner.swift */,
183+
42454DDA2D71CB39004E1374 /* ConfigValidator.swift */,
184184
427C181B2BD314B500955B98 /* Constants.swift */,
185185
427C18272BD31E2E00955B98 /* Controller.swift */,
186186
427C184C2BD65C5C00955B98 /* Defaults.swift */,
187187
42DFCD712D5B7D46002EA111 /* Events.swift */,
188188
42F4CDD02D48C51F00D0DD76 /* Extensions.swift */,
189189
423632122D676F5200878D92 /* FileMonitor.swift */,
190190
42F4CDC82D458FF700D0DD76 /* MainMenu.swift */,
191+
427C18252BD31E2E00955B98 /* MainWindow.swift */,
191192
427C18532BD6E59300955B98 /* NSWindow+Animations.swift */,
192193
427C182C2BD31F9500955B98 /* Settings.swift */,
193194
427C18262BD31E2E00955B98 /* StatusItem.swift */,
194195
423632272D6A806700878D92 /* Theme.swift */,
195196
427C183A2BD329F900955B98 /* UserConfig.swift */,
196197
427C182E2BD3206200955B98 /* UserState.swift */,
197198
427C184F2BD6652500955B98 /* Util.swift */,
198-
427C18252BD31E2E00955B98 /* MainWindow.swift */,
199199
427C17EE2BD311B500955B98 /* Assets.xcassets */,
200200
423632242D68CC5D00878D92 /* Settings */,
201201
427C18362BD3243C00955B98 /* Support */,

Leader Key/AppDelegate.swift

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -98,21 +98,41 @@ class AppDelegate: NSObject, NSApplicationDelegate,
9898
}
9999
}
100100

101+
registerGlobalShortcuts()
102+
}
103+
104+
func activate() {
105+
if self.controller.window.isKeyWindow {
106+
switch Defaults[.reactivateBehavior] {
107+
case .hide:
108+
self.hide()
109+
case .reset:
110+
self.controller.userState.clear()
111+
case .nothing:
112+
return
113+
}
114+
} else if self.controller.window.isVisible {
115+
// should never happen as the window will self-hide when not key
116+
self.controller.window.makeKeyAndOrderFront(nil)
117+
} else {
118+
self.show()
119+
}
120+
}
121+
122+
func registerGlobalShortcuts() {
123+
KeyboardShortcuts.removeAllHandlers()
124+
101125
KeyboardShortcuts.onKeyDown(for: .activate) {
102-
if self.controller.window.isKeyWindow {
103-
switch Defaults[.reactivateBehavior] {
104-
case .hide:
105-
self.hide()
106-
case .reset:
107-
self.controller.userState.clear()
108-
case .nothing:
109-
return
126+
self.activate()
127+
}
128+
129+
for groupKey in Defaults[.groupShortcuts] {
130+
print("Registering shortcut for \(groupKey)")
131+
KeyboardShortcuts.onKeyDown(for: KeyboardShortcuts.Name("group-\(groupKey)")) {
132+
if !self.controller.window.isVisible {
133+
self.activate()
110134
}
111-
} else if self.controller.window.isVisible {
112-
// should never happen as the window will self-hide when not key
113-
self.controller.window.makeKeyAndOrderFront(nil)
114-
} else {
115-
self.show()
135+
self.processKeys([groupKey])
116136
}
117137
}
118138
}

Leader Key/Defaults.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ extension Defaults.Keys {
3535
"reactivateBehavior", default: .hide, suite: defaultsSuite)
3636
static let screen = Key<Screen>(
3737
"screen", default: .primary, suite: defaultsSuite)
38+
39+
static let groupShortcuts = Key<Set<String>>(
40+
"groupShortcuts",
41+
default: Set(), suite: defaultsSuite)
3842
}
3943

4044
enum AutoOpenCheatsheetSetting: String, Defaults.Serializable {

Leader Key/Util.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import Foundation
21
import AppKit
2+
import Foundation
33

44
func delay(_ milliseconds: Int, callback: @escaping () -> Void) {
55
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(milliseconds), execute: callback)
@@ -12,4 +12,3 @@ extension NSScreen {
1212
return NSPoint(x: x, y: y)
1313
}
1414
}
15-

Leader Key/Views/ConfigEditorView.swift

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Defaults
2+
import KeyboardShortcuts
23
import SwiftUI
34
import SymbolPicker
45

@@ -215,7 +216,7 @@ struct ActionRow: View {
215216
get: { action.key ?? "" },
216217
set: { action.key = $0 }
217218
), placeholder: "Key", validationError: validationErrorForKey,
218-
onKeyChanged: { userConfig.finishEditingKey() }
219+
onKeyChanged: { _, _ in userConfig.finishEditingKey() }
219220
)
220221

221222
Picker("Type", selection: $action.type) {
@@ -336,7 +337,13 @@ struct GroupRow: View {
336337
),
337338
placeholder: "Group Key",
338339
validationError: validationErrorForKey,
339-
onKeyChanged: { userConfig.finishEditingKey() }
340+
onKeyChanged: { maybePrev, value in
341+
if maybePrev != value, let prev = maybePrev {
342+
Defaults[.groupShortcuts].remove(prev)
343+
KeyboardShortcuts.reset([KeyboardShortcuts.Name("group-\(prev)")])
344+
}
345+
userConfig.finishEditingKey()
346+
}
340347
)
341348

342349
IconPickerMenu(
@@ -360,8 +367,20 @@ struct GroupRow: View {
360367
) {
361368
Image(systemName: "chevron.right")
362369
.rotationEffect(.degrees(isExpanded ? 90 : 0))
363-
.padding(.leading, generalPadding / 3)
364-
}.buttonStyle(.plain)
370+
.padding(.horizontal, -3)
371+
}.buttonStyle(.bordered)
372+
373+
if path.count == 1 && group.key != "", let key = group.key {
374+
KeyboardShortcuts.Recorder(for: KeyboardShortcuts.Name("group-\(key)")) { shortcut in
375+
if shortcut != nil {
376+
Defaults[.groupShortcuts].insert(key)
377+
} else {
378+
Defaults[.groupShortcuts].remove(key)
379+
}
380+
381+
(NSApplication.shared.delegate as! AppDelegate).registerGlobalShortcuts()
382+
}
383+
}
365384

366385
Spacer(minLength: 0)
367386

@@ -478,6 +497,6 @@ struct GroupRow: View {
478497
let userConfig = UserConfig()
479498

480499
return ConfigEditorView(group: .constant(group), expandedGroups: .constant(Set<[Int]>()))
481-
.frame(width: 600, height: 500)
500+
.frame(width: 720, height: 500)
482501
.environmentObject(userConfig)
483502
}

Leader Key/Views/KeyButton.swift

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import AppKit
22
import SwiftUI
33

4+
typealias KeyChangedFn = ((_ before: String?, _ value: String?) -> Void)
5+
46
struct KeyButton: View {
57
@Binding var text: String
68
let placeholder: String
79
@State private var isListening = false
810
@State private var oldValue = ""
911
var validationError: ValidationErrorType? = nil
10-
var onKeyChanged: (() -> Void)? = nil
12+
var onKeyChanged: KeyChangedFn? = nil
1113

1214
var body: some View {
1315
Button(action: {
@@ -57,7 +59,7 @@ struct KeyListenerView: NSViewRepresentable {
5759
@Binding var isListening: Bool
5860
@Binding var text: String
5961
@Binding var oldValue: String
60-
var onKeyChanged: (() -> Void)?
62+
var onKeyChanged: KeyChangedFn?
6163

6264
func makeNSView(context: Context) -> NSView {
6365
let view = KeyListenerNSView()
@@ -87,7 +89,7 @@ struct KeyListenerView: NSViewRepresentable {
8789
var isListening: Binding<Bool>?
8890
var text: Binding<String>?
8991
var oldValue: Binding<String>?
90-
var onKeyChanged: (() -> Void)?
92+
var onKeyChanged: KeyChangedFn?
9193

9294
override var acceptsFirstResponder: Bool { true }
9395

@@ -126,15 +128,15 @@ struct KeyListenerView: NSViewRepresentable {
126128

127129
DispatchQueue.main.async {
128130
isListening.wrappedValue = false
129-
self.onKeyChanged?()
131+
self.onKeyChanged?(self.oldValue?.wrappedValue, self.text?.wrappedValue)
130132
}
131133
}
132134

133135
override func resignFirstResponder() -> Bool {
134136
if let isListening = isListening, isListening.wrappedValue {
135137
DispatchQueue.main.async {
136138
isListening.wrappedValue = false
137-
self.onKeyChanged?()
139+
self.onKeyChanged?(self.oldValue?.wrappedValue, self.text?.wrappedValue)
138140
}
139141
}
140142
return super.resignFirstResponder()
@@ -151,26 +153,22 @@ struct KeyListenerView: NSViewRepresentable {
151153
VStack(spacing: 20) {
152154
KeyButton(
153155
text: $text,
154-
placeholder: "Key",
155-
onKeyChanged: { print("Key changed") }
156+
placeholder: "Key"
156157
)
157158
KeyButton(
158159
text: $text,
159160
placeholder: "Key",
160-
validationError: .duplicateKey,
161-
onKeyChanged: { print("Key changed") }
161+
validationError: .duplicateKey
162162
)
163163
KeyButton(
164164
text: $text,
165165
placeholder: "Key",
166-
validationError: .emptyKey,
167-
onKeyChanged: { print("Key changed") }
166+
validationError: .emptyKey
168167
)
169168
KeyButton(
170169
text: $text,
171170
placeholder: "Key",
172-
validationError: .nonSingleCharacterKey,
173-
onKeyChanged: { print("Key changed") }
171+
validationError: .nonSingleCharacterKey
174172
)
175173
Text("Current value: '\(text)'")
176174
}

0 commit comments

Comments
 (0)