diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj index d63cc2771..2da67b0ef 100644 --- a/Easydict.xcodeproj/project.pbxproj +++ b/Easydict.xcodeproj/project.pbxproj @@ -272,6 +272,7 @@ DC3C643F2B187119008EEDD8 /* ChangeFontSizeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3C643E2B187119008EEDD8 /* ChangeFontSizeView.swift */; }; DC6D9C872B352EBC0055EFFC /* FontSizeHintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6D9C862B352EBC0055EFFC /* FontSizeHintView.swift */; }; DC6D9C892B3969510055EFFC /* Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6D9C882B3969510055EFFC /* Appearance.swift */; }; + EA1013442B5DBDB1005E43F9 /* KeyCombo+Defaults.Serializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1013432B5DBDB1005E43F9 /* KeyCombo+Defaults.Serializable.swift */; }; EA3B81F92B5254AA004C0E8B /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3B81F82B5254AA004C0E8B /* Configuration.swift */; }; EA3B81FC2B52555C004C0E8B /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = EA3B81FB2B52555C004C0E8B /* Defaults */; }; EA9943E32B534C3300EE7B97 /* TTSServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943E22B534C3300EE7B97 /* TTSServiceType.swift */; }; @@ -769,6 +770,7 @@ DC3C643E2B187119008EEDD8 /* ChangeFontSizeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeFontSizeView.swift; sourceTree = ""; }; DC6D9C862B352EBC0055EFFC /* FontSizeHintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSizeHintView.swift; sourceTree = ""; }; DC6D9C882B3969510055EFFC /* Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Appearance.swift; sourceTree = ""; }; + EA1013432B5DBDB1005E43F9 /* KeyCombo+Defaults.Serializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyCombo+Defaults.Serializable.swift"; sourceTree = ""; }; EA3B81F82B5254AA004C0E8B /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; EA9943E22B534C3300EE7B97 /* TTSServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTSServiceType.swift; sourceTree = ""; }; EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageDetectOptimizeExtensions.swift; sourceTree = ""; }; @@ -2256,6 +2258,14 @@ path = ChangeFontSizeView; sourceTree = ""; }; + EA1013412B5DBDA5005E43F9 /* Defaults */ = { + isa = PBXGroup; + children = ( + EA1013432B5DBDB1005E43F9 /* KeyCombo+Defaults.Serializable.swift */, + ); + path = Defaults; + sourceTree = ""; + }; EA3B81F72B52549B004C0E8B /* Configuration */ = { isa = PBXGroup; children = ( @@ -2284,6 +2294,7 @@ EA9943E62B534D7C00EE7B97 /* Extensions */ = { isa = PBXGroup; children = ( + EA1013412B5DBDA5005E43F9 /* Defaults */, EAED41F02B54B1A60005FE0A /* QueryService+ConfigurableService */, EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */, EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */, @@ -2745,6 +2756,7 @@ 03BB2DEB29F57DC000447EDD /* NSImage+EZSymbolmage.m in Sources */, 03B0230629231FA6001C7E63 /* EZLabel.m in Sources */, 03F25CB329327BC200E66A12 /* EZShortcut.m in Sources */, + EA1013442B5DBDB1005E43F9 /* KeyCombo+Defaults.Serializable.swift in Sources */, 033B7134293CE2430096E2DF /* EZWebViewTranslator.m in Sources */, 03CF88632B137F650030C199 /* Array+Convenience.swift in Sources */, 03B0231229231FA6001C7E63 /* NSObject+DarkMode.m in Sources */, diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings index b8a4d9137..5d0d13957 100644 --- a/Easydict/App/Localizable.xcstrings +++ b/Easydict/App/Localizable.xcstrings @@ -3412,6 +3412,9 @@ } } } + }, + "xx" : { + }, "Youdao" : { "extractionState" : "manual", diff --git a/Easydict/NewApp/Configuration/Configuration.swift b/Easydict/NewApp/Configuration/Configuration.swift index dfc6b9b2c..fa301a90b 100644 --- a/Easydict/NewApp/Configuration/Configuration.swift +++ b/Easydict/NewApp/Configuration/Configuration.swift @@ -8,6 +8,7 @@ import Defaults import Foundation +import Magnet // Setting extension Defaults.Keys { @@ -90,9 +91,9 @@ extension Defaults.Keys { /// shortcut extension Defaults.Keys { - static let selectionShortcutKey = Key("EZSelectionShortcutKey_keyHolder", default: Data()) - static let snipShortcutKey = Key("EZSnipShortcutKey_keyHolder", default: Data()) - static let inputShortcutKey = Key("EZInputShortcutKey_keyHolder", default: Data()) - static let screenshotOCRShortcutKey = Key("EZScreenshotOCRShortcutKey_keyHolder", default: Data()) - static let showMiniWindowShortcutKey = Key("EZShowMiniShortcutKey_keyHolder", default: Data()) + static let selectionShortcut = Key("EZSelectionShortcutKey_keyHolder", default: nil) + static let snipShortcut = Key("EZSnipShortcutKey_keyHolder", default: nil) + static let inputShortcut = Key("EZInputShortcutKey_keyHolder", default: nil) + static let screenshotOCRShortcut = Key("EZScreenshotOCRShortcutKey_keyHolder", default: nil) + static let showMiniWindowShortcut = Key("EZShowMiniShortcutKey_keyHolder", default: nil) } diff --git a/Easydict/NewApp/Feature/Shortcut/Shortcut.swift b/Easydict/NewApp/Feature/Shortcut/Shortcut.swift index e693da16f..a85aa407e 100644 --- a/Easydict/NewApp/Feature/Shortcut/Shortcut.swift +++ b/Easydict/NewApp/Feature/Shortcut/Shortcut.swift @@ -49,57 +49,31 @@ class Shortcut: NSObject { // restore shortcut extension Shortcut { func restoreShortcut() { - if let inputTranslateKeyCombo = restoreInputTranslate() { + if let inputTranslateKeyCombo = Defaults[.inputShortcut] { bindingShortCut(keyCombo: inputTranslateKeyCombo, type: .inputTranslate) } - if let snipShortcutKeyKeyCombo = restoreSnipShortcutKey() { + if let snipShortcutKeyKeyCombo = Defaults[.snipShortcut] { bindingShortCut(keyCombo: snipShortcutKeyKeyCombo, type: .snipTranslate) } - if let selectionShortcutKeyCombo = restoreSelectionShortcutKey() { + if let selectionShortcutKeyCombo = Defaults[.selectionShortcut] { bindingShortCut(keyCombo: selectionShortcutKeyCombo, type: .selectTranslate) } - if let screenshotOCRShortcutKeyCombo = restoreScreenshotOCRShortcutKey() { + if let screenshotOCRShortcutKeyCombo = Defaults[.screenshotOCRShortcut] { bindingShortCut(keyCombo: screenshotOCRShortcutKeyCombo, type: .silentScreenshotOcr) } - if let showMiniWindowShortcutKeyCombo = restoreShowMiniWindow() { + if let showMiniWindowShortcutKeyCombo = Defaults[.showMiniWindowShortcut] { bindingShortCut(keyCombo: showMiniWindowShortcutKeyCombo, type: .showMiniWindow) } } - - private func restoreInputTranslate() -> KeyCombo? { - let data = Defaults[.inputShortcutKey] - guard let keyCombo = try? JSONDecoder().decode(KeyCombo.self, from: data) else { return nil } - return keyCombo - } - - private func restoreSnipShortcutKey() -> KeyCombo? { - let data = Defaults[.snipShortcutKey] - guard let keyCombo = try? JSONDecoder().decode(KeyCombo.self, from: data) else { return nil } - return keyCombo - } - - private func restoreSelectionShortcutKey() -> KeyCombo? { - let data = Defaults[.selectionShortcutKey] - guard let keyCombo = try? JSONDecoder().decode(KeyCombo.self, from: data) else { return nil } - return keyCombo - } - - private func restoreScreenshotOCRShortcutKey() -> KeyCombo? { - let data = Defaults[.screenshotOCRShortcutKey] - guard let keyCombo = try? JSONDecoder().decode(KeyCombo.self, from: data) else { return nil } - return keyCombo - } - - private func restoreShowMiniWindow() -> KeyCombo? { - let data = Defaults[.showMiniWindowShortcutKey] - guard let keyCombo = try? JSONDecoder().decode(KeyCombo.self, from: data) else { return nil } - return keyCombo - } } // binding shortcut extension Shortcut { - func bindingShortCut(keyCombo: KeyCombo, type: ShortcutType) { + func bindingShortCut(keyCombo: KeyCombo?, type: ShortcutType) { + guard let keyCombo else { + HotKeyCenter.shared.unregisterHotKey(with: type.rawValue) + return + } var hotKey: HotKey switch type { case .inputTranslate: @@ -160,30 +134,54 @@ extension Shortcut { public func shortcutKeyCombo(_ type: ShortcutType) -> KeyCombo? { switch type { case .inputTranslate: - guard let keyCombo = restoreInputTranslate() else { return nil } + guard let keyCombo = Defaults[.inputShortcut] else { return nil } return keyCombo case .snipTranslate: - guard let keyCombo = restoreSnipShortcutKey() else { return nil } + guard let keyCombo = Defaults[.snipShortcut] else { return nil } return keyCombo case .selectTranslate: - guard let keyCombo = restoreSelectionShortcutKey() else { return nil } + guard let keyCombo = Defaults[.selectionShortcut] else { return nil } return keyCombo case .silentScreenshotOcr: - guard let keyCombo = restoreScreenshotOCRShortcutKey() else { return nil } + guard let keyCombo = Defaults[.screenshotOCRShortcut] else { return nil } return keyCombo case .showMiniWindow: - guard let keyCombo = restoreShowMiniWindow() else { return nil } + guard let keyCombo = Defaults[.showMiniWindowShortcut] else { return nil } return keyCombo } } } -/// can't using keyEquivalent and EventModifiers in SwiftUI MenuItemView direct, because item -/// keyboardShortcut not support double modifier key but can use ⌥ as character -extension View { - public func keyboardShortcut(_ type: ShortcutType) -> some View { - guard let keyCombo = Shortcut.shared.shortcutKeyCombo(type) else { return AnyView(self) } - return AnyView(keyboardShortcut(fetchShortcutKeyEquivalent(keyCombo), modifiers: fetchShortcutKeyEventModifiers(keyCombo))) +struct KeyboardShortcut: ViewModifier { + init(type: ShortcutType) { + let key: Defaults.Key = switch type { + case .inputTranslate: + .inputShortcut + case .snipTranslate: + .snipShortcut + case .selectTranslate: + .selectionShortcut + case .silentScreenshotOcr: + .screenshotOCRShortcut + case .showMiniWindow: + .showMiniWindowShortcut + } + + _shortcut = .init(key) + } + + @Default var shortcut: KeyCombo? + + func body(content: Content) -> some View { + if let shortcut { + content + .keyboardShortcut( + fetchShortcutKeyEquivalent(shortcut), + modifiers: fetchShortcutKeyEventModifiers(shortcut) + ) + } else { + content + } } private func fetchShortcutKeyEquivalent(_ keyCombo: KeyCombo) -> KeyEquivalent { @@ -216,3 +214,12 @@ extension View { return modifiers } } + +/// can't using keyEquivalent and EventModifiers in SwiftUI MenuItemView direct, because item +/// keyboardShortcut not support double modifier key but can use ⌥ as character +public extension View { + @ViewBuilder + func keyboardShortcut(_ type: ShortcutType) -> some View { + modifier(KeyboardShortcut(type: type)) + } +} diff --git a/Easydict/NewApp/Utility/Extensions/Defaults/KeyCombo+Defaults.Serializable.swift b/Easydict/NewApp/Utility/Extensions/Defaults/KeyCombo+Defaults.Serializable.swift new file mode 100644 index 000000000..6858aa387 --- /dev/null +++ b/Easydict/NewApp/Utility/Extensions/Defaults/KeyCombo+Defaults.Serializable.swift @@ -0,0 +1,31 @@ +// +// KeyCombo+Defaults.Serializable.swift +// Easydict +// +// Created by 戴藏龙 on 2024/1/21. +// Copyright © 2024 izual. All rights reserved. +// + +import Defaults +import Foundation +import Magnet + +extension KeyCombo: Defaults.Serializable { + public static var bridge = ShortcutBridge() + + public struct ShortcutBridge: Defaults.Bridge { + public func serialize(_ value: Magnet.KeyCombo??) -> Data? { + guard let value else { return nil } + return try? JSONEncoder().encode(value) + } + + public func deserialize(_ object: Data?) -> Magnet.KeyCombo?? { + guard let data = object else { return nil } + return try? JSONDecoder().decode(KeyCombo.self, from: data) as Magnet.KeyCombo? + } + + public typealias Value = KeyCombo? + + public typealias Serializable = Data + } +} diff --git a/Easydict/NewApp/View/SettingView/Tabs/View/Genearl/GeneralKeyHolderWrapper.swift b/Easydict/NewApp/View/SettingView/Tabs/View/Genearl/GeneralKeyHolderWrapper.swift index 9774d9e4f..8cdf368ee 100644 --- a/Easydict/NewApp/View/SettingView/Tabs/View/Genearl/GeneralKeyHolderWrapper.swift +++ b/Easydict/NewApp/View/SettingView/Tabs/View/Genearl/GeneralKeyHolderWrapper.swift @@ -56,43 +56,40 @@ extension GeneralKeyHolderWrapper { func recordView(_: RecordView, didChangeKeyCombo keyCombo: KeyCombo?) { storeKeyCombo(with: keyCombo) - guard let keyCombo else { return } Shortcut.shared.bindingShortCut(keyCombo: keyCombo, type: type) } func restoreKeyCombo(_ recordView: RecordView) { - var data: Data + var keyCombo: KeyCombo? switch type { case .inputTranslate: - data = Defaults[.inputShortcutKey] + keyCombo = Defaults[.inputShortcut] case .snipTranslate: - data = Defaults[.snipShortcutKey] + keyCombo = Defaults[.snipShortcut] case .selectTranslate: - data = Defaults[.selectionShortcutKey] + keyCombo = Defaults[.selectionShortcut] case .silentScreenshotOcr: - data = Defaults[.screenshotOCRShortcutKey] + keyCombo = Defaults[.screenshotOCRShortcut] case .showMiniWindow: - data = Defaults[.showMiniWindowShortcutKey] + keyCombo = Defaults[.showMiniWindowShortcut] } - guard let keyCombo = try? JSONDecoder().decode(KeyCombo.self, from: data) else { return } recordView.keyCombo = keyCombo Shortcut.shared.bindingShortCut(keyCombo: keyCombo, type: type) } // shortcut func storeKeyCombo(with keyCombo: KeyCombo?) { - let data = try? JSONEncoder().encode(keyCombo) switch type { case .inputTranslate: - Defaults[.inputShortcutKey] = data ?? Data() + Defaults[.inputShortcut] = keyCombo case .snipTranslate: - Defaults[.snipShortcutKey] = data ?? Data() + Defaults[.snipShortcut] = keyCombo case .selectTranslate: - Defaults[.selectionShortcutKey] = data ?? Data() + Defaults[.selectionShortcut] = keyCombo case .silentScreenshotOcr: - Defaults[.screenshotOCRShortcutKey] = data ?? Data() + Defaults[.screenshotOCRShortcut] = keyCombo case .showMiniWindow: - Defaults[.showMiniWindowShortcutKey] = data ?? Data() + Defaults[.showMiniWindowShortcut] = keyCombo } } }