Skip to content
Open
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
29 changes: 22 additions & 7 deletions Sources/KeyboardShortcuts/Recorder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ extension KeyboardShortcuts {

let name: Name
let onChange: ((_ shortcut: Shortcut?) -> Void)?
let onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)?

func makeNSView(context: Context) -> NSViewType {
.init(for: name, onChange: onChange)
.init(for: name, onChange: onChange, onError: onError)
}

func updateNSView(_ nsView: NSViewType, context: Context) {
Expand Down Expand Up @@ -44,17 +45,20 @@ extension KeyboardShortcuts {
public struct Recorder<Label: View>: View { // swiftlint:disable:this type_name
private let name: Name
private let onChange: ((Shortcut?) -> Void)?
private let onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)?
private let hasLabel: Bool
private let label: Label

init(
for name: Name,
onChange: ((Shortcut?) -> Void)? = nil,
onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)? = nil,
hasLabel: Bool,
@ViewBuilder label: () -> Label
) {
self.name = name
self.onChange = onChange
self.onError = onError
self.hasLabel = hasLabel
self.label = label()
}
Expand All @@ -65,15 +69,17 @@ extension KeyboardShortcuts {
LabeledContent {
_Recorder(
name: name,
onChange: onChange
onChange: onChange,
onError: onError
)
} label: {
label
}
} else {
_Recorder(
name: name,
onChange: onChange
onChange: onChange,
onError: onError
)
.formLabel {
label
Expand All @@ -82,7 +88,8 @@ extension KeyboardShortcuts {
} else {
_Recorder(
name: name,
onChange: onChange
onChange: onChange,
onError: onError
)
}
}
Expand All @@ -96,11 +103,13 @@ extension KeyboardShortcuts.Recorder<EmptyView> {
*/
public init(
for name: KeyboardShortcuts.Name,
onChange: ((KeyboardShortcuts.Shortcut?) -> Void)? = nil
onChange: ((KeyboardShortcuts.Shortcut?) -> Void)? = nil,
onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)? = nil
) {
self.init(
for: name,
onChange: onChange,
onError: onError,
hasLabel: false
) {}
}
Expand All @@ -115,11 +124,13 @@ extension KeyboardShortcuts.Recorder<Text> {
public init(
_ title: LocalizedStringKey,
name: KeyboardShortcuts.Name,
onChange: ((KeyboardShortcuts.Shortcut?) -> Void)? = nil
onChange: ((KeyboardShortcuts.Shortcut?) -> Void)? = nil,
onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)? = nil
) {
self.init(
for: name,
onChange: onChange,
onError: onError,
hasLabel: true
) {
Text(title)
Expand All @@ -137,11 +148,13 @@ extension KeyboardShortcuts.Recorder<Text> {
public init(
_ title: String,
name: KeyboardShortcuts.Name,
onChange: ((KeyboardShortcuts.Shortcut?) -> Void)? = nil
onChange: ((KeyboardShortcuts.Shortcut?) -> Void)? = nil,
onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)? = nil
) {
self.init(
for: name,
onChange: onChange,
onError: onError,
hasLabel: true
) {
Text(title)
Expand All @@ -158,11 +171,13 @@ extension KeyboardShortcuts.Recorder {
public init(
for name: KeyboardShortcuts.Name,
onChange: ((KeyboardShortcuts.Shortcut?) -> Void)? = nil,
onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)? = nil,
@ViewBuilder label: () -> Label
) {
self.init(
for: name,
onChange: onChange,
onError: onError,
hasLabel: true,
label: label
)
Expand Down
111 changes: 74 additions & 37 deletions Sources/KeyboardShortcuts/RecorderCocoa.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
public final class RecorderCocoa: NSSearchField, NSSearchFieldDelegate {
private let minimumWidth = 130.0
private let onChange: ((_ shortcut: Shortcut?) -> Void)?
private let onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)?
private var canBecomeKey = false
private var eventMonitor: LocalEventMonitor?
private var shortcutsNameChangeObserver: NSObjectProtocol?
Expand Down Expand Up @@ -83,10 +84,12 @@
*/
public required init(
for name: Name,
onChange: ((_ shortcut: Shortcut?) -> Void)? = nil
onChange: ((_ shortcut: Shortcut?) -> Void)? = nil,
onError: ((_ title: String, _ message: String?, _ buttonOk: String?, _ buttonForce: String?) -> Bool)? = nil
) {
self.shortcutName = name
self.onChange = onChange
self.onError = onError

super.init(frame: .zero)
self.delegate = self
Expand Down Expand Up @@ -273,51 +276,85 @@
}

if let menuItem = shortcut.takenByMainMenu {
// TODO: Find a better way to make it possible to dismiss the alert by pressing "Enter". How can we make the input automatically temporarily lose focus while the alert is open?
blur()

NSAlert.showModal(
for: window,
title: String.localizedStringWithFormat("keyboard_shortcut_used_by_menu_item".localized, menuItem.title)
)

focus()

return nil
if let onError {
if !onError(
String.localizedStringWithFormat("keyboard_shortcut_used_by_menu_item".localized, menuItem.title),
nil,
nil,
nil
) {
return nil
}
} else {
// TODO: Find a better way to make it possible to dismiss the alert by pressing "Enter". How can we make the input automatically temporarily lose focus while the alert is open?
blur()

Check warning on line 291 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
NSAlert.showModal(
for: window,
title: String.localizedStringWithFormat("keyboard_shortcut_used_by_menu_item".localized, menuItem.title)
)

Check warning on line 296 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
focus()

Check warning on line 298 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
return nil
}
}

// See: https://developer.apple.com/forums/thread/763878?answerId=804374022#804374022
if shortcut.isDisallowed {
blur()

NSAlert.showModal(
for: window,
title: "keyboard_shortcut_disallowed".localized
)

focus()
return nil
if let onError {
if !onError(
"keyboard_shortcut_disallowed".localized,
nil,
nil,
nil
) {
return nil
}
} else {
blur()

Check warning on line 316 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
NSAlert.showModal(
for: window,
title: "keyboard_shortcut_disallowed".localized
)

Check warning on line 321 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
focus()

Check warning on line 323 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
return nil
}
}

if shortcut.isTakenBySystem {
blur()

let modalResponse = NSAlert.showModal(
for: window,
title: "keyboard_shortcut_used_by_system".localized,
// TODO: Add button to offer to open the relevant system settings pane for the user.
message: "keyboard_shortcuts_can_be_changed".localized,
buttonTitles: [
if let onError {
if !onError(
"keyboard_shortcut_used_by_system".localized,
"keyboard_shortcuts_can_be_changed".localized,
"ok".localized,
"force_use_shortcut".localized
]
)

focus()

// If the user has selected "Use Anyway" in the dialog (the second option), we'll continue setting the keyboard shorcut even though it's reserved by the system.
guard modalResponse == .alertSecondButtonReturn else {
return nil
) {
return nil
}
} else {
blur()

Check warning on line 340 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
let modalResponse = NSAlert.showModal(
for: window,
title: "keyboard_shortcut_used_by_system".localized,
// TODO: Add button to offer to open the relevant system settings pane for the user.
message: "keyboard_shortcuts_can_be_changed".localized,
buttonTitles: [
"ok".localized,
"force_use_shortcut".localized
]
)

Check warning on line 351 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
focus()

Check warning on line 353 in Sources/KeyboardShortcuts/RecorderCocoa.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
// If the user has selected "Use Anyway" in the dialog (the second option), we'll continue setting the keyboard shorcut even though it's reserved by the system.
guard modalResponse == .alertSecondButtonReturn else {
return nil
}
}
}

Expand Down