Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions Sources/Stag/Models/HotKeyCombination+Display.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Cocoa

extension HotKeyCombination {
/// Human-readable shortcut string, e.g. "⇧⌘1". Modifiers are emitted in the
/// macOS-standard order (⌃⌥⇧⌘) followed by the key label. Returns "" when no
/// key is set (keyCode 0); callers present their own placeholder there.
var displayString: String {
guard keyCode != 0 else { return "" }
var parts: [String] = []
let flags = modifierFlags
if flags.contains(.control) { parts.append("\u{2303}") } // ⌃
if flags.contains(.option) { parts.append("\u{2325}") } // ⌥
if flags.contains(.shift) { parts.append("\u{21E7}") } // ⇧
if flags.contains(.command) { parts.append("\u{2318}") } // ⌘
parts.append(Self.keyName(keyCode))
return parts.joined()
}

/// Maps a macOS ANSI virtual keycode to its display label. Unknown codes fall
/// back to "Key<code>".
static func keyName(_ code: UInt16) -> String {
keyNames[code] ?? "Key\(code)"
}

/// True macOS ANSI keycodes (NOT sequential — e.g. kVK_ANSI_5 = 23, _6 = 22).
private static let keyNames: [UInt16: String] = [
18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6",
26: "7", 28: "8", 25: "9", 29: "0",
24: "=", 27: "-",
12: "Q", 13: "W", 14: "E", 15: "R", 16: "T", 17: "Y",
32: "U", 34: "I", 31: "O", 35: "P",
0: "A", 1: "S", 2: "D", 3: "F", 4: "H", 5: "G",
38: "J", 40: "K", 37: "L",
45: "N", 46: "M",
6: "Z", 7: "X", 8: "C", 9: "V", 11: "B",
49: "Space",
36: "Return", 53: "Esc", 48: "Tab", 51: "Delete",
]
}
28 changes: 1 addition & 27 deletions Sources/Stag/Views/PreferencesWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -830,33 +830,7 @@ private struct ShortcutRecorder: View {
private var displayText: String {
if isRecording { return "Press shortcut\u{2026}" }
if current.keyCode == 0 { return "None" }
var parts: [String] = []
let flags = current.modifierFlags
if flags.contains(.control) { parts.append("\u{2303}") }
if flags.contains(.option) { parts.append("\u{2325}") }
if flags.contains(.shift) { parts.append("\u{21E7}") }
if flags.contains(.command) { parts.append("\u{2318}") }
let key = keyName(current.keyCode)
parts.append(key)
return parts.joined()
}

private func keyName(_ code: UInt16) -> String {
let map: [UInt16: String] = [
// True macOS ANSI digit keycodes (NOT sequential).
18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6",
26: "7", 28: "8", 25: "9", 29: "0",
24: "=", 27: "-",
12: "Q", 13: "W", 14: "E", 15: "R", 16: "T", 17: "Y",
32: "U", 34: "I", 31: "O", 35: "P",
0: "A", 1: "S", 2: "D", 3: "F", 4: "H", 5: "G",
38: "J", 40: "K", 37: "L",
45: "N", 46: "M",
6: "Z", 7: "X", 8: "C", 9: "V", 11: "B",
49: "Space",
36: "Return", 53: "Esc", 48: "Tab", 51: "Delete",
]
return map[code] ?? "Key\(code)"
return current.displayString
}
}

Expand Down
42 changes: 42 additions & 0 deletions Tests/StagTests/HotKeyCombinationDisplayTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import XCTest
import Cocoa
@testable import Stag

/// Display-string formatting extracted from PreferencesWindow's shortcut row.
final class HotKeyCombinationDisplayTests: XCTestCase {

private func combo(_ keyCode: UInt16, _ flags: NSEvent.ModifierFlags) -> HotKeyCombination {
HotKeyCombination(keyCode: keyCode, modifiers: flags.rawValue)
}

func testCommandShiftDigit() {
// keyCode 18 == "1"; modifiers render in ⌃⌥⇧⌘ order.
XCTAssertEqual(combo(18, [.command, .shift]).displayString, "\u{21E7}\u{2318}1")
}

func testModifierOrderingIsControlOptionShiftCommand() {
let s = combo(8, [.command, .control, .shift, .option]).displayString // 8 == "C"
XCTAssertEqual(s, "\u{2303}\u{2325}\u{21E7}\u{2318}C")
}

func testNonSequentialDigitKeycodes() {
// Regression guard: kVK_ANSI_5 = 23, kVK_ANSI_6 = 22.
XCTAssertEqual(HotKeyCombination.keyName(23), "5")
XCTAssertEqual(HotKeyCombination.keyName(22), "6")
}

func testUnknownKeycodeFallsBack() {
XCTAssertEqual(HotKeyCombination.keyName(99), "Key99")
XCTAssertEqual(combo(99, [.command]).displayString, "\u{2318}Key99")
}

func testZeroKeyCodeIsEmpty() {
XCTAssertEqual(combo(0, [.command]).displayString, "")
}

func testSpecialKeyLabels() {
XCTAssertEqual(HotKeyCombination.keyName(49), "Space")
XCTAssertEqual(HotKeyCombination.keyName(36), "Return")
XCTAssertEqual(HotKeyCombination.keyName(53), "Esc")
}
}
Loading