listless

A simple list app for Apple platforms
Log | Files | Refs | README | LICENSE

KeyboardNavigationModifier.swift (1603B)


      1 import SwiftUI
      2 
      3 struct ShortcutKey: Hashable {
      4     let key: KeyEquivalent
      5     let modifiers: EventModifiers
      6 
      7     init(key: KeyEquivalent, modifiers: EventModifiers = []) {
      8         self.key = key
      9         self.modifiers = modifiers
     10     }
     11 
     12     static func == (lhs: ShortcutKey, rhs: ShortcutKey) -> Bool {
     13         lhs.key == rhs.key && lhs.modifiers == rhs.modifiers
     14     }
     15 
     16     func hash(into hasher: inout Hasher) {
     17         hasher.combine(key)
     18         hasher.combine(modifiers.rawValue)
     19     }
     20 }
     21 
     22 extension View {
     23     func keyboardNavigation(_ bindings: [ShortcutKey: () -> KeyPress.Result]) -> some View {
     24         self.onKeyPress { press in
     25             let key = normalizeKey(press)
     26             let modifiers = normalizeModifiers(press.modifiers)
     27             let shortcut = ShortcutKey(key: key, modifiers: modifiers)
     28 
     29             if let action = bindings[shortcut] {
     30                 return action()
     31             }
     32             return .ignored
     33         }
     34     }
     35 
     36     private func normalizeKey(_ press: KeyPress) -> KeyEquivalent {
     37         // Normalize backspace/delete key
     38         if press.characters == "\u{7F}" {
     39             return .delete
     40         }
     41         return press.key
     42     }
     43 
     44     private func normalizeModifiers(_ modifiers: EventModifiers) -> EventModifiers {
     45         // Mask to only meaningful shortcut modifiers, excluding system artifacts
     46         // like .function (deprecated), .numericPad, .capsLock, etc.
     47         let shortcutModifierMask: EventModifiers = [.command, .shift, .option, .control]
     48         return EventModifiers(rawValue: modifiers.rawValue & shortcutModifierMask.rawValue)
     49     }
     50 }