diff --git a/Source/3rdParty/VDKComboBox/VDKComboBox.swift b/Source/3rdParty/VDKComboBox/VDKComboBox.swift index 6a4690a7..d42ef3a0 100644 --- a/Source/3rdParty/VDKComboBox/VDKComboBox.swift +++ b/Source/3rdParty/VDKComboBox/VDKComboBox.swift @@ -8,7 +8,7 @@ import SwiftUI // MARK: - NSComboBox // Ref: https://stackoverflow.com/a/71058587/4162914 -@available(macOS 11.0, *) +@available(macOS 10.15, *) struct ComboBox: NSViewRepresentable { // The items that will show up in the pop-up menu: var items: [String] diff --git a/Source/Data b/Source/Data index 1d1224ee..1daf4d57 160000 --- a/Source/Data +++ b/Source/Data @@ -1 +1 @@ -Subproject commit 1d1224ee392d757526dc8aab6550e9d41dd3d784 +Subproject commit 1daf4d576a248373889889213bea4bd3cbb4b5d4 diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index d362d9c9..984a7948 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -372,7 +372,6 @@ extension KeyHandler { // MARK: 摁住 Shift+字母鍵 的處理 (Shift+Letter Processing) - // 這裡不使用小麥注音 2.2 版的組字區處理方式,而是直接由詞庫負責。 if input.isUpperCaseASCIILetterKey, !input.isCommandHold, !input.isControlHold { if input.isShiftHold { // 這裡先不要判斷 isOptionHold。 switch mgrPrefs.upperCaseLetterKeyBehavior { @@ -388,7 +387,8 @@ extension KeyHandler { return true default: // 包括 case 0,直接塞給組字區。 let letter: String! = String( - format: "%@%c", "_letter_", charCode.isPrintableASCII ? CChar(charCode) : inputText) + format: "%@%c", "_letter_", charCode.isPrintableASCII ? CChar(charCode) : inputText + ) if handlePunctuation( letter, state: state, diff --git a/Source/Modules/ControllerModules/SyllableComposer.swift b/Source/Modules/ControllerModules/SyllableComposer.swift index a9b5d26e..5e8fa2df 100644 --- a/Source/Modules/ControllerModules/SyllableComposer.swift +++ b/Source/Modules/ControllerModules/SyllableComposer.swift @@ -922,7 +922,8 @@ public struct Tekkon { /// - newToneOne: 對陰平指定新的標記。預設情況下該標記為空字串。 /// - Returns: 轉換結果。 static func cnvHanyuPinyinToPhona(target: String, newToneOne: String = "") -> String { - if target.contains("_") { return target } + /// 如果當前內容有任何除了半形英數內容以外的內容的話,就直接放棄轉換。 + if target.contains("_") || !target.isNotPureAlphanumerical { return target } var result = target for key in Tekkon.mapHanyuPinyin.keys.sorted(by: { $0.count > $1.count }) { guard let value = Tekkon.mapHanyuPinyin[key] else { continue } @@ -1446,3 +1447,12 @@ public struct Tekkon { "s": "ㄙ", "t": "ㄊ", "u": "ㄡ", "v": "ㄩ", "w": "ㄨ", "x": "ㄒ", "y": "ㄧ", "z": "ㄗ", " ": " ", ] } + +/// 檢測字串是否包含半形英數內容 +extension String { + fileprivate var isNotPureAlphanumerical: Bool { + let regex = ".*[^A-Za-z0-9].*" + let testString = NSPredicate(format: "SELF MATCHES %@", regex) + return testString.evaluate(with: self) + } +} diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift index e9d60314..02c59768 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift @@ -187,20 +187,22 @@ class ctlInputMethod: IMKInputController { @objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { _ = sender // 防止格式整理工具毀掉與此對應的參數。 - // 用 Shift 開關半形英數模式。 - if ShiftKeyUpChecker.check(event) { - if !rencentKeyHandledByKeyHandler { - NotifierController.notify( - message: String( - format: "%@%@%@", NSLocalizedString("Alphanumerical Mode", comment: ""), "\n", - toggleASCIIMode() - ? NSLocalizedString("NotificationSwitchON", comment: "") - : NSLocalizedString("NotificationSwitchOFF", comment: "") + // 用 Shift 開關半形英數模式,僅對 macOS 10.15 及之後的 macOS 有效。 + if #available(macOS 10.15, *) { + if ShiftKeyUpChecker.check(event) { + if !rencentKeyHandledByKeyHandler { + NotifierController.notify( + message: String( + format: "%@%@%@", NSLocalizedString("Alphanumerical Mode", comment: ""), "\n", + toggleASCIIMode() + ? NSLocalizedString("NotificationSwitchON", comment: "") + : NSLocalizedString("NotificationSwitchOFF", comment: "") + ) ) - ) + } + rencentKeyHandledByKeyHandler = false + return false } - rencentKeyHandledByKeyHandler = false - return false } /// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。 diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift index cc8782f5..4ec14a69 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift @@ -83,8 +83,23 @@ extension ctlInputMethod { return NSFont.systemFont(ofSize: size) } + /// FB10978412: Since macOS 11 Big Sur, CTFontCreateUIFontForLanguage cannot + /// distinguish zh-Hans and zh-Hant with correct adoptation of proper PingFang SC/TC variants. + /// + /// Instructions for Apple Developer relations to reveal this bug: + /// + /// 1) Remove the usage of ".languageIdentifier" from ctlCandidateUniversal.swift (already done). + /// 2) Run "make update" in the project folder to download the latest git-submodule of dictionary file. + /// 3) Compile the target "vChewingInstaller", run it. It will install the input method into + /// "~/Library/Input Methods/" folder. Remember to ENABLE BOTH "vChewing-CHS" + /// and "vChewing-CHT" input sources in System Preferences / Settings. + /// 4) Type Zhuyin "ej3" (ㄍㄨˇ) (or "gu3" in Pinyin if you enabled Pinyin typing in vChewing preferences.) + /// using both "vChewing-CHS" and "vChewing-CHT", and check the candidate window by pressing SPACE key. + /// 5) Do NOT enable either KangXi conversion mode nor JIS conversion mode. They are disabled by default. + /// 6) Expecting the glyph differences of the candidate "骨" between PingFang SC and PingFang TC when rendering + /// the candidate window in different "vChewing-CHS" and "vChewing-CHT" input modes. func candidateFont(name: String?, size: CGFloat) -> NSFont { - var finalReturnFont: NSFont = + let finalReturnFont: NSFont = { switch IME.currentInputMode { case InputMode.imeModeCHS: @@ -98,9 +113,6 @@ extension ctlInputMethod { } }() ?? NSFont.systemFont(ofSize: size) - // 上述方法對 macOS 10.11-10.15 有效,但對 macOS 12 Monterey 無效(懷疑是 Bug)。 - // macOS 12 Monterey 開始就用系統內建的函式來處理,相關流程直接寫在 ctlCandidateUniversal 內。 - if #available(macOS 12.0, *) { finalReturnFont = NSFont.systemFont(ofSize: size) } if let name = name { return NSFont(name: name, size: size) ?? finalReturnFont } diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift b/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift index 03971b48..66ba5629 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift @@ -185,7 +185,7 @@ extension ctlInputMethod { // MARK: - IME Menu Items @objc override func showPreferences(_: Any?) { - if #available(macOS 11.0, *) { + if #available(macOS 10.15, *) { NSApp.setActivationPolicy(.accessory) ctlPrefUI.shared.controller.show(preferencePane: Preferences.PaneIdentifier(rawValue: "General")) ctlPrefUI.shared.controller.window?.level = .floating diff --git a/Source/Modules/IMEModules/IME.swift b/Source/Modules/IMEModules/IME.swift index 3cc7405e..24e52104 100644 --- a/Source/Modules/IMEModules/IME.swift +++ b/Source/Modules/IMEModules/IME.swift @@ -77,8 +77,15 @@ public enum IME { // mgrLangModel 的 loadUserPhrases 等函式在自動讀取 dataFolderPath 時, // 如果發現自訂目錄不可用,則會自動抹去自訂目錄設定、改採預設目錄。 // 所以這裡不需要特別處理。 - mgrLangModel.loadUserAssociatesData() - mgrLangModel.loadUserPhraseReplacement() + if mgrPrefs.associatedPhrasesEnabled { + mgrLangModel.loadUserAssociatesData() + } + if mgrPrefs.phraseReplacementEnabled { + mgrLangModel.loadUserPhraseReplacement() + } + if mgrPrefs.useSCPCTypingMode { + mgrLangModel.loadUserSCPCSequencesData() + } mgrLangModel.loadUserPhrasesData() if !userOnly { // mgrLangModel.loadDataModels() diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index 4b1e4232..c766ad1a 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -23,7 +23,7 @@ public enum UserDef: String, CaseIterable { case kCandidateListTextSize = "CandidateListTextSize" case kAppleLanguages = "AppleLanguages" case kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles" - case kUseRearCursorMode = "useRearCursorMode" + case kUseRearCursorMode = "UseRearCursorMode" case kUseHorizontalCandidateList = "UseHorizontalCandidateList" case kChooseCandidateUsingSpace = "ChooseCandidateUsingSpace" case kCNS11643Enabled = "CNS11643Enabled" @@ -388,15 +388,6 @@ public enum mgrPrefs { mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2 } - @UserDefault(key: UserDef.kUseSCPCTypingMode.rawValue, defaultValue: false) - static var useSCPCTypingMode: Bool - - static func toggleSCPCTypingModeEnabled() -> Bool { - useSCPCTypingMode = !useSCPCTypingMode - UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode.rawValue) - return useSCPCTypingMode - } - @UserDefault(key: UserDef.kMaxCandidateLength.rawValue, defaultValue: 10) static var maxCandidateLength: Int @@ -564,8 +555,29 @@ public enum mgrPrefs { } } + @UserDefault(key: UserDef.kUseSCPCTypingMode.rawValue, defaultValue: false) + static var useSCPCTypingMode: Bool { + willSet { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { + mgrLangModel.loadUserSCPCSequencesData() + } + } + } + + static func toggleSCPCTypingModeEnabled() -> Bool { + useSCPCTypingMode = !useSCPCTypingMode + UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode.rawValue) + return useSCPCTypingMode + } + @UserDefault(key: UserDef.kPhraseReplacementEnabled.rawValue, defaultValue: false) - static var phraseReplacementEnabled: Bool + static var phraseReplacementEnabled: Bool { + willSet { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { + mgrLangModel.loadUserPhraseReplacement() + } + } + } static func togglePhraseReplacementEnabled() -> Bool { phraseReplacementEnabled = !phraseReplacementEnabled @@ -575,7 +587,13 @@ public enum mgrPrefs { } @UserDefault(key: UserDef.kAssociatedPhrasesEnabled.rawValue, defaultValue: false) - static var associatedPhrasesEnabled: Bool + static var associatedPhrasesEnabled: Bool { + willSet { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { + mgrLangModel.loadUserAssociatesData() + } + } + } static func toggleAssociatedPhrasesEnabled() -> Bool { associatedPhrasesEnabled = !associatedPhrasesEnabled diff --git a/Source/Modules/LangModelRelated/LMInstantiator.swift b/Source/Modules/LangModelRelated/LMInstantiator.swift index e0c3c681..9237f4fc 100644 --- a/Source/Modules/LangModelRelated/LMInstantiator.swift +++ b/Source/Modules/LangModelRelated/LMInstantiator.swift @@ -76,6 +76,7 @@ extension vChewing { ) var lmReplacements = LMReplacments() var lmAssociates = LMAssociates() + var lmPlainBopomofo = LMPlainBopomofo() // MARK: - 工具函式 @@ -166,6 +167,16 @@ extension vChewing { } } + public func loadUserSCPCSequencesData(path: String) { + if FileManager.default.isReadableFile(atPath: path) { + lmPlainBopomofo.close() + lmPlainBopomofo.open(path) + IME.prtDebugIntel("lmPlainBopomofo: \(lmPlainBopomofo.count) entries of data loaded from: \(path)") + } else { + IME.prtDebugIntel("lmPlainBopomofo: File access failure: \(path)") + } + } + // MARK: - 核心函式(對外) /// 威注音輸入法目前尚未具備對雙元圖的處理能力,故停用該函式。 @@ -181,6 +192,11 @@ extension vChewing { /// 準備不同的語言模組容器,開始逐漸往容器陣列內塞入資料。 var rawAllUnigrams: [Megrez.Unigram] = [] + // 如果有檢測到使用者自訂逐字選字語料庫內的相關資料的話,在這裡先插入。 + if mgrPrefs.useSCPCTypingMode { + rawAllUnigrams += lmPlainBopomofo.valuesFor(key: key).map { Megrez.Unigram(value: $0, score: 0) } + } + // 用 reversed 指令讓使用者語彙檔案內的詞條優先順序隨著行數增加而逐漸增高。 // 這樣一來就可以在就地新增語彙時徹底複寫優先權。 // 將兩句差分也是為了讓 rawUserUnigrams 的類型不受可能的影響。 diff --git a/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift b/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift index be170656..8dfc1ce6 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift @@ -32,7 +32,7 @@ extension vChewing { } let arrTarget = target.dropLast().dropFirst().split(separator: ",") guard arrTarget.count == 2 else { return target } - return "(\(Tekkon.cnvHanyuPinyinToPhona(target: String(arrTarget[0]))),\(arrTarget[1]))" + return "(\(Tekkon.cnvHanyuPinyinToPhona(target: String(arrTarget[0]).lowercased())),\(arrTarget[1]))" } @discardableResult public mutating func open(_ path: String) -> Bool { diff --git a/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift b/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift index 5879eb95..12fe3627 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift @@ -77,7 +77,7 @@ extension vChewing { if !neta[0].isEmpty, !neta[1].isEmpty { let theKey = shouldReverse ? String(neta[1]) : String(neta[0]) let theValue = $0 - rangeMap[Tekkon.cnvHanyuPinyinToPhona(target: theKey), default: []].append(theValue) + rangeMap[Tekkon.cnvHanyuPinyinToPhona(target: theKey.lowercased()), default: []].append(theValue) } } } diff --git a/Source/Modules/LangModelRelated/SubLMs/lmPlainBopomofo.swift b/Source/Modules/LangModelRelated/SubLMs/lmPlainBopomofo.swift new file mode 100644 index 00000000..6a689e5c --- /dev/null +++ b/Source/Modules/LangModelRelated/SubLMs/lmPlainBopomofo.swift @@ -0,0 +1,83 @@ +// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// StringView Ranges extension by (c) 2022 and onwards Isaac Xen (MIT License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import Foundation + +extension vChewing { + @frozen public struct LMPlainBopomofo { + var rangeMap: [String: String] = [:] + + public var count: Int { + rangeMap.count + } + + public init() { + rangeMap = [:] + } + + public func isLoaded() -> Bool { + !rangeMap.isEmpty + } + + @discardableResult public mutating func open(_ path: String) -> Bool { + if isLoaded() { + return false + } + + do { + let rawData = try Data(contentsOf: URL(fileURLWithPath: path)) + let rawPlist: [String: String] = + try PropertyListSerialization.propertyList(from: rawData, format: nil) as? [String: String] ?? .init() + rangeMap = rawPlist + } catch { + IME.prtDebugIntel("\(error)") + IME.prtDebugIntel("↑ Exception happened when reading data at: \(path).") + return false + } + + return true + } + + public mutating func close() { + if isLoaded() { + rangeMap.removeAll() + } + } + + public func dump() { + // We remove this function in order to reduce out maintenance workload. + // This function will be implemented only if further hard-necessity comes. + } + + public func valuesFor(key: String) -> [String] { + var pairs: [String] = [] + if let arrRangeRecords: String = rangeMap[key] { + pairs.append(contentsOf: arrRangeRecords.map { String($0) }) + } + var set = Set() + return pairs.filter { set.insert($0).inserted } + } + + public func hasValuesFor(key: String) -> Bool { rangeMap.keys.contains(key) } + } +} + +// MARK: - StringView Ranges Extension (by Isaac Xen) + +extension String { + fileprivate func ranges(splitBy separator: Element) -> [Range] { + var startIndex = startIndex + return split(separator: separator).reduce(into: []) { ranges, substring in + _ = range(of: substring, range: startIndex.. URL { + let fileName = (mode == InputMode.imeModeCHT) ? "data-plain-bpmf-cht.plist" : "data-plain-bpmf-chs.plist" + return URL(fileURLWithPath: dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName) + } + /// 使用者波浪符號選單資料路徑。 /// - Returns: 資料路徑(URL)。 static func userSymbolNodeDataURL() -> URL { @@ -311,6 +328,7 @@ enum mgrLangModel { userAssociatesDataURL(mode), populateWithTemplate: mode == .imeModeCHS ? kTemplateNameUserAssociatesCHS : kTemplateNameUserAssociatesCHT ) + || !ensureFileExists(userSCPCSequencesURL(mode)) || !ensureFileExists(userFilteredDataURL(mode), populateWithTemplate: kTemplateNameUserExclusions) || !ensureFileExists(userReplacementsDataURL(mode), populateWithTemplate: kTemplateNameUserReplacements) || !ensureFileExists(userSymbolDataURL(mode), populateWithTemplate: kTemplateNameUserSymbolPhrases) diff --git a/Source/Modules/LanguageParsers/Megrez/4_Span.swift b/Source/Modules/LanguageParsers/Megrez/4_Span.swift index 9d7efb30..73209391 100644 --- a/Source/Modules/LanguageParsers/Megrez/4_Span.swift +++ b/Source/Modules/LanguageParsers/Megrez/4_Span.swift @@ -79,12 +79,8 @@ extension Megrez.Compositor { // 再獲取以當前位置結尾或開頭的節點。 let begin: Int = location - min(location, Megrez.Compositor.maxSpanLength - 1) for theLocation in begin.. Void) -> Binding { + Binding( + get: { + wrappedValue + }, + set: { newValue in + wrappedValue = newValue + action() + } + ) + } +} + +// MARK: - Add ".tooltip" support. + +// Ref: https://stackoverflow.com/a/63217861 + +@available(macOS 10.15, *) +struct Tooltip: NSViewRepresentable { + let tooltip: String + + func makeNSView(context _: NSViewRepresentableContext) -> NSView { + let view = NSView() + view.toolTip = tooltip + + return view + } + + func updateNSView(_: NSView, context _: NSViewRepresentableContext) {} +} + +@available(macOS 10.15, *) +extension View { + public func toolTip(_ tooltip: String) -> some View { + overlay(Tooltip(tooltip: tooltip)) + } +} diff --git a/Source/UI/PrefUI/suiPrefPaneDictionary.swift b/Source/UI/PrefUI/suiPrefPaneDictionary.swift index 23a5203d..4ea4932a 100644 --- a/Source/UI/PrefUI/suiPrefPaneDictionary.swift +++ b/Source/UI/PrefUI/suiPrefPaneDictionary.swift @@ -8,7 +8,7 @@ import SwiftUI -@available(macOS 11.0, *) +@available(macOS 10.15, *) struct suiPrefPaneDictionary: View { private var fdrDefault = mgrLangModel.dataFolderPath(isDefaultFolder: true) @State private var tbxUserDataPathSpecified: String = @@ -43,8 +43,13 @@ struct suiPrefPaneDictionary: View { Preferences.Section(title: "", bottomDivider: true) { Text(LocalizedStringKey("Choose your desired user data folder path. Will be omitted if invalid.")) HStack { - TextField(fdrDefault, text: $tbxUserDataPathSpecified).disabled(true) - .help(tbxUserDataPathSpecified) + if #available(macOS 11.0, *) { + TextField(fdrDefault, text: $tbxUserDataPathSpecified).disabled(true) + .help(tbxUserDataPathSpecified) + } else { + TextField(fdrDefault, text: $tbxUserDataPathSpecified).disabled(true) + .toolTip(tbxUserDataPathSpecified) + } Button { IME.dlgOpenPath.title = NSLocalizedString( "Choose your desired user data folder.", comment: "" @@ -98,48 +103,43 @@ struct suiPrefPaneDictionary: View { } Toggle( LocalizedStringKey("Automatically reload user data files if changes detected"), - isOn: $selAutoReloadUserData - ).controlSize(.small).onChange(of: selAutoReloadUserData) { value in - mgrPrefs.shouldAutoReloadUserDataFiles = value - } + isOn: $selAutoReloadUserData.onChange { + mgrPrefs.shouldAutoReloadUserDataFiles = selAutoReloadUserData + } + ).controlSize(.small) Divider() Toggle( LocalizedStringKey("Enable CNS11643 Support (2022-07-20)"), - isOn: $selEnableCNS11643 + isOn: $selEnableCNS11643.onChange { + mgrPrefs.cns11643Enabled = selEnableCNS11643 + mgrLangModel.setCNSEnabled(mgrPrefs.cns11643Enabled) + } ) - .onChange(of: selEnableCNS11643) { value in - mgrPrefs.cns11643Enabled = value - mgrLangModel.setCNSEnabled(value) - } Toggle( LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"), - isOn: $selEnableSymbolInputSupport + isOn: $selEnableSymbolInputSupport.onChange { + mgrPrefs.symbolInputEnabled = selEnableSymbolInputSupport + mgrLangModel.setSymbolEnabled(mgrPrefs.symbolInputEnabled) + } ) - .onChange(of: selEnableSymbolInputSupport) { value in - mgrPrefs.symbolInputEnabled = value - mgrLangModel.setSymbolEnabled(value) - } Toggle( LocalizedStringKey("Allow boosting / excluding a candidate of single kanji"), - isOn: $selAllowBoostingSingleKanjiAsUserPhrase + isOn: $selAllowBoostingSingleKanjiAsUserPhrase.onChange { + mgrPrefs.allowBoostingSingleKanjiAsUserPhrase = selAllowBoostingSingleKanjiAsUserPhrase + } ) - .onChange(of: selAllowBoostingSingleKanjiAsUserPhrase) { value in - mgrPrefs.allowBoostingSingleKanjiAsUserPhrase = value - } Toggle( LocalizedStringKey("Applying typing suggestions from half-life user override model"), - isOn: $selFetchSuggestionsFromUserOverrideModel + isOn: $selFetchSuggestionsFromUserOverrideModel.onChange { + mgrPrefs.fetchSuggestionsFromUserOverrideModel = selFetchSuggestionsFromUserOverrideModel + } ) - .onChange(of: selFetchSuggestionsFromUserOverrideModel) { value in - mgrPrefs.fetchSuggestionsFromUserOverrideModel = value - } Toggle( LocalizedStringKey("Always use fixed listing order in candidate window"), - isOn: $selUseFixecCandidateOrderOnSelection + isOn: $selUseFixecCandidateOrderOnSelection.onChange { + mgrPrefs.useFixecCandidateOrderOnSelection = selUseFixecCandidateOrderOnSelection + } ) - .onChange(of: selUseFixecCandidateOrderOnSelection) { value in - mgrPrefs.useFixecCandidateOrderOnSelection = value - } } } } diff --git a/Source/UI/PrefUI/suiPrefPaneExperience.swift b/Source/UI/PrefUI/suiPrefPaneExperience.swift index a95c7b57..c63d522f 100644 --- a/Source/UI/PrefUI/suiPrefPaneExperience.swift +++ b/Source/UI/PrefUI/suiPrefPaneExperience.swift @@ -9,11 +9,8 @@ import Cocoa import SwiftUI -@available(macOS 11.0, *) +@available(macOS 10.15, *) struct suiPrefPaneExperience: View { - @State private var selSelectionKeysList = mgrPrefs.suggestedCandidateKeys - @State private var selSelectionKeys = - (UserDefaults.standard.string(forKey: UserDef.kCandidateKeys.rawValue) ?? mgrPrefs.defaultCandidateKeys) as String @State private var selCursorPosition = UserDefaults.standard.bool( forKey: UserDef.kUseRearCursorMode.rawValue) ? 1 : 0 @@ -54,40 +51,15 @@ struct suiPrefPaneExperience: View { var body: some View { Preferences.Container(contentWidth: contentWidth) { - Preferences.Section(label: { Text(LocalizedStringKey("Selection Keys:")) }) { - ComboBox(items: mgrPrefs.suggestedCandidateKeys, text: $selSelectionKeys).frame(width: 180).onChange( - of: selSelectionKeys - ) { value in - let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicate - do { - try mgrPrefs.validate(candidateKeys: keys) - mgrPrefs.candidateKeys = keys - selSelectionKeys = mgrPrefs.candidateKeys - } catch mgrPrefs.CandidateKeyError.empty { - selSelectionKeys = mgrPrefs.candidateKeys - } catch { - if let window = ctlPrefUI.shared.controller.window { - let alert = NSAlert(error: error) - alert.beginSheetModal(for: window) { _ in - selSelectionKeys = mgrPrefs.candidateKeys - } - clsSFX.beep() - } - } - } - Text( - LocalizedStringKey( - "Choose or hit Enter to confim your prefered keys for selecting candidates." - ) - ) - .preferenceDescription() - } Preferences.Section(label: { Text(LocalizedStringKey("Cursor Selection:")) }) { - Picker("", selection: $selCursorPosition) { + Picker( + "", + selection: $selCursorPosition.onChange { + mgrPrefs.useRearCursorMode = (selCursorPosition == 1) ? true : false + } + ) { Text(LocalizedStringKey("in front of the phrase (like macOS built-in Zhuyin IME)")).tag(0) Text(LocalizedStringKey("at the rear of the phrase (like Microsoft New Phonetic)")).tag(1) - }.onChange(of: selCursorPosition) { value in - mgrPrefs.useRearCursorMode = (value == 1) ? true : false } .labelsHidden() .pickerStyle(RadioGroupPickerStyle()) @@ -95,17 +67,20 @@ struct suiPrefPaneExperience: View { .preferenceDescription() Toggle( LocalizedStringKey("Push the cursor in front of the phrase after selection"), - isOn: $selPushCursorAfterSelection - ).onChange(of: selPushCursorAfterSelection) { value in - mgrPrefs.moveCursorAfterSelectingCandidate = value - }.controlSize(.small) + isOn: $selPushCursorAfterSelection.onChange { + mgrPrefs.moveCursorAfterSelectingCandidate = selPushCursorAfterSelection + } + ).controlSize(.small) } Preferences.Section(title: "(Shift+)Tab:") { - Picker("", selection: $selKeyBehaviorShiftTab) { + Picker( + "", + selection: $selKeyBehaviorShiftTab.onChange { + mgrPrefs.specifyShiftTabKeyBehavior = (selKeyBehaviorShiftTab == 1) ? true : false + } + ) { Text(LocalizedStringKey("for cycling candidates")).tag(0) Text(LocalizedStringKey("for cycling pages")).tag(1) - }.onChange(of: selKeyBehaviorShiftTab) { value in - mgrPrefs.specifyShiftTabKeyBehavior = (value == 1) ? true : false } .labelsHidden() .horizontalRadioGroupLayout() @@ -114,11 +89,14 @@ struct suiPrefPaneExperience: View { .preferenceDescription() } Preferences.Section(label: { Text(LocalizedStringKey("(Shift+)Space:")) }) { - Picker("", selection: $selKeyBehaviorShiftSpace) { + Picker( + "", + selection: $selKeyBehaviorShiftSpace.onChange { + mgrPrefs.specifyShiftSpaceKeyBehavior = (selKeyBehaviorShiftSpace == 1) ? true : false + } + ) { Text(LocalizedStringKey("Space to +cycle candidates, Shift+Space to +cycle pages")).tag(0) Text(LocalizedStringKey("Space to +cycle pages, Shift+Space to +cycle candidates")).tag(1) - }.onChange(of: selKeyBehaviorShiftSpace) { value in - mgrPrefs.specifyShiftSpaceKeyBehavior = (value == 1) ? true : false } .labelsHidden() .pickerStyle(RadioGroupPickerStyle()) @@ -126,12 +104,15 @@ struct suiPrefPaneExperience: View { .preferenceDescription() } Preferences.Section(label: { Text(LocalizedStringKey("Shift+Letter:")) }) { - Picker("", selection: $selUpperCaseLetterKeyBehavior) { + Picker( + "", + selection: $selUpperCaseLetterKeyBehavior.onChange { + mgrPrefs.upperCaseLetterKeyBehavior = selUpperCaseLetterKeyBehavior + } + ) { Text(LocalizedStringKey("Type them into inline composition buffer")).tag(0) Text(LocalizedStringKey("Directly commit lowercased letters")).tag(1) Text(LocalizedStringKey("Directly commit uppercased letters")).tag(2) - }.onChange(of: selUpperCaseLetterKeyBehavior) { value in - mgrPrefs.upperCaseLetterKeyBehavior = value } .labelsHidden() .pickerStyle(RadioGroupPickerStyle()) @@ -141,39 +122,46 @@ struct suiPrefPaneExperience: View { Preferences.Section(label: { Text(LocalizedStringKey("Misc Settings:")) }) { Toggle( LocalizedStringKey("Enable Space key for calling candidate window"), - isOn: $selKeyBehaviorSpaceForCallingCandidate - ).onChange(of: selKeyBehaviorSpaceForCallingCandidate) { value in - mgrPrefs.chooseCandidateUsingSpace = value - } + isOn: $selKeyBehaviorSpaceForCallingCandidate.onChange { + mgrPrefs.chooseCandidateUsingSpace = selKeyBehaviorSpaceForCallingCandidate + } + ) Toggle( LocalizedStringKey("Use ESC key to clear the entire input buffer"), - isOn: $selKeyBehaviorESCForClearingTheBuffer - ).onChange(of: selKeyBehaviorESCForClearingTheBuffer) { value in - mgrPrefs.escToCleanInputBuffer = value - } + isOn: $selKeyBehaviorESCForClearingTheBuffer.onChange { + mgrPrefs.escToCleanInputBuffer = selKeyBehaviorESCForClearingTheBuffer + } + ) Toggle( LocalizedStringKey("Automatically correct reading combinations when typing"), - isOn: $selAutoCorrectReadingCombination - ).onChange(of: selAutoCorrectReadingCombination) { value in - mgrPrefs.autoCorrectReadingCombination = value - } + isOn: $selAutoCorrectReadingCombination.onChange { + mgrPrefs.autoCorrectReadingCombination = selAutoCorrectReadingCombination + } + ) Toggle( LocalizedStringKey("Allow using Enter key to confirm associated candidate selection"), - isOn: $selAlsoConfirmAssociatedCandidatesByEnter - ).onChange(of: selAlsoConfirmAssociatedCandidatesByEnter) { value in - mgrPrefs.alsoConfirmAssociatedCandidatesByEnter = value - } + isOn: $selAlsoConfirmAssociatedCandidatesByEnter.onChange { + mgrPrefs.alsoConfirmAssociatedCandidatesByEnter = selAlsoConfirmAssociatedCandidatesByEnter + } + ) Toggle( LocalizedStringKey("Also toggle alphanumerical mode with Left-Shift"), - isOn: $selTogglingAlphanumericalModeWithLShift - ).onChange(of: selTogglingAlphanumericalModeWithLShift) { value in - mgrPrefs.togglingAlphanumericalModeWithLShift = value - } + isOn: $selTogglingAlphanumericalModeWithLShift.onChange { + mgrPrefs.togglingAlphanumericalModeWithLShift = selTogglingAlphanumericalModeWithLShift + } + ) Toggle( - LocalizedStringKey("Emulating select-candidate-per-character mode"), isOn: $selEnableSCPCTypingMode - ).onChange(of: selEnableSCPCTypingMode) { value in - mgrPrefs.useSCPCTypingMode = value - } + LocalizedStringKey("Allow backspace-editing miscomposed readings"), + isOn: $selKeepReadingUponCompositionError.onChange { + mgrPrefs.keepReadingUponCompositionError = selKeepReadingUponCompositionError + } + ) + Toggle( + LocalizedStringKey("Emulating select-candidate-per-character mode"), + isOn: $selEnableSCPCTypingMode.onChange { + mgrPrefs.useSCPCTypingMode = selEnableSCPCTypingMode + } + ) Text(LocalizedStringKey("An accomodation for elder computer users.")) .preferenceDescription() } diff --git a/Source/UI/PrefUI/suiPrefPaneGeneral.swift b/Source/UI/PrefUI/suiPrefPaneGeneral.swift index 5d50831d..505ad520 100644 --- a/Source/UI/PrefUI/suiPrefPaneGeneral.swift +++ b/Source/UI/PrefUI/suiPrefPaneGeneral.swift @@ -9,7 +9,7 @@ import Cocoa import SwiftUI -@available(macOS 11.0, *) +@available(macOS 10.15, *) struct suiPrefPaneGeneral: View { @State private var selCandidateUIFontSize = UserDefaults.standard.integer( forKey: UserDef.kCandidateListTextSize.rawValue) @@ -53,7 +53,12 @@ struct suiPrefPaneGeneral: View { var body: some View { Preferences.Container(contentWidth: contentWidth) { Preferences.Section(bottomDivider: false, label: { Text(LocalizedStringKey("Candidate Size:")) }) { - Picker("", selection: $selCandidateUIFontSize) { + Picker( + "", + selection: $selCandidateUIFontSize.onChange { + mgrPrefs.candidateListTextSize = CGFloat(selCandidateUIFontSize) + } + ) { Text("12").tag(12) Text("14").tag(14) Text("16").tag(16) @@ -62,8 +67,6 @@ struct suiPrefPaneGeneral: View { Text("32").tag(32) Text("64").tag(64) Text("96").tag(96) - }.onChange(of: selCandidateUIFontSize) { value in - mgrPrefs.candidateListTextSize = CGFloat(value) } .labelsHidden() .frame(width: 120.0) @@ -71,27 +74,30 @@ struct suiPrefPaneGeneral: View { .preferenceDescription() } Preferences.Section(bottomDivider: false, label: { Text(LocalizedStringKey("UI Language:")) }) { - Picker(LocalizedStringKey("Follow OS settings"), selection: $selUILanguage) { + Picker( + LocalizedStringKey("Follow OS settings"), + selection: $selUILanguage.onChange { + IME.prtDebugIntel(selUILanguage[0]) + if selUILanguage == mgrPrefs.appleLanguages + || (selUILanguage[0] == "auto" + && UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil) + { + return + } + if selUILanguage[0] != "auto" { + mgrPrefs.appleLanguages = selUILanguage + } else { + UserDefaults.standard.removeObject(forKey: UserDef.kAppleLanguages.rawValue) + } + NSLog("vChewing App self-terminated due to UI language change.") + NSApplication.shared.terminate(nil) + } + ) { Text(LocalizedStringKey("Follow OS settings")).tag(["auto"]) Text(LocalizedStringKey("Simplified Chinese")).tag(["zh-Hans"]) Text(LocalizedStringKey("Traditional Chinese")).tag(["zh-Hant"]) Text(LocalizedStringKey("Japanese")).tag(["ja"]) Text(LocalizedStringKey("English")).tag(["en"]) - }.onChange(of: selUILanguage) { value in - IME.prtDebugIntel(value[0]) - if selUILanguage == mgrPrefs.appleLanguages - || (selUILanguage[0] == "auto" - && UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil) - { - return - } - if selUILanguage[0] != "auto" { - mgrPrefs.appleLanguages = value - } else { - UserDefaults.standard.removeObject(forKey: UserDef.kAppleLanguages.rawValue) - } - NSLog("vChewing App self-terminated due to UI language change.") - NSApplication.shared.terminate(nil) } .labelsHidden() .frame(width: 180.0) @@ -100,11 +106,14 @@ struct suiPrefPaneGeneral: View { .preferenceDescription() } Preferences.Section(bottomDivider: true, label: { Text(LocalizedStringKey("Candidate Layout:")) }) { - Picker("", selection: $selEnableHorizontalCandidateLayout) { + Picker( + "", + selection: $selEnableHorizontalCandidateLayout.onChange { + mgrPrefs.useHorizontalCandidateList = selEnableHorizontalCandidateLayout + } + ) { Text(LocalizedStringKey("Vertical")).tag(false) Text(LocalizedStringKey("Horizontal")).tag(true) - }.onChange(of: selEnableHorizontalCandidateLayout) { value in - mgrPrefs.useHorizontalCandidateList = value } .labelsHidden() .horizontalRadioGroupLayout() @@ -112,11 +121,9 @@ struct suiPrefPaneGeneral: View { Text(LocalizedStringKey("Choose your preferred layout of the candidate window.")) .preferenceDescription() Toggle( - LocalizedStringKey("Show page buttons in candidate window"), isOn: $selShowPageButtonsInCandidateUI - ).onChange( - of: selShowPageButtonsInCandidateUI, - perform: { value in - mgrPrefs.showPageButtonsInCandidateWindow = value + LocalizedStringKey("Show page buttons in candidate window"), + isOn: $selShowPageButtonsInCandidateUI.onChange { + mgrPrefs.showPageButtonsInCandidateWindow = selShowPageButtonsInCandidateUI } ) .controlSize(.small) @@ -124,58 +131,59 @@ struct suiPrefPaneGeneral: View { Preferences.Section(bottomDivider: true, label: { Text(LocalizedStringKey("Output Settings:")) }) { Toggle( LocalizedStringKey("Auto-convert traditional Chinese glyphs to KangXi characters"), - isOn: $selEnableKanjiConvToKangXi - ).onChange(of: selEnableKanjiConvToKangXi) { value in - mgrPrefs.chineseConversionEnabled = value - selEnableKanjiConvToKangXi = value - if value { - mgrPrefs.shiftJISShinjitaiOutputEnabled = !value - selEnableKanjiConvToJIS = !value + isOn: $selEnableKanjiConvToKangXi.onChange { + mgrPrefs.chineseConversionEnabled = selEnableKanjiConvToKangXi + if selEnableKanjiConvToKangXi { + mgrPrefs.shiftJISShinjitaiOutputEnabled = !selEnableKanjiConvToKangXi + selEnableKanjiConvToJIS = !selEnableKanjiConvToKangXi + } } - } + ) Toggle( LocalizedStringKey("Auto-convert traditional Chinese glyphs to JIS Shinjitai characters"), - isOn: $selEnableKanjiConvToJIS - ).onChange(of: selEnableKanjiConvToJIS) { value in - mgrPrefs.shiftJISShinjitaiOutputEnabled = value - selEnableKanjiConvToJIS = value - if value { - mgrPrefs.chineseConversionEnabled = !value - selEnableKanjiConvToKangXi = !value + isOn: $selEnableKanjiConvToJIS.onChange { + mgrPrefs.shiftJISShinjitaiOutputEnabled = selEnableKanjiConvToJIS + if selEnableKanjiConvToJIS { + mgrPrefs.chineseConversionEnabled = !selEnableKanjiConvToJIS + selEnableKanjiConvToKangXi = !selEnableKanjiConvToJIS + } } - } + ) Toggle( LocalizedStringKey("Show Hanyu-Pinyin in the inline composition buffer & tooltip"), - isOn: $selShowHanyuPinyinInCompositionBuffer - ).onChange(of: selShowHanyuPinyinInCompositionBuffer) { value in - mgrPrefs.showHanyuPinyinInCompositionBuffer = value - selShowHanyuPinyinInCompositionBuffer = value - } + isOn: $selShowHanyuPinyinInCompositionBuffer.onChange { + mgrPrefs.showHanyuPinyinInCompositionBuffer = selShowHanyuPinyinInCompositionBuffer + } + ) Toggle( LocalizedStringKey("Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"), - isOn: $selInlineDumpPinyinInLieuOfZhuyin - ).onChange(of: selInlineDumpPinyinInLieuOfZhuyin) { value in - mgrPrefs.inlineDumpPinyinInLieuOfZhuyin = value - selInlineDumpPinyinInLieuOfZhuyin = value - } + isOn: $selInlineDumpPinyinInLieuOfZhuyin.onChange { + mgrPrefs.inlineDumpPinyinInLieuOfZhuyin = selInlineDumpPinyinInLieuOfZhuyin + } + ) Toggle( LocalizedStringKey("Stop farting (when typed phonetic combination is invalid, etc.)"), - isOn: $selEnableFartSuppressor - ).onChange(of: selEnableFartSuppressor) { value in - mgrPrefs.shouldNotFartInLieuOfBeep = value - clsSFX.beep() - } + isOn: $selEnableFartSuppressor.onChange { + mgrPrefs.shouldNotFartInLieuOfBeep = selEnableFartSuppressor + clsSFX.beep() + } + ) } Preferences.Section(label: { Text(LocalizedStringKey("Misc Settings:")).controlSize(.small) }) { - Toggle(LocalizedStringKey("Check for updates automatically"), isOn: $selEnableAutoUpdateCheck) - .onChange(of: selEnableAutoUpdateCheck) { value in - mgrPrefs.checkUpdateAutomatically = value + Toggle( + LocalizedStringKey("Check for updates automatically"), + isOn: $selEnableAutoUpdateCheck.onChange { + mgrPrefs.checkUpdateAutomatically = selEnableAutoUpdateCheck } - .controlSize(.small) - Toggle(LocalizedStringKey("Debug Mode"), isOn: $selEnableDebugMode).controlSize(.small) - .onChange(of: selEnableDebugMode) { value in - mgrPrefs.isDebugModeEnabled = value + ) + .controlSize(.small) + Toggle( + LocalizedStringKey("Debug Mode"), + isOn: $selEnableDebugMode.onChange { + mgrPrefs.isDebugModeEnabled = selEnableDebugMode } + ) + .controlSize(.small) } } } diff --git a/Source/UI/PrefUI/suiPrefPaneKeyboard.swift b/Source/UI/PrefUI/suiPrefPaneKeyboard.swift index 3cae715d..9da8ebcd 100644 --- a/Source/UI/PrefUI/suiPrefPaneKeyboard.swift +++ b/Source/UI/PrefUI/suiPrefPaneKeyboard.swift @@ -8,8 +8,11 @@ import SwiftUI -@available(macOS 11.0, *) +@available(macOS 10.15, *) struct suiPrefPaneKeyboard: View { + @State private var selSelectionKeysList = mgrPrefs.suggestedCandidateKeys + @State private var selSelectionKeys = + UserDefaults.standard.string(forKey: UserDef.kCandidateKeys.rawValue) ?? mgrPrefs.defaultCandidateKeys @State private var selMandarinParser = UserDefaults.standard.integer(forKey: UserDef.kMandarinParser.rawValue) @State private var selBasicKeyboardLayout: String = UserDefaults.standard.string(forKey: UserDef.kBasicKeyboardLayout.rawValue) ?? mgrPrefs.basicKeyboardLayout @@ -40,9 +43,57 @@ struct suiPrefPaneKeyboard: View { var body: some View { Preferences.Container(contentWidth: contentWidth) { + Preferences.Section(label: { Text(LocalizedStringKey("Selection Keys:")) }) { + ComboBox( + items: mgrPrefs.suggestedCandidateKeys, + text: $selSelectionKeys.onChange { + let value = selSelectionKeys + let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicate + do { + try mgrPrefs.validate(candidateKeys: keys) + mgrPrefs.candidateKeys = keys + selSelectionKeys = mgrPrefs.candidateKeys + } catch mgrPrefs.CandidateKeyError.empty { + selSelectionKeys = mgrPrefs.candidateKeys + } catch { + if let window = ctlPrefUI.shared.controller.window { + let alert = NSAlert(error: error) + alert.beginSheetModal(for: window) { _ in + selSelectionKeys = mgrPrefs.candidateKeys + } + clsSFX.beep() + } + } + } + ).frame(width: 180) + Text( + LocalizedStringKey( + "Choose or hit Enter to confim your prefered keys for selecting candidates." + ) + ) + .preferenceDescription() + } Preferences.Section(label: { Text(LocalizedStringKey("Phonetic Parser:")) }) { HStack { - Picker("", selection: $selMandarinParser) { + Picker( + "", + selection: $selMandarinParser.onChange { + let value = selMandarinParser + mgrPrefs.mandarinParser = value + switch value { + case 0: + if !AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { + mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ZhuyinBopomofo" + selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout + } + default: + if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { + mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ABC" + selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout + } + } + } + ) { Group { Text(LocalizedStringKey("Dachen (Microsoft Standard / Wang / 01, etc.)")).tag(0) Text(LocalizedStringKey("Eten Traditional")).tag(1) @@ -66,20 +117,6 @@ struct suiPrefPaneKeyboard: View { Text(LocalizedStringKey("Hualuo Pinyin with Numeral Intonation")).tag(13) Text(LocalizedStringKey("Universal Pinyin with Numeral Intonation")).tag(14) } - }.onChange(of: selMandarinParser) { value in - mgrPrefs.mandarinParser = value - switch value { - case 0: - if !AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { - mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ZhuyinBopomofo" - selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout - } - default: - if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) { - mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ABC" - selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout - } - } } .labelsHidden() Button { @@ -105,17 +142,21 @@ struct suiPrefPaneKeyboard: View { } Preferences.Section(bottomDivider: true, label: { Text(LocalizedStringKey("Basic Keyboard Layout:")) }) { HStack { - Picker("", selection: $selBasicKeyboardLayout) { + Picker( + "", + selection: $selBasicKeyboardLayout.onChange { + let value = selBasicKeyboardLayout + mgrPrefs.basicKeyboardLayout = value + if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(value) { + mgrPrefs.mandarinParser = 0 + selMandarinParser = mgrPrefs.mandarinParser + } + } + ) { ForEach(0...(IME.arrEnumerateSystemKeyboardLayouts.count - 1), id: \.self) { id in Text(IME.arrEnumerateSystemKeyboardLayouts[id].strName).tag( IME.arrEnumerateSystemKeyboardLayouts[id].strValue) }.id(UUID()) - }.onChange(of: selBasicKeyboardLayout) { value in - mgrPrefs.basicKeyboardLayout = value - if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(value) { - mgrPrefs.mandarinParser = 0 - selMandarinParser = mgrPrefs.mandarinParser - } } .labelsHidden() .frame(width: 240.0) @@ -126,53 +167,46 @@ struct suiPrefPaneKeyboard: View { Preferences.Section(bottomDivider: true, label: { Text(LocalizedStringKey("Keyboard Shortcuts:")) }) { Toggle( LocalizedStringKey("Per-Char Select Mode"), - isOn: $selUsingHotKeySCPC - ).onChange(of: selUsingHotKeySCPC) { value in - mgrPrefs.usingHotKeySCPC = value - selUsingHotKeySCPC = value - } + isOn: $selUsingHotKeySCPC.onChange { + mgrPrefs.usingHotKeySCPC = selUsingHotKeySCPC + } + ) Toggle( LocalizedStringKey("Per-Char Associated Phrases"), - isOn: $selUsingHotKeyAssociates - ).onChange(of: selUsingHotKeyAssociates) { value in - mgrPrefs.usingHotKeyAssociates = value - selUsingHotKeyAssociates = value - } + isOn: $selUsingHotKeyAssociates.onChange { + mgrPrefs.usingHotKeyAssociates = selUsingHotKeyAssociates + } + ) Toggle( LocalizedStringKey("CNS11643 Mode"), - isOn: $selUsingHotKeyCNS - ).onChange(of: selUsingHotKeyCNS) { value in - mgrPrefs.usingHotKeyCNS = value - selUsingHotKeyCNS = value - } + isOn: $selUsingHotKeyCNS.onChange { + mgrPrefs.usingHotKeyCNS = selUsingHotKeyCNS + } + ) Toggle( LocalizedStringKey("Force KangXi Writing"), - isOn: $selUsingHotKeyKangXi - ).onChange(of: selUsingHotKeyKangXi) { value in - mgrPrefs.usingHotKeyKangXi = value - selUsingHotKeyKangXi = value - } + isOn: $selUsingHotKeyKangXi.onChange { + mgrPrefs.usingHotKeyKangXi = selUsingHotKeyKangXi + } + ) Toggle( LocalizedStringKey("JIS Shinjitai Output"), - isOn: $selUsingHotKeyJIS - ).onChange(of: selUsingHotKeyJIS) { value in - mgrPrefs.usingHotKeyJIS = value - selUsingHotKeyJIS = value - } + isOn: $selUsingHotKeyJIS.onChange { + mgrPrefs.usingHotKeyJIS = selUsingHotKeyJIS + } + ) Toggle( LocalizedStringKey("Half-Width Punctuation Mode"), - isOn: $selUsingHotKeyHalfWidthASCII - ).onChange(of: selUsingHotKeyHalfWidthASCII) { value in - mgrPrefs.usingHotKeyHalfWidthASCII = value - selUsingHotKeyHalfWidthASCII = value - } + isOn: $selUsingHotKeyHalfWidthASCII.onChange { + mgrPrefs.usingHotKeyHalfWidthASCII = selUsingHotKeyHalfWidthASCII + } + ) Toggle( LocalizedStringKey("Currency Numeral Output"), - isOn: $selUsingHotKeyCurrencyNumerals - ).onChange(of: selUsingHotKeyCurrencyNumerals) { value in - mgrPrefs.usingHotKeyCurrencyNumerals = value - selUsingHotKeyCurrencyNumerals = value - } + isOn: $selUsingHotKeyCurrencyNumerals.onChange { + mgrPrefs.usingHotKeyCurrencyNumerals = selUsingHotKeyCurrencyNumerals + } + ) } } Divider() @@ -181,7 +215,7 @@ struct suiPrefPaneKeyboard: View { VStack(alignment: .leading, spacing: 10) { Text( LocalizedStringKey( - "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only." + "Non-QWERTY alphanumerical keyboard layouts are for Hanyu Pinyin parser only." ) ) .preferenceDescription() diff --git a/Source/WindowControllers/ctlPrefWindow.swift b/Source/WindowControllers/ctlPrefWindow.swift index 9d08bd8f..fe1d8d14 100644 --- a/Source/WindowControllers/ctlPrefWindow.swift +++ b/Source/WindowControllers/ctlPrefWindow.swift @@ -375,7 +375,7 @@ extension ctlPrefWindow: NSToolbarDelegate { systemSymbolName: "wrench.and.screwdriver.fill", accessibilityDescription: "General Preferences" ) } else { - item.image = NSImage(named: NSImage.homeTemplateName) + item.image = NSImage(named: "PrefToolbar-General") } item.action = #selector(showGeneralView(_:)) @@ -387,7 +387,7 @@ extension ctlPrefWindow: NSToolbarDelegate { systemSymbolName: "person.fill.questionmark", accessibilityDescription: "Experiences Preferences" ) } else { - item.image = NSImage(named: NSImage.flowViewTemplateName) + item.image = NSImage(named: "PrefToolbar-Experiences") } item.action = #selector(showExperienceView(_:)) @@ -399,7 +399,7 @@ extension ctlPrefWindow: NSToolbarDelegate { systemSymbolName: "character.book.closed.fill", accessibilityDescription: "Dictionary Preferences" ) } else { - item.image = NSImage(named: NSImage.bookmarksTemplateName) + item.image = NSImage(named: "PrefToolbar-Dictionary") } item.action = #selector(showDictionaryView(_:)) @@ -409,7 +409,7 @@ extension ctlPrefWindow: NSToolbarDelegate { if #available(macOS 11.0, *) { item.image = NSImage(systemSymbolName: "keyboard.macwindow", accessibilityDescription: "Keyboard Preferences") } else { - item.image = NSImage(named: NSImage.slideshowTemplateName) + item.image = NSImage(named: "PrefToolbar-Keyboard") } item.action = #selector(showKeyboardView(_:)) diff --git a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib index 4ba628a9..b9b3661b 100644 --- a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib +++ b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib @@ -410,7 +410,7 @@ - +