fix: rare crash when assigning shortcuts with arrow keys

This commit is contained in:
lwouis committed 2026-05-25 09:35:27 +02:00
1 parent f4a54c8f1b
commit 49778089ea
24 files changed
+132 -30

No files matched your search

+3
View File
@@ -160,6 +160,9 @@
/* No comment provided by engineer. */
"Arrow keys" = "Arrow keys";
/* No comment provided by engineer. */
"Arrow keys already assigned to other actions:\n%@" = "Arrow keys already assigned to other actions:\n%@";
/* No comment provided by engineer. */
"Ask whether to send crash reports" = "Ask whether to send crash reports";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "اسم التطبيق - عنوان النافذة";
"Applies in other Appearances" = "يُطبَّق في المظاهر الأخرى";
"Arrow keys" = "مفاتيح الأسهم";
"Arrow keys already assigned to other actions:\n%@" = "مفاتيح الأسهم معيّنة بالفعل لإجراءات أخرى:\n%@";
"Ask whether to send crash reports" = "السؤال عن إرسال تقارير الأعطال";
"Auto" = "تلقائي";
"Auto Size — larger previews with few windows, more at once with many" = "حجم تلقائي — معاينات أكبر مع عدد قليل من النوافذ، وأكثر مرة واحدة مع الكثير منها";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Anwendungsname - Fenstertitel";
"Applies in other Appearances" = "Gilt in anderen Darstellungen";
"Arrow keys" = "Pfeiltasten";
"Arrow keys already assigned to other actions:\n%@" = "Pfeiltasten sind bereits anderen Aktionen zugewiesen:\n%@";
"Ask whether to send crash reports" = "Fragen, ob Absturzberichte gesendet werden sollen";
"Auto" = "Automatisch";
"Auto Size — larger previews with few windows, more at once with many" = "Automatische Größe — größere Vorschauen bei wenigen Fenstern, mehr auf einmal bei vielen";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Application Name - Window Title";
"Applies in other Appearances" = "Applies in other Appearances";
"Arrow keys" = "Arrow keys";
"Arrow keys already assigned to other actions:\n%@" = "Arrow keys already assigned to other actions:\n%@";
"Ask whether to send crash reports" = "Ask whether to send crash reports";
"Auto" = "Auto";
"Auto Size — larger previews with few windows, more at once with many" = "Auto Size — larger previews with few windows, more at once with many";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Nombre de la aplicación - Título de la ventana";
"Applies in other Appearances" = "Se aplica en otras Apariencias";
"Arrow keys" = "Teclas de flecha";
"Arrow keys already assigned to other actions:\n%@" = "Teclas de flecha ya asignadas a otras acciones:\n%@";
"Ask whether to send crash reports" = "Preguntar si enviar los informes de problema";
"Auto" = "Automático";
"Auto Size — larger previews with few windows, more at once with many" = "Tamaño automático — previsualizaciones más grandes con pocas ventanas, más a la vez con muchas";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Nom de l'application - Titre de la fenêtre";
"Applies in other Appearances" = "S'applique aux autres apparences";
"Arrow keys" = "Touches fléchées";
"Arrow keys already assigned to other actions:\n%@" = "Touches fléchées déjà affectées à d'autres actions :\n%@";
"Ask whether to send crash reports" = "Demander avant d'envoyer un rapport de plantage";
"Auto" = "Auto";
"Auto Size — larger previews with few windows, more at once with many" = "Taille automatique — aperçus plus grands avec peu de fenêtres, plus à la fois quand il y en a beaucoup";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "שם היישום - כותרת החלון";
"Applies in other Appearances" = "חל במראים אחרים";
"Arrow keys" = "מקשי חצים";
"Arrow keys already assigned to other actions:\n%@" = "מקשי חצים כבר משויכים לפעולות אחרות:\n%@";
"Ask whether to send crash reports" = "שאל לפני שליחת דיווחי קריסה";
"Auto" = "אוטומטי";
"Auto Size — larger previews with few windows, more at once with many" = "גודל אוטומטי — תצוגות מקדימות גדולות יותר עם מעט חלונות, יותר בבת אחת עם רבים";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Nama Aplikasi - Judul Jendela";
"Applies in other Appearances" = "Berlaku pada Tampilan lain";
"Arrow keys" = "Tombol panah";
"Arrow keys already assigned to other actions:\n%@" = "Tombol panah sudah ditetapkan untuk tindakan lain:\n%@";
"Ask whether to send crash reports" = "Tanya apakah akan mengirim laporan kerusakan";
"Auto" = "Otomatis";
"Auto Size — larger previews with few windows, more at once with many" = "Ukuran Otomatis — pratinjau lebih besar saat jendela sedikit, lebih banyak sekaligus saat jendela banyak";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Nome dell'applicazione - Titolo della finestra";
"Applies in other Appearances" = "Si applica negli altri Aspetti";
"Arrow keys" = "Tasti freccia";
"Arrow keys already assigned to other actions:\n%@" = "Tasti freccia già assegnati ad altre azioni:\n%@";
"Ask whether to send crash reports" = "Chiedi se inviare i resoconti di arresti anomali";
"Auto" = "Automatico";
"Auto Size — larger previews with few windows, more at once with many" = "Dimensione automatica — anteprime più grandi con poche finestre, più contemporaneamente con molte";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "アプリケーション名 - ウインドウタイトル";
"Applies in other Appearances" = "他の外観にも適用されます";
"Arrow keys" = "矢印キー";
"Arrow keys already assigned to other actions:\n%@" = "矢印キーはすでに他のアクションに割り当てられています:\n%@";
"Ask whether to send crash reports" = "クラッシュレポートの送信を毎回確認";
"Auto" = "自動";
"Auto Size — larger previews with few windows, more at once with many" = "自動サイズ — ウインドウが少ないと大きく、多いとまとめて表示";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "응용 프로그램 이름 - 윈도우 제목";
"Applies in other Appearances" = "다른 모양에도 적용됨";
"Arrow keys" = "화살표 키";
"Arrow keys already assigned to other actions:\n%@" = "화살표 키가 이미 다른 동작에 할당되어 있습니다:\n%@";
"Ask whether to send crash reports" = "충돌 보고서 전송 여부 물어보기";
"Auto" = "자동";
"Auto Size — larger previews with few windows, more at once with many" = "자동 크기 — 윈도우가 적으면 미리보기가 크고, 많으면 한 번에 더 많이 표시";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Naam van app - Venstertitel";
"Applies in other Appearances" = "Geldt in andere weergaven";
"Arrow keys" = "Pijltjestoetsen";
"Arrow keys already assigned to other actions:\n%@" = "Pijltjestoetsen zijn al toegewezen aan andere acties:\n%@";
"Ask whether to send crash reports" = "Vragen of crashrapporten verstuurd moeten worden";
"Auto" = "Automatisch";
"Auto Size — larger previews with few windows, more at once with many" = "Automatische grootte — grotere voorvertoningen bij weinig vensters, meer tegelijk bij veel";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Nazwa aplikacji - Tytuł okna";
"Applies in other Appearances" = "Stosuje się w innych Wyglądach";
"Arrow keys" = "Klawisze strzałek";
"Arrow keys already assigned to other actions:\n%@" = "Klawisze strzałek są już przypisane do innych akcji:\n%@";
"Ask whether to send crash reports" = "Pytaj o wysyłanie raportów awarii";
"Auto" = "Automatycznie";
"Auto Size — larger previews with few windows, more at once with many" = "Automatyczny rozmiar — większe podglądy przy małej liczbie okien, więcej naraz przy dużej";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Nome do Aplicativo - Título da Janela";
"Applies in other Appearances" = "Aplica-se a outras aparências";
"Arrow keys" = "Teclas de seta";
"Arrow keys already assigned to other actions:\n%@" = "Teclas de seta já atribuídas a outras ações:\n%@";
"Ask whether to send crash reports" = "Perguntar se deve enviar relatórios de falha";
"Auto" = "Automático";
"Auto Size — larger previews with few windows, more at once with many" = "Tamanho automático — pré-visualizações maiores com poucas janelas, mais janelas de uma vez quando houver muitas";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Название приложения — Заголовок окна";
"Applies in other Appearances" = "Применяется в других оформлениях";
"Arrow keys" = "Клавиши со стрелками";
"Arrow keys already assigned to other actions:\n%@" = "Клавиши со стрелками уже назначены другим действиям:\n%@";
"Ask whether to send crash reports" = "Спрашивать перед отправкой отчётов о сбоях";
"Auto" = "Авто";
"Auto Size — larger previews with few windows, more at once with many" = "Автоматический размер — крупнее при малом числе окон, компактнее при большом";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Programnamn - Fönstertitel";
"Applies in other Appearances" = "Gäller i andra Utseenden";
"Arrow keys" = "Piltangenter";
"Arrow keys already assigned to other actions:\n%@" = "Piltangenter är redan tilldelade andra åtgärder:\n%@";
"Ask whether to send crash reports" = "Fråga om kraschrapporter ska skickas";
"Auto" = "Auto";
"Auto Size — larger previews with few windows, more at once with many" = "Automatisk storlek — större förhandsvisningar med få fönster, fler åt gången med många";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "ชื่อแอปพลิเคชัน - ชื่อหน้าต่าง";
"Applies in other Appearances" = "ใช้ในลักษณะอื่น";
"Arrow keys" = "ปุ่มลูกศร";
"Arrow keys already assigned to other actions:\n%@" = "ปุ่มลูกศรถูกกำหนดให้กับการกระทำอื่นแล้ว:\n%@";
"Ask whether to send crash reports" = "ถามว่าจะส่งรายงานการขัดข้องหรือไม่";
"Auto" = "อัตโนมัติ";
"Auto Size — larger previews with few windows, more at once with many" = "ขนาดอัตโนมัติ — ภาพตัวอย่างใหญ่ขึ้นเมื่อมีหน้าต่างน้อย แสดงพร้อมกันมากขึ้นเมื่อมีหน้าต่างเยอะ";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Uygulama Adı - Pencere Başlığı";
"Applies in other Appearances" = "Diğer Görünümlerde uygulanır";
"Arrow keys" = "Ok tuşları";
"Arrow keys already assigned to other actions:\n%@" = "Ok tuşları zaten başka eylemlere atanmış:\n%@";
"Ask whether to send crash reports" = "Çökme raporlarının gönderilip gönderilmeyeceğini sor";
"Auto" = "Otomatik";
"Auto Size — larger previews with few windows, more at once with many" = "Otomatik Boyut — az pencerede büyük önizlemeler, çok pencerede aynı anda daha fazlası";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "Tên ứng dụng - Tiêu đề cửa sổ";
"Applies in other Appearances" = "Áp dụng trong các Giao diện khác";
"Arrow keys" = "Phím mũi tên";
"Arrow keys already assigned to other actions:\n%@" = "Phím mũi tên đã được gán cho các hành động khác:\n%@";
"Ask whether to send crash reports" = "Hỏi trước khi gửi báo cáo sự cố";
"Auto" = "Tự động";
"Auto Size — larger previews with few windows, more at once with many" = "Kích cỡ tự động — bản xem trước lớn hơn khi có ít cửa sổ, hiển thị nhiều hơn cùng lúc khi có nhiều cửa sổ";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "应用名称 - 窗口标题";
"Applies in other Appearances" = "适用于其他外观";
"Arrow keys" = "方向键";
"Arrow keys already assigned to other actions:\n%@" = "方向键已分配给其他操作:\n%@";
"Ask whether to send crash reports" = "询问是否发送崩溃报告";
"Auto" = "自动";
"Auto Size — larger previews with few windows, more at once with many" = "自动大小 — 窗口较少时预览更大,窗口较多时一次显示更多";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "應用程式名稱 - 視窗標題";
"Applies in other Appearances" = "套用至其他外觀";
"Arrow keys" = "方向鍵";
"Arrow keys already assigned to other actions:\n%@" = "方向鍵已指派給其他動作:\n%@";
"Ask whether to send crash reports" = "詢問是否傳送當機報告";
"Auto" = "自動";
"Auto Size — larger previews with few windows, more at once with many" = "自動大小 — 視窗少時預覽較大,視窗多時一次顯示更多";
@@ -52,6 +52,7 @@
"Application Name - Window Title" = "應用程式名稱 - 視窗標題";
"Applies in other Appearances" = "套用至其他外觀";
"Arrow keys" = "方向鍵";
"Arrow keys already assigned to other actions:\n%@" = "方向鍵已指派給其他動作:\n%@";
"Ask whether to send crash reports" = "詢問是否傳送當機報告";
"Auto" = "自動";
"Auto Size — larger previews with few windows, more at once with many" = "自動調整大小 — 視窗少時預覽較大,視窗多時一次顯示更多";
+49 -19
View File
@@ -41,15 +41,39 @@ class CustomRecorderControl: RecorderControl {
set(allowedModifierFlags: CustomRecorderControl.allowedModifiers.subtracting(restrictedModifiers), requiredModifierFlags: [], allowsEmptyModifierFlags: true)
}
private enum ShortcutConflict {
case arrow
case vim
case regular(control: CustomRecorderControl, label: String)
case unknown
var label: String {
switch self {
case .arrow: return "Arrow keys"
case .vim: return "Vim keys"
case .regular(_, let label): return label
case .unknown: return "an unknown action"
}
}
static func classify(_ id: String) -> ShortcutConflict {
if ["←", "→", "↑", "↓"].contains(id) { return .arrow }
if id.starts(with: "vimCycle") { return .vim }
if let existing = ControlsTab.shortcutControls[id] {
return .regular(control: existing.0, label: existing.1)
}
assertionFailure("conflict id '\(id)' missing from shortcutControls")
return .unknown
}
}
func alertIfSameShortcutAlreadyAssigned(_ candidateShortcut: Shortcut, _ shortcutAlreadyAssigned: String) {
let isArrowKeys = ["←", "→", "↑", "↓"].contains(shortcutAlreadyAssigned)
let isVimKeys = shortcutAlreadyAssigned.starts(with: "vimCycle")
let existingShortcut = ControlsTab.shortcutControls[shortcutAlreadyAssigned]!
let conflict = ShortcutConflict.classify(shortcutAlreadyAssigned)
let alert = NSAlert()
alert.alertStyle = .warning
alert.messageText = NSLocalizedString("Conflicting shortcut", comment: "")
alert.informativeText = String(format: NSLocalizedString("Shortcut already assigned to another action: %@", comment: ""),
(isArrowKeys ? "Arrow keys" : (isVimKeys ? "Vim keys" : existingShortcut.1)).replacingOccurrences(of: " ", with: "\u{00A0}"))
conflict.label.replacingOccurrences(of: " ", with: "\u{00A0}"))
if !id.starts(with: "holdShortcut") {
alert.addButton(withTitle: NSLocalizedString("Unassign existing shortcut and continue", comment: "")).setAccessibilityFocused(true)
}
@@ -59,22 +83,26 @@ class CustomRecorderControl: RecorderControl {
cancelButton.setAccessibilityFocused(true)
}
let userChoice = alert.runModal()
if !id.starts(with: "holdShortcut") && userChoice == .alertFirstButtonReturn {
if isArrowKeys, let cb = ControlsTab.arrowKeysCheckbox {
guard !id.starts(with: "holdShortcut"), userChoice == .alertFirstButtonReturn else { return }
switch conflict {
case .arrow:
if let cb = ControlsTab.arrowKeysCheckbox {
cb.state = .off
ControlsTab.arrowKeysEnabledCallback(cb)
LabelAndControl.controlWasChanged(cb, nil)
} else if isVimKeys, let cb = ControlsTab.vimKeysCheckbox {
}
case .vim:
if let cb = ControlsTab.vimKeysCheckbox {
cb.state = .off
ControlsTab.vimKeysEnabledCallback(cb)
LabelAndControl.controlWasChanged(cb, nil)
} else {
updateShortcut(existingShortcut.0, nil, existingShortcut.0, shortcutAlreadyAssigned)
}
if let target = ControlsTab.shortcutControls[id]?.0 {
updateShortcut(target, candidateShortcut, self, id)
}
case .regular(let existingControl, _):
updateShortcut(existingControl, nil, existingControl, shortcutAlreadyAssigned)
case .unknown:
return
}
updateShortcut(self, candidateShortcut, self, id)
}
func updateShortcut(_ control: CustomRecorderControl, _ objectValue: Shortcut?, _ senderControl: NSControl, _ id: String) {
@@ -84,22 +112,24 @@ class CustomRecorderControl: RecorderControl {
}
func alertIfShortcutReservedByMacos(_ candidateShortcut: Shortcut, _ shortcutReservedByMacos: String) {
let existingShortcutLabel = ControlsTab.shortcutControls[shortcutReservedByMacos]!.1
let existing = ControlsTab.shortcutControls[shortcutReservedByMacos]
if existing == nil {
assertionFailure("reserved-by-macos id '\(shortcutReservedByMacos)' missing from shortcutControls")
}
let alert = NSAlert()
alert.alertStyle = .warning
alert.messageText = NSLocalizedString("Conflicting shortcut", comment: "")
alert.informativeText = String(format: NSLocalizedString("macOS reserves ⌘⌥⎋, ⌘⌥⇧⎋, and ⌘⌥⇧⌃⎋ for Force Quit and they cannot be unbound. AltTab cannot use them.\n\nYour change would assign one of these to: %@.", comment: ""), existingShortcutLabel)
alert.informativeText = String(format: NSLocalizedString("macOS reserves ⌘⌥⎋, ⌘⌥⇧⎋, and ⌘⌥⇧⌃⎋ for Force Quit and they cannot be unbound. AltTab cannot use them.\n\nYour change would assign one of these to: %@.", comment: ""), existing?.1 ?? "an unknown action")
alert.addButton(withTitle: NSLocalizedString("Unassign existing shortcut and continue", comment: ""))
let cancelButton = alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
cancelButton.keyEquivalent = "\u{1b}"
cancelButton.setAccessibilityFocused(true)
let userChoice = alert.runModal()
if userChoice == .alertFirstButtonReturn {
guard id != shortcutReservedByMacos else { return }
let existingShortcut = ControlsTab.shortcutControls[shortcutReservedByMacos]!
updateShortcut(existingShortcut.0, nil, existingShortcut.0, shortcutReservedByMacos)
updateShortcut(ControlsTab.shortcutControls[id]!.0, candidateShortcut, self, id)
guard userChoice == .alertFirstButtonReturn, id != shortcutReservedByMacos else { return }
if let existing {
updateShortcut(existing.0, nil, existing.0, shortcutReservedByMacos)
}
updateShortcut(self, candidateShortcut, self, id)
}
func save(_ candidateShortcut: Shortcut) {
@@ -51,6 +51,7 @@ class ControlsTab {
"showAppsOrWindows",
]
private static let arrowKeys = ["←", "→", "↑", "↓"]
private static let arrowKeyCodes: Set<KeyCode> = [.leftArrow, .rightArrow, .upArrow, .downArrow]
private static let vimKeyActions = [
"h": "vimCycleLeft",
"l": "vimCycleRight",
@@ -92,7 +93,7 @@ class ControlsTab {
static func initializePreferencesDependentState() {
applyActiveShortcutPreferences()
staticManagedShortcutPreferences.forEach { applyShortcutPreference($0) }
applyArrowKeysPreference()
applyArrowKeysPreferenceWithoutDialogs()
applyVimKeysPreferenceWithoutDialogs()
}
@@ -123,8 +124,8 @@ class ControlsTab {
// An override key changed (e.g. user picked a different value, or remembered-restore wrote).
refreshUnlinkButtons()
AppearanceTab.refreshAllOverrideInfoLabels()
case "arrowKeysEnabled":
applyArrowKeysPreference()
case "arrowKeysEnabled" where arrowKeysCheckbox == nil:
applyArrowKeysPreferenceWithoutDialogs()
case "vimKeysEnabled" where vimKeysCheckbox == nil:
applyVimKeysPreferenceWithoutDialogs()
default:
@@ -1197,7 +1198,42 @@ class ControlsTab {
}
@objc static func arrowKeysEnabledCallback(_ sender: NSControl) {
applyArrowKeysPreference()
if (sender as! Switch).state == .on {
if isClearArrowKeysSuccessful() {
arrowKeys.forEach { addShortcut(.down, .local, Shortcut(keyEquivalent: $0)!, $0, nil) }
} else {
(sender as! Switch).state = .off
Preferences.set("arrowKeysEnabled", "false", false)
}
} else {
arrowKeys.forEach { removeShortcutIfExists($0) }
}
}
private static func isClearArrowKeysSuccessful() -> Bool {
var conflicts = [String: String]()
shortcuts.forEach {
guard arrowKeyCodes.contains($1.shortcut.keyCode) else { return }
guard !arrowKeys.contains($1.id) else { return }
if let label = conflictLabel($1.id) {
conflicts[$1.id] = label
}
}
if !conflicts.isEmpty {
if SettingsWindow.shared == nil || !shouldClearConflictingShortcuts(conflicts.map { $0.value }, NSLocalizedString("Arrow keys already assigned to other actions:\n%@", comment: "")) {
return false
}
conflicts.forEach {
removeShortcutIfExists($0.key)
let existing = shortcutControls[$0.key]
if existing != nil {
existing!.0.objectValue = nil
shortcutChangedCallback(existing!.0)
LabelAndControl.controlWasChanged(existing!.0, $0.key)
}
}
}
return true
}
@objc static func vimKeysEnabledCallback(_ sender: NSControl) {
@@ -1226,7 +1262,7 @@ class ControlsTab {
}
}
if !conflicts.isEmpty {
if SettingsWindow.shared == nil || !shouldClearConflictingShortcuts(conflicts.map { $0.value }) {
if SettingsWindow.shared == nil || !shouldClearConflictingShortcuts(conflicts.map { $0.value }, NSLocalizedString("Vim keys already assigned to other actions:\n%@", comment: "")) {
return false
}
conflicts.forEach {
@@ -1255,12 +1291,12 @@ class ControlsTab {
return nil
}
private static func shouldClearConflictingShortcuts(_ conflicts: [String]) -> Bool {
private static func shouldClearConflictingShortcuts(_ conflicts: [String], _ messageFormat: String) -> Bool {
let alert = NSAlert()
alert.alertStyle = .warning
alert.messageText = NSLocalizedString("Conflicting shortcut", comment: "")
let informativeText = conflicts.map { "• " + $0 }.joined(separator: "\n")
alert.informativeText = String(format: NSLocalizedString("Vim keys already assigned to other actions:\n%@", comment: ""), informativeText.replacingOccurrences(of: " ", with: "\u{00A0}"))
alert.informativeText = String(format: messageFormat, informativeText.replacingOccurrences(of: " ", with: "\u{00A0}"))
alert.addButton(withTitle: NSLocalizedString("Unassign existing shortcut and continue", comment: "")).setAccessibilityFocused(true)
let cancelButton = alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
cancelButton.keyEquivalent = "\u{1b}"
@@ -1323,11 +1359,23 @@ class ControlsTab {
return Shortcut(code: baseShortcut.keyCode, modifierFlags: [holdShortcut.modifierFlags, baseShortcut.modifierFlags], characters: baseShortcut.characters, charactersIgnoringModifiers: baseShortcut.charactersIgnoringModifiers)
}
private static func applyArrowKeysPreference() {
if Preferences.arrowKeysEnabled {
arrowKeys.forEach { addShortcut(.down, .local, Shortcut(keyEquivalent: $0)!, $0, nil) }
} else {
private static func applyArrowKeysPreferenceWithoutDialogs() {
guard Preferences.arrowKeysEnabled else {
arrowKeys.forEach { removeShortcutIfExists($0) }
return
}
if hasArrowKeysConflictWithoutUi() {
arrowKeys.forEach { removeShortcutIfExists($0) }
Preferences.set("arrowKeysEnabled", "false", false)
return
}
arrowKeys.forEach { addShortcut(.down, .local, Shortcut(keyEquivalent: $0)!, $0, nil) }
}
private static func hasArrowKeysConflictWithoutUi() -> Bool {
return shortcuts.values.contains {
guard arrowKeyCodes.contains($0.shortcut.keyCode) else { return false }
return !arrowKeys.contains($0.id)
}
}