Skip to content

Commit a24c838

Browse files
authored
Fix triggering return key actions, refactor a bit, ... (#267)
1 parent 67002df commit a24c838

File tree

8 files changed

+381
-210
lines changed

8 files changed

+381
-210
lines changed

Leader Key.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
/* Begin PBXBuildFile section */
1010
115AA5BF2DA521C600C17E18 /* ActionIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 115AA5BE2DA521C200C17E18 /* ActionIcon.swift */; };
11+
6D9B9C042DBA000000000002 /* KeyCapture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D9B9C032DBA000000000002 /* KeyCapture.swift */; };
12+
6D9B9C062DBA000000000003 /* ConfigEditorShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D9B9C052DBA000000000003 /* ConfigEditorShared.swift */; };
1113
115AA5C22DA546D500C17E18 /* SymbolPicker in Frameworks */ = {isa = PBXBuildFile; productRef = 115AA5C12DA546D500C17E18 /* SymbolPicker */; };
1214
130196C62D73B3DE0093148B /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 130196C52D73B3DC0093148B /* Breadcrumbs.swift */; };
1315
423632222D68CA6500878D92 /* MysteryBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 423632212D68CA6500878D92 /* MysteryBox.swift */; };
@@ -106,6 +108,8 @@
106108
605385A22D523CAD00BEDB4B /* Pulsate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pulsate.swift; sourceTree = "<group>"; };
107109
606C56EE2DAB875A00198B9F /* Cheater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cheater.swift; sourceTree = "<group>"; };
108110
6D9B9C002DBA000000000001 /* ConfigOutlineEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigOutlineEditorView.swift; sourceTree = "<group>"; };
111+
6D9B9C032DBA000000000002 /* KeyCapture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyCapture.swift; sourceTree = "<group>"; };
112+
6D9B9C052DBA000000000003 /* ConfigEditorShared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigEditorShared.swift; sourceTree = "<group>"; };
109113
/* End PBXFileReference section */
110114

111115
/* Begin PBXFrameworksBuildPhase section */
@@ -241,6 +245,8 @@
241245
6D9B9C002DBA000000000001 /* ConfigOutlineEditorView.swift */,
242246
427C18372BD3262100955B98 /* VisualEffectBackground.swift */,
243247
42F4CDCC2D45B13600D0DD76 /* KeyButton.swift */,
248+
6D9B9C032DBA000000000002 /* KeyCapture.swift */,
249+
6D9B9C052DBA000000000003 /* ConfigEditorShared.swift */,
244250
115AA5BE2DA521C200C17E18 /* ActionIcon.swift */,
245251
);
246252
path = Views;
@@ -420,6 +426,8 @@
420426
427C18282BD31E2E00955B98 /* GeneralPane.swift in Sources */,
421427
423632282D6A806700878D92 /* Theme.swift in Sources */,
422428
6D9B9C012DBA000000000001 /* ConfigOutlineEditorView.swift in Sources */,
429+
6D9B9C042DBA000000000002 /* KeyCapture.swift in Sources */,
430+
6D9B9C062DBA000000000003 /* ConfigEditorShared.swift in Sources */,
423431
42F4CDD12D48C52400D0DD76 /* Extensions.swift in Sources */,
424432
427C182F2BD3206200955B98 /* UserState.swift in Sources */,
425433
427C18202BD31C3D00955B98 /* AppDelegate.swift in Sources */,

Leader Key/AppDelegate.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class AppDelegate: NSObject, NSApplicationDelegate,
3636
AdvancedPane().environmentObject(self.config)
3737
}),
3838
],
39-
style: .segmentedControl
39+
style: .segmentedControl,
4040
)
4141

4242
func applicationDidFinishLaunching(_: Notification) {
@@ -147,7 +147,7 @@ class AppDelegate: NSObject, NSApplicationDelegate,
147147

148148
// MARK: - Sparkle Gentle Reminders
149149

150-
var supportsGentleScheduledUpdateReminders: Bool {
150+
@objc var supportsGentleScheduledUpdateReminders: Bool {
151151
return true
152152
}
153153

Leader Key/Controller.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,29 @@ class Controller {
220220
return englishGlyph(for: event)
221221
}
222222

223-
// 2. Use the system-translated character first.
223+
// 2. For special keys like Enter, always use the mapped glyph
224+
if let entry = KeyMaps.entry(for: event.keyCode) {
225+
// For Enter, Space, Tab, arrows, etc. - use the glyph representation
226+
if event.keyCode == KeyHelpers.enter.rawValue || event.keyCode == KeyHelpers.space.rawValue
227+
|| event.keyCode == KeyHelpers.tab.rawValue
228+
|| event.keyCode == KeyHelpers.leftArrow.rawValue
229+
|| event.keyCode == KeyHelpers.rightArrow.rawValue
230+
|| event.keyCode == KeyHelpers.upArrow.rawValue
231+
|| event.keyCode == KeyHelpers.downArrow.rawValue
232+
{
233+
return entry.glyph
234+
}
235+
}
236+
237+
// 3. Use the system-translated character for regular keys.
224238
if let printable = event.charactersIgnoringModifiers,
225239
!printable.isEmpty,
226240
printable.unicodeScalars.first?.isASCII ?? false
227241
{
228242
return printable // already contains correct case
229243
}
230244

231-
// 3. For arrows, ␣, ⌫ … use map as last resort.
245+
// 4. For arrows, ␣, ⌫ … use map as last resort.
232246
return englishGlyph(for: event)
233247
}
234248

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import AppKit
2+
import ObjectiveC
3+
4+
enum ConfigEditorUI {
5+
static func setButtonTitle(_ button: NSButton, text: String, placeholder: Bool) {
6+
let attr = NSMutableAttributedString(string: text)
7+
let color: NSColor = placeholder ? .secondaryLabelColor : .labelColor
8+
attr.addAttribute(
9+
.foregroundColor, value: color, range: NSRange(location: 0, length: attr.length))
10+
button.title = text
11+
button.attributedTitle = attr
12+
}
13+
14+
static func presentMoreMenu(
15+
anchor: NSView?,
16+
onDuplicate: @escaping () -> Void,
17+
onDelete: @escaping () -> Void
18+
) {
19+
guard let anchor else { return }
20+
let menu = NSMenu()
21+
menu.addItem(
22+
withTitle: "Duplicate",
23+
action: #selector(MenuHandler.duplicate),
24+
keyEquivalent: ""
25+
)
26+
menu.addItem(
27+
withTitle: "Delete",
28+
action: #selector(MenuHandler.delete),
29+
keyEquivalent: ""
30+
)
31+
let handler = MenuHandler(onDuplicate: onDuplicate, onDelete: onDelete)
32+
for item in menu.items { item.target = handler }
33+
objc_setAssociatedObject(
34+
menu,
35+
&handlerAssociationKey,
36+
handler,
37+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
38+
)
39+
let point = NSPoint(x: 0, y: anchor.bounds.height)
40+
menu.popUp(positioning: nil, at: point, in: anchor)
41+
}
42+
43+
static func presentIconMenu(
44+
anchor: NSView?,
45+
onPickAppIcon: @escaping () -> Void,
46+
onPickSymbol: @escaping () -> Void,
47+
onClear: @escaping () -> Void
48+
) {
49+
guard let anchor else { return }
50+
let menu = NSMenu()
51+
menu.addItem(
52+
withTitle: "App Icon…",
53+
action: #selector(MenuHandler.pickAppIcon),
54+
keyEquivalent: ""
55+
)
56+
menu.addItem(
57+
withTitle: "Symbol…",
58+
action: #selector(MenuHandler.pickSymbol),
59+
keyEquivalent: ""
60+
)
61+
menu.addItem(NSMenuItem.separator())
62+
menu.addItem(withTitle: "Clear", action: #selector(MenuHandler.clearIcon), keyEquivalent: "")
63+
let handler = MenuHandler(
64+
onPickAppIcon: onPickAppIcon,
65+
onPickSymbol: onPickSymbol,
66+
onClearIcon: onClear,
67+
onDuplicate: {},
68+
onDelete: {}
69+
)
70+
for item in menu.items { item.target = handler }
71+
objc_setAssociatedObject(
72+
menu,
73+
&handlerAssociationKey,
74+
handler,
75+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
76+
)
77+
let point = NSPoint(x: 0, y: anchor.bounds.height)
78+
menu.popUp(positioning: nil, at: point, in: anchor)
79+
}
80+
81+
private static var handlerAssociationKey: UInt8 = 0
82+
83+
private final class MenuHandler: NSObject {
84+
let onPickAppIcon: (() -> Void)?
85+
let onPickSymbol: (() -> Void)?
86+
let onClearIcon: (() -> Void)?
87+
let onDuplicate: () -> Void
88+
let onDelete: () -> Void
89+
90+
init(
91+
onPickAppIcon: (() -> Void)? = nil,
92+
onPickSymbol: (() -> Void)? = nil,
93+
onClearIcon: (() -> Void)? = nil,
94+
onDuplicate: @escaping () -> Void,
95+
onDelete: @escaping () -> Void
96+
) {
97+
self.onPickAppIcon = onPickAppIcon
98+
self.onPickSymbol = onPickSymbol
99+
self.onClearIcon = onClearIcon
100+
self.onDuplicate = onDuplicate
101+
self.onDelete = onDelete
102+
}
103+
104+
@objc func pickAppIcon() { onPickAppIcon?() }
105+
@objc func pickSymbol() { onPickSymbol?() }
106+
@objc func clearIcon() { onClearIcon?() }
107+
@objc func duplicate() { onDuplicate() }
108+
@objc func delete() { onDelete() }
109+
}
110+
}
111+
112+
extension Action {
113+
func resolvedIcon() -> NSImage? {
114+
if let iconPath = iconPath, !iconPath.isEmpty {
115+
if iconPath.hasSuffix(".app") { return NSWorkspace.shared.icon(forFile: iconPath) }
116+
if let img = NSImage(systemSymbolName: iconPath, accessibilityDescription: nil) { return img }
117+
}
118+
switch type {
119+
case .application:
120+
return NSWorkspace.shared.icon(forFile: value)
121+
case .url:
122+
return NSImage(systemSymbolName: "link", accessibilityDescription: nil)
123+
case .command:
124+
return NSImage(systemSymbolName: "terminal", accessibilityDescription: nil)
125+
case .folder:
126+
return NSImage(systemSymbolName: "folder", accessibilityDescription: nil)
127+
default:
128+
return NSImage(systemSymbolName: "questionmark", accessibilityDescription: nil)
129+
}
130+
}
131+
}
132+
133+
extension Group {
134+
func resolvedIcon() -> NSImage? {
135+
if let iconPath = iconPath, !iconPath.isEmpty {
136+
if iconPath.hasSuffix(".app") { return NSWorkspace.shared.icon(forFile: iconPath) }
137+
if let img = NSImage(systemSymbolName: iconPath, accessibilityDescription: nil) { return img }
138+
}
139+
return NSImage(systemSymbolName: "folder", accessibilityDescription: nil)
140+
}
141+
}

0 commit comments

Comments
 (0)