From 7cf1fd74489f9fb727c815115e7273c3c5ef6f21 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Thu, 29 Sep 2022 12:47:26 +0800 Subject: [PATCH 01/23] SessionCtl // Add caps lock notification support, etc. --- Source/Modules/SessionCtl_Core.swift | 9 ++++++--- Source/Modules/SessionCtl_Delegates.swift | 2 -- Source/Modules/SessionCtl_HandleDisplay.swift | 2 -- Source/Modules/SessionCtl_HandleEvent.swift | 17 ++++++++++++++--- Source/Modules/SessionCtl_HandleStates.swift | 2 -- Source/Modules/SessionCtl_Menu.swift | 2 -- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Source/Modules/SessionCtl_Core.swift b/Source/Modules/SessionCtl_Core.swift index 763c8729..9c2824d1 100644 --- a/Source/Modules/SessionCtl_Core.swift +++ b/Source/Modules/SessionCtl_Core.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) @@ -8,6 +6,7 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. +import CocoaExtension import IMKUtils import PopupCompositionBuffer import Shared @@ -40,6 +39,9 @@ class SessionCtl: IMKInputController { // MARK: - + /// 當前 CapsLock 按鍵是否被摁下。 + var isCapsLocked: Bool { NSEvent.modifierFlags.intersection(.deviceIndependentFlagsMask).contains(.capsLock) } + /// 當前這個 SessionCtl 副本是否處於英數輸入模式。 var isASCIIMode = false { didSet { @@ -159,7 +161,8 @@ extension SessionCtl { keyHandler.clear() // 這句不要砍,因為後面 handle State.Empty() 不一定執行。 keyHandler.ensureKeyboardParser() - if isASCIIMode, PrefMgr.shared.disableShiftTogglingAlphanumericalMode { isASCIIMode = false } + if isASCIIMode, !isCapsLocked, PrefMgr.shared.disableShiftTogglingAlphanumericalMode { isASCIIMode = false } + if isCapsLocked { isASCIIMode = isCapsLocked } // 同步 CapsLock 狀態。 /// 必須加上下述條件,否則會在每次切換至輸入法本體的視窗(比如偏好設定視窗)時會卡死。 /// 這是很多 macOS 副廠輸入法的常見失誤之處。 diff --git a/Source/Modules/SessionCtl_Delegates.swift b/Source/Modules/SessionCtl_Delegates.swift index 66ad9022..2b7cb21f 100644 --- a/Source/Modules/SessionCtl_Delegates.swift +++ b/Source/Modules/SessionCtl_Delegates.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) diff --git a/Source/Modules/SessionCtl_HandleDisplay.swift b/Source/Modules/SessionCtl_HandleDisplay.swift index ebc31d76..574cdfc4 100644 --- a/Source/Modules/SessionCtl_HandleDisplay.swift +++ b/Source/Modules/SessionCtl_HandleDisplay.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) diff --git a/Source/Modules/SessionCtl_HandleEvent.swift b/Source/Modules/SessionCtl_HandleEvent.swift index f3eae536..5636e37e 100644 --- a/Source/Modules/SessionCtl_HandleEvent.swift +++ b/Source/Modules/SessionCtl_HandleEvent.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) @@ -8,6 +6,7 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. +import CocoaExtension import InputMethodKit import NotifierUI import Shared @@ -36,6 +35,18 @@ extension SessionCtl { return false } + // Caps Lock 通知與切換處理。 + if event.type == .flagsChanged, event.keyCode == KeyCode.kCapsLock.rawValue { + let isCapsLockTurnedOn = event.modifierFlags.intersection(.deviceIndependentFlagsMask).contains(.capsLock) + let status = NSLocalizedString("NotificationSwitchShift", comment: "") + Notifier.notify( + message: isCapsLockTurnedOn + ? "Caps Lock" + NSLocalizedString("Alphanumerical Input Mode", comment: "") + "\n" + status + : NSLocalizedString("Chinese Input Mode", comment: "") + "\n" + status + ) + isASCIIMode = isCapsLockTurnedOn + } + // 用 Shift 開關半形英數模式,僅對 macOS 10.15 及之後的 macOS 有效。 let shouldUseShiftToggleHandle: Bool = { switch PrefMgr.shared.shiftKeyAccommodationBehavior { @@ -67,7 +78,7 @@ extension SessionCtl { // MARK: 針對客體的具體處理 // 不再讓威注音處理由 Shift 切換到的英文模式的按鍵輸入。 - if isASCIIMode { return false } + if isASCIIMode, !isCapsLocked { return false } /// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。 /// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。 diff --git a/Source/Modules/SessionCtl_HandleStates.swift b/Source/Modules/SessionCtl_HandleStates.swift index 2f3c8928..c4a8ab49 100644 --- a/Source/Modules/SessionCtl_HandleStates.swift +++ b/Source/Modules/SessionCtl_HandleStates.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) diff --git a/Source/Modules/SessionCtl_Menu.swift b/Source/Modules/SessionCtl_Menu.swift index deada5c3..fb4279ee 100644 --- a/Source/Modules/SessionCtl_Menu.swift +++ b/Source/Modules/SessionCtl_Menu.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) From 34738e7bd6bd31d532b3885fca2ef94114e33e3a Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 26 Sep 2022 23:20:39 +0800 Subject: [PATCH 02/23] i18n // Fix the localization key name according to recent changes. --- Source/Modules/SessionCtl_HandleEvent.swift | 4 ++-- Source/Resources/Base.lproj/Localizable.strings | 2 +- Source/Resources/en.lproj/Localizable.strings | 2 +- Source/Resources/ja.lproj/Localizable.strings | 2 +- Source/Resources/zh-Hans.lproj/Localizable.strings | 2 +- Source/Resources/zh-Hant.lproj/Localizable.strings | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Modules/SessionCtl_HandleEvent.swift b/Source/Modules/SessionCtl_HandleEvent.swift index 5636e37e..5b55c778 100644 --- a/Source/Modules/SessionCtl_HandleEvent.swift +++ b/Source/Modules/SessionCtl_HandleEvent.swift @@ -38,7 +38,7 @@ extension SessionCtl { // Caps Lock 通知與切換處理。 if event.type == .flagsChanged, event.keyCode == KeyCode.kCapsLock.rawValue { let isCapsLockTurnedOn = event.modifierFlags.intersection(.deviceIndependentFlagsMask).contains(.capsLock) - let status = NSLocalizedString("NotificationSwitchShift", comment: "") + let status = NSLocalizedString("NotificationSwitchASCII", comment: "") Notifier.notify( message: isCapsLockTurnedOn ? "Caps Lock" + NSLocalizedString("Alphanumerical Input Mode", comment: "") + "\n" + status @@ -61,7 +61,7 @@ extension SessionCtl { if #available(macOS 10.15, *) { if Self.theShiftKeyDetector.check(event), !PrefMgr.shared.disableShiftTogglingAlphanumericalMode { if !shouldUseShiftToggleHandle || (!rencentKeyHandledByKeyHandlerEtc && shouldUseShiftToggleHandle) { - let status = NSLocalizedString("NotificationSwitchShift", comment: "") + let status = NSLocalizedString("NotificationSwitchASCII", comment: "") Notifier.notify( message: isASCIIMode.toggled() ? NSLocalizedString("Alphanumerical Input Mode", comment: "") + "\n" + status diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index f9fbaae2..babb07e4 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -38,7 +38,7 @@ "Force KangXi Writing" = "Force KangXi Writing"; "NotificationSwitchON" = "✔ ON"; "NotificationSwitchOFF" = "✘ OFF"; -"NotificationSwitchShift" = "↺ Switched"; +"NotificationSwitchASCII" = "↺ Switched"; "Edit User Phrases…" = "Edit User Phrases…"; "Reload User Phrases" = "Reload User Phrases"; "Unable to create the user phrase file." = "Unable to create the user phrase file."; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index f9fbaae2..babb07e4 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -38,7 +38,7 @@ "Force KangXi Writing" = "Force KangXi Writing"; "NotificationSwitchON" = "✔ ON"; "NotificationSwitchOFF" = "✘ OFF"; -"NotificationSwitchShift" = "↺ Switched"; +"NotificationSwitchASCII" = "↺ Switched"; "Edit User Phrases…" = "Edit User Phrases…"; "Reload User Phrases" = "Reload User Phrases"; "Unable to create the user phrase file." = "Unable to create the user phrase file."; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index 3502f69a..e0d83018 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -38,7 +38,7 @@ "Force KangXi Writing" = "康熙文字変換モード"; "NotificationSwitchON" = "✔ 機能起動"; "NotificationSwitchOFF" = "✘ 機能停止"; -"NotificationSwitchShift" = "↺ 切替完了"; +"NotificationSwitchASCII" = "↺ 切替完了"; "Edit User Phrases…" = "ユーザー辞書を編集…"; "Reload User Phrases" = "ユーザー辞書を再び読込む"; "Unable to create the user phrase file." = "ユーザー辞書ファイルの作成は失敗しました。"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 7b44bb52..0e86e76c 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -38,7 +38,7 @@ "Force KangXi Writing" = "康熙正体字模式"; "NotificationSwitchON" = "✔ 已启用"; "NotificationSwitchOFF" = "✘ 已停用"; -"NotificationSwitchShift" = "↺ 切换完毕"; +"NotificationSwitchASCII" = "↺ 切换完毕"; "Edit User Phrases…" = "编辑自订语汇…"; "Reload User Phrases" = "重载自订语汇"; "Unable to create the user phrase file." = "无法创建自订语汇档案。"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 5e9fc178..285d8a1f 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -38,7 +38,7 @@ "Force KangXi Writing" = "康熙正體字模式"; "NotificationSwitchON" = "✔ 已啟用"; "NotificationSwitchOFF" = "✘ 已停用"; -"NotificationSwitchShift" = "↺ 切換完畢"; +"NotificationSwitchASCII" = "↺ 切換完畢"; "Edit User Phrases…" = "編輯自訂語彙…"; "Reload User Phrases" = "重載自訂語彙"; "Unable to create the user phrase file." = "無法創建自訂語彙檔案。"; From 3a90bb7665fca222206eb23e8e611fbb282a1be4 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 26 Sep 2022 23:29:09 +0800 Subject: [PATCH 03/23] PrefUI // Fix certain terms according to recent changes. --- Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift | 2 +- Source/Resources/Base.lproj/Localizable.strings | 2 +- Source/Resources/en.lproj/Localizable.strings | 2 +- Source/Resources/ja.lproj/Localizable.strings | 2 +- Source/Resources/zh-Hans.lproj/Localizable.strings | 2 +- Source/Resources/zh-Hant.lproj/Localizable.strings | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift b/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift index 47b23312..db5b8442 100644 --- a/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift +++ b/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift @@ -225,7 +225,7 @@ struct suiPrefPaneKeyboard: View { HStack { Text( NSLocalizedString( - "Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key.", + "Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only.", comment: "" ) ) diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index babb07e4..a18ff432 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -145,7 +145,7 @@ "Choose the behavior of (Shift+)Tab key in the candidate window." = "Choose the behavior of (Shift+)Tab key in the candidate window."; "Choose the behavior of Shift+Letter key with letter inputs." = "Choose the behavior of Shift+Letter key with letter inputs."; "Choose the cursor position where you want to list possible candidates." = "Choose the cursor position where you want to list possible candidates."; -"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key." = "Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key."; +"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only." = "Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only."; "Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only." = "Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only."; "Choose the phonetic layout for Mandarin parser." = "Choose the phonetic layout for Mandarin parser."; "Choose your desired user data folder path. Will be omitted if invalid." = "Choose your desired user data folder path. Will be omitted if invalid."; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index babb07e4..a18ff432 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -145,7 +145,7 @@ "Choose the behavior of (Shift+)Tab key in the candidate window." = "Choose the behavior of (Shift+)Tab key in the candidate window."; "Choose the behavior of Shift+Letter key with letter inputs." = "Choose the behavior of Shift+Letter key with letter inputs."; "Choose the cursor position where you want to list possible candidates." = "Choose the cursor position where you want to list possible candidates."; -"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key." = "Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key."; +"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only." = "Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only."; "Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only." = "Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only."; "Choose the phonetic layout for Mandarin parser." = "Choose the phonetic layout for Mandarin parser."; "Choose your desired user data folder path. Will be omitted if invalid." = "Choose your desired user data folder path. Will be omitted if invalid."; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index e0d83018..c0855035 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -145,7 +145,7 @@ "Choose the behavior of (Shift+)Tab key in the candidate window." = "入力候補陳列での (Shift+)Tab キーの輪番切替対象をご指定ください。"; "Choose the behavior of Shift+Letter key with letter inputs." = "Shift+文字キーの行為をご指定ください。"; "Choose the cursor position where you want to list possible candidates." = "カーソルはどこで入力候補を呼び出すかとご指定ださい。"; -"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key." = "macOS 英数キーボード配置をご指定ください。「Shift キーで切り替える英数入力モード」に使われる配置である。"; +"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only." = "macOS 英数キーボード配置をご指定ください。英数入力モードだけに使われる配置である。"; "Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only." = "macOS 基礎キーボード配置をご指定ください。QWERTY 以外の英数キーボードは弁音以外の配列に不適用。"; "Choose the phonetic layout for Mandarin parser." = "共通語分析器の注音配列をご指定ください。"; "Choose your desired user data folder path. Will be omitted if invalid." = "欲しがるユーザー辞書保存先をご指定ください。無効なる保存先設定は省かれる。"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 0e86e76c..783a758c 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -145,7 +145,7 @@ "Choose the behavior of (Shift+)Tab key in the candidate window." = "指定 (Shift+)Tab 在选字窗内的轮替操作对象。"; "Choose the behavior of Shift+Letter key with letter inputs." = "指定 Shift+字母键 的行为。"; "Choose the cursor position where you want to list possible candidates." = "请选择用以触发选字的游标相对位置。"; -"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key." = "请选择 macOS 英数键盘布局。该布局用于以 Shift 切换至的英数输入模式。"; +"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only." = "请选择 macOS 英数键盘布局。该布局仅用于英数输入模式。"; "Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only." = "请选择 macOS 基础键盘布局。QWERTY 以外的英数布局是为了拼音排列使用者而准备的。"; "Choose the phonetic layout for Mandarin parser." = "请指定普通话/国音分析器所使用的注音排列。"; "Choose your desired user data folder path. Will be omitted if invalid." = "请在此指定您想指定的使用者语汇档案目录。无效值会被忽略。"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 285d8a1f..93ebd52a 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -145,7 +145,7 @@ "Choose the behavior of (Shift+)Tab key in the candidate window." = "指定 (Shift+)Tab 在選字窗內的輪替操作對象。"; "Choose the behavior of Shift+Letter key with letter inputs." = "指定 Shift+字母鍵 的行為。"; "Choose the cursor position where you want to list possible candidates." = "請選擇用以觸發選字的游標相對位置。"; -"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode toggled by Shift key." = "請選擇 macOS 英數鍵盤佈局。該佈局用於以 Shift 切換至的英數輸入模式。"; +"Choose the macOS-level alphanumerical keyboard layout. This setting is for the alphanumerical mode only." = "請選擇 macOS 英數鍵盤佈局。該佈局僅用於英數輸入模式。"; "Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only." = "請選擇 macOS 基礎鍵盤佈局。QWERTY 以外的英數佈局是為了拼音排列使用者而準備的。"; "Choose the phonetic layout for Mandarin parser." = "請指定普通話/國音分析器所使用的注音排列。"; "Choose your desired user data folder path. Will be omitted if invalid." = "請在此指定您想指定的使用者語彙檔案目錄。無效值會被忽略。"; From a0e7d8dfd57a3048fd3a7b77c5e5ee8b445015fb Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 26 Sep 2022 23:36:13 +0800 Subject: [PATCH 04/23] KeyHandler // Add additional notes regarding CapsLock handling. --- Source/Modules/KeyHandler_HandleInput.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/Modules/KeyHandler_HandleInput.swift b/Source/Modules/KeyHandler_HandleInput.swift index bdda6aa1..2469fd80 100644 --- a/Source/Modules/KeyHandler_HandleInput.swift +++ b/Source/Modules/KeyHandler_HandleInput.swift @@ -56,9 +56,7 @@ extension KeyHandler { // MARK: Caps Lock processing. /// 若 Caps Lock 被啟用的話,則暫停對注音輸入的處理。 - /// 這裡的處理原先是給威注音曾經有過的 Shift 切換英數模式來用的,但因為採 Chromium 核 - /// 心的瀏覽器會讓 IMK 無法徹底攔截對 Shift 鍵的單擊行為、導致這個模式的使用體驗非常糟 - /// 糕,故僅保留以 Caps Lock 驅動的英數模式。 + /// 這裡的處理仍舊有用,不然 Caps Lock 英文模式無法直接鍵入小寫字母。 if input.isBackSpace || input.isEnter || input.isCursorClockLeft || input.isCursorClockRight || input.isCursorForward || input.isCursorBackward From 5e4d8a60b02cfce35c37c4d478b3bcc2f4aebb29 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 27 Sep 2022 07:53:24 +0800 Subject: [PATCH 05/23] NotifierUI // Tweak how out-of-date notifications look like. --- .../vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift b/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift index 7fe0fc21..61869cec 100644 --- a/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift +++ b/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift @@ -142,7 +142,10 @@ extension Notifier { private func display() { let existingInstanceArray = Self.instanceStack.compactMap(\.window) if !existingInstanceArray.isEmpty { - existingInstanceArray.forEach { $0.alphaValue *= 0.5 } + existingInstanceArray.forEach { + $0.alphaValue -= 0.1 + $0.contentView?.subviews.forEach { $0.alphaValue *= 0.5 } + } } shiftExistingWindowPositions() fadeIn() From b0c24416512de47911065a2ac737ac37cc59e347 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 27 Sep 2022 08:34:52 +0800 Subject: [PATCH 06/23] NotifierUI // Deduplicate messages when necessary. --- .../Sources/NotifierUI/NotifierUI.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift b/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift index 61869cec..cbf7c9f5 100644 --- a/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift +++ b/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift @@ -21,17 +21,33 @@ public class Notifier: NSWindowController { } } + private var currentMessage: String // 承載該副本在初期化時被傳入的訊息內容。 + private var isNew = true // 新通知標記。 + // MARK: - Private Declarations private static var instanceStack: [Notifier] = [] private let blankValue = "" @discardableResult private init(_ message: String) { + currentMessage = message let rawMessage = message.replacingOccurrences(of: "\n", with: "") - guard let screenRect = NSScreen.main?.visibleFrame, !rawMessage.isEmpty else { + let isDuplicated: Bool = { + if let firstInstanceExisted = Self.instanceStack.first { + return message == firstInstanceExisted.currentMessage && firstInstanceExisted.isNew + } + return false + }() + guard let screenRect = NSScreen.main?.visibleFrame, !rawMessage.isEmpty, !isDuplicated else { super.init(window: nil) return } + // 正式進入處理環節。 + defer { // 先讓新通知標記自此開始過 0.3 秒自動變為 false。 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + self.isNew = false + } + } let kLargeFontSize: Double = 17 let kSmallFontSize: Double = 15 let messageArray = message.components(separatedBy: "\n") From e73fd7183564c7304708ec54104120bbee70861c Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 27 Sep 2022 21:42:31 +0800 Subject: [PATCH 07/23] Repo // Fix copyright information among certain headers. --- Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift | 2 +- .../Sources/IMKUtils/TISInputSourceExtension.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift b/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift index 64ba205b..17bebffb 100644 --- a/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift +++ b/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift @@ -1,4 +1,4 @@ -// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// (c) 2022 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) // ... with NTL restriction stating that: diff --git a/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift b/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift index 058ccd6c..c0f6d15f 100644 --- a/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift +++ b/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift @@ -9,7 +9,7 @@ import Foundation import InputMethodKit -// MARK: - TISInputSource Extension by The vChewing Project (MIT License). +// MARK: - TISInputSource Extension by The vChewing Project (MIT-NTL License). extension TISInputSource { public static var allRegisteredInstancesOfThisInputMethod: [TISInputSource] { From bb8140988a4d6467deeb15130d4cdb630d15cc36 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 28 Sep 2022 19:06:17 +0800 Subject: [PATCH 08/23] i18n // Remove redundant contents. --- Source/Resources/Base.lproj/Localizable.strings | 1 - Source/Resources/en.lproj/Localizable.strings | 1 - Source/Resources/ja.lproj/Localizable.strings | 1 - Source/Resources/zh-Hans.lproj/Localizable.strings | 1 - Source/Resources/zh-Hant.lproj/Localizable.strings | 1 - 5 files changed, 5 deletions(-) diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index a18ff432..09705640 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -82,7 +82,6 @@ "Optimize Memorized Phrases" = "Optimize Memorized Phrases"; "Clear Memorized Phrases" = "Clear Memorized Phrases"; "Currency Numeral Output" = "Currency Numeral Output"; -"Alphanumerical Mode" = "Alphanumerical Mode"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "CommonSymbols"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index a18ff432..09705640 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -82,7 +82,6 @@ "Optimize Memorized Phrases" = "Optimize Memorized Phrases"; "Clear Memorized Phrases" = "Clear Memorized Phrases"; "Currency Numeral Output" = "Currency Numeral Output"; -"Alphanumerical Mode" = "Alphanumerical Mode"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "CommonSymbols"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index c0855035..3ce6f421 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -82,7 +82,6 @@ "Optimize Memorized Phrases" = "臨時記憶資料を整う"; "Clear Memorized Phrases" = "臨時記憶資料を削除"; "Currency Numeral Output" = "数字大字変換"; -"Alphanumerical Mode" = "英数入力モード"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 783a758c..a79cc57d 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -82,7 +82,6 @@ "Optimize Memorized Phrases" = "精简临时记忆语汇资料"; "Clear Memorized Phrases" = "清除临时记忆语汇资料"; "Currency Numeral Output" = "大写汉字数字输出"; -"Alphanumerical Mode" = "英数输入模式"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 93ebd52a..23a3f5db 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -82,7 +82,6 @@ "Optimize Memorized Phrases" = "精簡臨時記憶語彙資料"; "Clear Memorized Phrases" = "清除臨時記憶語彙資料"; "Currency Numeral Output" = "大寫漢字數字輸出"; -"Alphanumerical Mode" = "英數輸入模式"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; From 4ee5f4b2b4c00d6154ed6d30ae10c6c59fb87e8e Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 27 Sep 2022 10:14:39 +0800 Subject: [PATCH 09/23] CtlCandidate // Comments update. --- .../Sources/CandidateWindow/CtlCandidate.swift | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift index ef5509c9..ce01d513 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift @@ -21,7 +21,7 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol { if #available(macOS 10.14, *), !NSApplication.isDarkMode, locale == "zh-Hant" { colorBlendAmount = 0.15 } - // The background color of the highlightened candidate + // 設定當前高亮候選字的背景顏色。 switch locale { case "zh-Hans": result = NSColor.systemRed @@ -72,16 +72,13 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol { visible = false } - /// Sets the location of the candidate window. + /// 設定選字窗的顯示位置。 /// - /// Please note that the method has side effects that modifies - /// `windowTopLeftPoint` to make the candidate window to stay in at least - /// in a screen. + /// 需注意:該函數會藉由設定選字窗左上角頂點的方式、使選字窗始終位於某個螢幕之內。 /// /// - Parameters: - /// - windowTopLeftPoint: The given location. - /// - height: The height that helps the window not to be out of the bottom - /// of a screen. + /// - windowTopLeftPoint: 給定的視窗顯示位置。 + /// - heightDelta: 為了「防止選字窗抻出螢幕下方」而給定的預留高度。 public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double) { DispatchQueue.main.async { [self] in guard let window = window, var screenFrame = NSScreen.main?.visibleFrame else { return } @@ -106,7 +103,7 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol { } } - // MARK: - Contents that are not needed to be implemented here. + // MARK: - 不需要在這裡仔細實作的內容。 @available(*, unavailable) public required init?(coder _: NSCoder) { From 83f5b00f2112343a2772349cb436532b6c9ca69c Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 27 Sep 2022 10:19:49 +0800 Subject: [PATCH 10/23] Repo // Introducing CtlCandidateTDK (horizontal). --- .../Voltaire/CtlCandidateUniversal.swift | 19 +-- .../vChewing_CandidateWindow/Package.swift | 6 +- Packages/vChewing_CandidateWindow/README.md | 13 +- .../CandidateWindow/CandidatePool.swift | 128 ++++++++++++++++++ .../CandidateWindow/CtlCandidate.swift | 11 +- .../CandidateWindow/CtlCandidateTDK.swift | 106 +++++++++++++++ .../CandidateWindow/VwrCandidateTDK.swift | 116 ++++++++++++++++ .../CandidatePoolTests.swift | 46 +++++++ .../Sources/Shared/CandidateBasicUnits.swift | 108 +++++++++++++++ .../Protocols/CtlCandidateProtocol.swift | 10 +- .../Sources/Shared/Shared.swift | 17 --- .../Modules/KeyHandler_HandleCandidate.swift | 10 +- Source/Modules/SessionCtl_Core.swift | 14 +- Source/Modules/SessionCtl_Delegates.swift | 12 +- Source/Modules/SessionCtl_HandleDisplay.swift | 39 +++--- Source/Modules/SessionCtl_HandleEvent.swift | 1 + .../CandidateUI/IMKCandidatesImpl.swift | 19 ++- .../Resources/Base.lproj/Localizable.strings | 1 + Source/Resources/en.lproj/Localizable.strings | 1 + Source/Resources/ja.lproj/Localizable.strings | 1 + .../zh-Hans.lproj/Localizable.strings | 1 + .../zh-Hant.lproj/Localizable.strings | 1 + 22 files changed, 615 insertions(+), 65 deletions(-) create mode 100644 Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift create mode 100644 Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidateTDK.swift create mode 100644 Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift create mode 100644 Packages/vChewing_CandidateWindow/Tests/CandidateWindowTests/CandidatePoolTests.swift create mode 100644 Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift diff --git a/Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift b/Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift index 858d215f..64791caf 100644 --- a/Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift +++ b/Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift @@ -301,17 +301,18 @@ public class CtlCandidateUniversal: CtlCandidate { private var nextPageButton: NSButton private var pageCounterLabel: NSTextField private var currentPageIndex: Int = 0 - override public var currentLayout: CandidateLayout { + override public var currentLayout: NSUserInterfaceLayoutOrientation { get { candidateView.isVerticalLayout ? .vertical : .horizontal } set { switch newValue { case .vertical: candidateView.isVerticalLayout = true case .horizontal: candidateView.isVerticalLayout = false + @unknown default: candidateView.isVerticalLayout = false } } } - public required init(_ layout: CandidateLayout = .horizontal) { + public required init(_ layout: NSUserInterfaceLayoutOrientation = .horizontal) { var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) let styleMask: NSWindow.StyleMask = [.nonactivatingPanel] let panel = NSPanel( @@ -433,7 +434,7 @@ public class CtlCandidateUniversal: CtlCandidate { @discardableResult override public func highlightNextCandidate() -> Bool { guard let delegate = delegate else { return false } selectedCandidateIndex = - (selectedCandidateIndex + 1 >= delegate.candidatePairs().count) + (selectedCandidateIndex + 1 >= delegate.candidatePairs(conv: false).count) ? 0 : selectedCandidateIndex + 1 return true } @@ -442,7 +443,7 @@ public class CtlCandidateUniversal: CtlCandidate { guard let delegate = delegate else { return false } selectedCandidateIndex = (selectedCandidateIndex == 0) - ? delegate.candidatePairs().count - 1 : selectedCandidateIndex - 1 + ? delegate.candidatePairs(conv: false).count - 1 : selectedCandidateIndex - 1 return true } @@ -452,7 +453,7 @@ public class CtlCandidateUniversal: CtlCandidate { } let result = currentPageIndex * keyLabels.count + index - return result < delegate.candidatePairs().count ? result : Int.max + return result < delegate.candidatePairs(conv: false).count ? result : Int.max } override public var selectedCandidateIndex: Int { @@ -464,7 +465,7 @@ public class CtlCandidateUniversal: CtlCandidate { return } let keyLabelCount = keyLabels.count - if newValue < delegate.candidatePairs().count { + if newValue < delegate.candidatePairs(conv: false).count { currentPageIndex = newValue / keyLabelCount candidateView.highlightedIndex = newValue % keyLabelCount layoutCandidateView() @@ -478,7 +479,7 @@ extension CtlCandidateUniversal { guard let delegate = delegate else { return 0 } - let totalCount = delegate.candidatePairs().count + let totalCount = delegate.candidatePairs(conv: false).count let keyLabelCount = keyLabels.count return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0) } @@ -487,7 +488,7 @@ extension CtlCandidateUniversal { guard let delegate = delegate else { return 0 } - let totalCount = delegate.candidatePairs().count + let totalCount = delegate.candidatePairs(conv: false).count let keyLabelCount = keyLabels.count return totalCount % keyLabelCount } @@ -497,7 +498,7 @@ extension CtlCandidateUniversal { candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont) var candidates = [(String, String)]() - let count = delegate.candidatePairs().count + let count = delegate.candidatePairs(conv: false).count let keyLabelCount = keyLabels.count let begin = currentPageIndex * keyLabelCount diff --git a/Packages/vChewing_CandidateWindow/Package.swift b/Packages/vChewing_CandidateWindow/Package.swift index 0ca9b8be..837e3572 100644 --- a/Packages/vChewing_CandidateWindow/Package.swift +++ b/Packages/vChewing_CandidateWindow/Package.swift @@ -21,6 +21,10 @@ let package = Package( dependencies: [ .product(name: "Shared", package: "vChewing_Shared") ] - ) + ), + .testTarget( + name: "CandidateWindowTests", + dependencies: ["CandidateWindow"] + ), ] ) diff --git a/Packages/vChewing_CandidateWindow/README.md b/Packages/vChewing_CandidateWindow/README.md index 628d5b76..e929a70b 100644 --- a/Packages/vChewing_CandidateWindow/README.md +++ b/Packages/vChewing_CandidateWindow/README.md @@ -1,6 +1,17 @@ # CandidateWindow -用以定義與威注音的選字窗有關的基礎內容,目前尚未完工。 +用以定義與威注音的選字窗有關的基礎內容。此外,還包含了威注音自家的次世代選字窗「田所(TDK)」。 + +> 命名緣由:野獸先輩「田所」的姓氏。 + +TDK 選字窗以純 SwiftUI 構築,用以取代此前自上游繼承來的 Voltaire 選字窗。 + +然而,TDK 選字窗目前有下述侷限: + +- 因 SwiftUI 自身特性所導致的嚴重的效能問題。基本上來講,如果您經常使用全字庫模式的話,請在偏好設定內啟用效能更高的 IMK 選字窗。 +- TDK 選字窗目前僅完成了橫版矩陣陳列模式的實作,且尚未引入對縱排選字窗陳列佈局的支援。 + +因為這些問題恐怕需要很久才能全部解決,所以威注音會在這段時間內推薦使用者們優先使用 IMK 選字窗。 ``` // (c) 2021 and onwards The vChewing Project (MIT-NTL License). diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift new file mode 100644 index 00000000..dcdae5b8 --- /dev/null +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift @@ -0,0 +1,128 @@ +// (c) 2022 and onwards The vChewing Project (MIT-NTL 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 Cocoa +import Shared + +/// 候選字窗會用到的資料池單位。用 class 型別會更方便一些。 +public class CandidatePool { + public var currentRowNumber = 0 + public private(set) var selectionKeys: String + public private(set) var highlightedIndex: Int = 0 + public private(set) var maxColumnCapacity: Int = 6 + public private(set) var candidateDataAll: [CandidateCellData] = [] + public private(set) var candidateRows: [[CandidateCellData]] = [] + public var maxWindowHeight: Double { ceil(maxWindowWidth * 0.4) } + public var isVerticalLayout: Bool { maxColumnCapacity == 1 } + public var maxColumnWidth: Int { Int(Double(maxColumnCapacity + 3) * 2) * Int(ceil(CandidateCellData.unifiedSize)) } + public var maxWindowWidth: Double { + ceil(Double(maxColumnCapacity + 3) * 2.7 * ceil(CandidateCellData.unifiedSize) * 1.2) + } + + public enum VerticalDirection { + case up + case down + } + + /// 初期化一個候選字池。 + /// - Parameters: + /// - candidates: 要塞入的候選字詞陣列。 + /// - columnCapacity: (第一行的最大候選字詞數量, 陣列畫面展開之後的每一行的最大候選字詞數量)。 + public init(candidates: [String], columnCapacity: Int = 6, selectionKeys: String = "123456789", locale: String = "") { + maxColumnCapacity = max(1, columnCapacity) + self.selectionKeys = selectionKeys + candidateDataAll = candidates.map { .init(key: "0", displayedText: $0) } + var currentColumn: [CandidateCellData] = [] + for (i, candidate) in candidateDataAll.enumerated() { + candidate.index = i + candidate.whichRow = candidateRows.count + let isOverflown: Bool = currentColumn.map(\.cellLength).reduce(0, +) + candidate.cellLength > maxColumnWidth + if isOverflown || currentColumn.count == maxColumnCapacity, !currentColumn.isEmpty { + candidateRows.append(currentColumn) + currentColumn.removeAll() + candidate.whichRow += 1 + } + candidate.subIndex = currentColumn.count + candidate.locale = locale + currentColumn.append(candidate) + } + candidateRows.append(currentColumn) + } + + public func selectNewNeighborRow(direction: VerticalDirection) { + let currentSubIndex = candidateDataAll[highlightedIndex].subIndex + var result = currentSubIndex + switch direction { + case .up: + if currentRowNumber <= 0 { + if candidateRows.isEmpty { break } + let firstRow = candidateRows[0] + let newSubIndex = min(currentSubIndex, firstRow.count - 1) + highlight(at: firstRow[newSubIndex].index) + break + } + if currentRowNumber >= candidateRows.count - 1 { currentRowNumber = candidateRows.count - 1 } + if candidateRows[currentRowNumber].count != candidateRows[currentRowNumber - 1].count { + let ratio: Double = min(1, Double(currentSubIndex) / Double(candidateRows[currentRowNumber].count)) + result = Int(floor(Double(candidateRows[currentRowNumber - 1].count) * ratio)) + } + let targetRow = candidateRows[currentRowNumber - 1] + let newSubIndex = min(result, targetRow.count - 1) + highlight(at: targetRow[newSubIndex].index) + case .down: + if currentRowNumber >= candidateRows.count - 1 { + if candidateRows.isEmpty { break } + let finalRow = candidateRows[candidateRows.count - 1] + let newSubIndex = min(currentSubIndex, finalRow.count - 1) + highlight(at: finalRow[newSubIndex].index) + break + } + if candidateRows[currentRowNumber].count != candidateRows[currentRowNumber + 1].count { + let ratio: Double = min(1, Double(currentSubIndex) / Double(candidateRows[currentRowNumber].count)) + result = Int(floor(Double(candidateRows[currentRowNumber + 1].count) * ratio)) + } + let targetRow = candidateRows[currentRowNumber + 1] + let newSubIndex = min(result, targetRow.count - 1) + highlight(at: targetRow[newSubIndex].index) + } + } + + public func highlight(at indexSpecified: Int) { + var indexSpecified = indexSpecified + highlightedIndex = indexSpecified + if !(0.. Bool { + thePool.selectNewNeighborRow(direction: .down) + updateDisplay() + return true + } + + @discardableResult override public func showPreviousPage() -> Bool { + thePool.selectNewNeighborRow(direction: .up) + updateDisplay() + return true + } + + @discardableResult override public func highlightNextCandidate() -> Bool { + thePool.highlight(at: thePool.highlightedIndex + 1) + updateDisplay() + return true + } + + @discardableResult override public func highlightPreviousCandidate() -> Bool { + thePool.highlight(at: thePool.highlightedIndex - 1) + updateDisplay() + return true + } + + override public func candidateIndexAtKeyLabelIndex(_ id: Int) -> Int { + let currentRow = thePool.candidateRows[thePool.currentRowNumber] + let actualID = max(0, min(id, currentRow.count - 1)) + return thePool.candidateRows[thePool.currentRowNumber][actualID].index + } + + override public var selectedCandidateIndex: Int { + get { + thePool.highlightedIndex + } + set { + thePool.highlight(at: newValue) + updateDisplay() + } + } +} diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift new file mode 100644 index 00000000..adbbda0e --- /dev/null +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift @@ -0,0 +1,116 @@ +// (c) 2022 and onwards The vChewing Project (MIT-NTL 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 Cocoa +import Shared +import SwiftUI + +// MARK: - Some useless tests + +@available(macOS 12, *) +struct CandidatePoolViewUI_Previews: PreviewProvider { + @State static var testCandidates: [String] = [ + "八月中秋山林涼", "八月中秋", "風吹大地", "山林涼", "草枝擺", "八月", "中秋", + "山林", "風吹", "大地", "草枝", "八", "月", "中", "秋", "山", "林", "涼", "風", + "吹", "大", "地", "草", "枝", "擺", "八", "月", "中", "秋", "山", "林", "涼", "風", + "吹", "大", "地", "草", "枝", "擺", + ] + static var thePool: CandidatePool { + let result = CandidatePool(candidates: testCandidates, columnCapacity: 6) + // 下一行待解決:無論這裡怎麼指定高亮選中項是哪一筆,其所在行都得被卷動到使用者眼前。 + result.highlight(at: 14) + return result + } + + static var previews: some View { + VwrCandidateTDK(controller: .init(.horizontal), thePool: thePool).fixedSize() + } +} + +@available(macOS 12, *) +public struct VwrCandidateTDK: View { + public var controller: CtlCandidateTDK + @State public var thePool: CandidatePool + @State public var hint: String = "" + + private var positionLabel: String { + (thePool.highlightedIndex + 1).description + "/" + thePool.candidateDataAll.count.description + } + + private func didSelectCandidateAt(_ pos: Int) { + if let delegate = controller.delegate { + delegate.candidatePairSelected(at: pos) + } + } + + public var body: some View { + VStack(alignment: .leading, spacing: 0) { + ScrollViewReader { proxy in + ScrollView(.vertical, showsIndicators: true) { + VStack(alignment: .leading, spacing: 1.6) { + ForEach(thePool.candidateRows.indices, id: \.self) { columnIndex in + HStack(spacing: 10) { + ForEach(Array(thePool.candidateRows[columnIndex]), id: \.self) { currentCandidate in + currentCandidate.attributedStringForSwiftUI.fixedSize() + .frame(maxWidth: .infinity, alignment: .topLeading) + .contentShape(Rectangle()) + .onTapGesture { didSelectCandidateAt(currentCandidate.index) } + } + Spacer() + }.frame( + minWidth: 0, + maxWidth: .infinity, + alignment: .topLeading + ).id(columnIndex) + Divider() + } + } + }.onAppear { + proxy.scrollTo(thePool.currentRowNumber) + } + } + .frame(minHeight: thePool.maxWindowHeight, maxHeight: thePool.maxWindowHeight).padding(5) + .background(Color(nsColor: NSColor.controlBackgroundColor).ignoresSafeArea()) + HStack(alignment: .bottom) { + Text(hint).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold)).lineLimit(1) + Spacer() + Text(positionLabel).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold)).lineLimit( + 1) + }.padding(6).foregroundColor(.init(nsColor: .controlTextColor)) + .shadow(color: .init(nsColor: .textBackgroundColor), radius: 1) + } + .frame(minWidth: thePool.maxWindowWidth, maxWidth: thePool.maxWindowWidth) + } +} + +@available(macOS 12, *) +extension CandidateCellData { + public var attributedStringForSwiftUI: some View { + var result: some View { + ZStack(alignment: .leading) { + if isSelected { + Color(nsColor: CandidateCellData.highlightBackground).ignoresSafeArea().cornerRadius(6) + } + VStack(spacing: 0) { + HStack(spacing: 4) { + if UserDefaults.standard.bool(forKey: UserDef.kHandleDefaultCandidateFontsByLangIdentifier.rawValue) { + Text(AttributedString(attributedStringHeader)).frame(width: CandidateCellData.unifiedSize / 2) + Text(AttributedString(attributedString)) + } else { + Text(key).font(.system(size: fontSizeKey).monospaced()) + .foregroundColor(.init(nsColor: fontColorKey)).lineLimit(1) + Text(displayedText).font(.system(size: fontSizeCandidate)) + .foregroundColor(.init(nsColor: fontColorCandidate)).lineLimit(1) + } + }.padding(4) + } + }.fixedSize(horizontal: false, vertical: true) + } + return result + } +} diff --git a/Packages/vChewing_CandidateWindow/Tests/CandidateWindowTests/CandidatePoolTests.swift b/Packages/vChewing_CandidateWindow/Tests/CandidateWindowTests/CandidatePoolTests.swift new file mode 100644 index 00000000..33b69f6f --- /dev/null +++ b/Packages/vChewing_CandidateWindow/Tests/CandidateWindowTests/CandidatePoolTests.swift @@ -0,0 +1,46 @@ +// (c) 2022 and onwards The vChewing Project (MIT-NTL 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 Cocoa +import XCTest + +@testable import CandidateWindow + +final class CandidatePoolTests: XCTestCase { + let testCandidates: [String] = [ + "八月中秋山林涼", "八月中秋", "風吹大地", "山林涼", "草枝擺", "八月", "中秋", + "山林", "風吹", "大地", "草枝", "涼", "擺", "涼", "擺", "涼", "擺", "涼", "擺", + "涼", "擺", "擺", "涼", + ] + + func testPoolHorizontal() throws { + let pool = CandidatePool(candidates: testCandidates, columnCapacity: 8) + var strOutput = "" + pool.candidateRows.forEach { + $0.forEach { + strOutput += $0.displayedText + ", " + } + strOutput += "\n" + } + print("The matrix:") + print(strOutput) + } + + func testPoolVertical() throws { + let pool = CandidatePool(candidates: testCandidates, columnCapacity: 8) + var strOutput = "" + pool.candidateRows.forEach { + $0.forEach { + strOutput += $0.displayedText + ", " + } + strOutput += "\n" + } + print("The matrix:") + print(strOutput) + } +} diff --git a/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift b/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift new file mode 100644 index 00000000..74f57af4 --- /dev/null +++ b/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift @@ -0,0 +1,108 @@ +// (c) 2022 and onwards The vChewing Project (MIT-NTL 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 Cocoa + +// MARK: - Classes used by Candidate Window + +/// 用來管理選字窗內顯示的候選字的單位。用 class 型別會比較方便一些。 +public class CandidateCellData: Hashable { + public var locale = "" + public static var unifiedSize: Double = 16 + public static var highlightBackground: NSColor = { + if #available(macOS 10.14, *) { + return .selectedContentBackgroundColor + } + return NSColor.alternateSelectedControlColor + }() + + public var key: String + public var displayedText: String + public var size: Double { Self.unifiedSize } + public var isSelected: Bool = false + public var whichRow: Int = 0 + public var index: Int = 0 + public var subIndex: Int = 0 + + public var fontSizeCandidate: Double { CandidateCellData.unifiedSize } + public var fontSizeKey: Double { ceil(CandidateCellData.unifiedSize * 0.8) } + public var fontColorKey: NSColor { + isSelected ? .selectedMenuItemTextColor.withAlphaComponent(0.8) : .secondaryLabelColor + } + + public var fontColorCandidate: NSColor { isSelected ? .selectedMenuItemTextColor : .labelColor } + + public init(key: String, displayedText: String, isSelected: Bool = false) { + self.key = key + self.displayedText = displayedText + self.isSelected = isSelected + } + + public var cellLength: Int { + let rect = attributedString.boundingRect( + with: NSSize(width: 1600.0, height: 1600.0), + options: [.usesLineFragmentOrigin] + ) + let rawResult = ceil(rect.width + size / size) + return Int(rawResult) + } + + public var attributedStringHeader: NSAttributedString { + let paraStyleKey = NSMutableParagraphStyle() + paraStyleKey.setParagraphStyle(NSParagraphStyle.default) + paraStyleKey.alignment = .natural + let paraStyle = NSMutableParagraphStyle() + paraStyle.setParagraphStyle(NSParagraphStyle.default) + paraStyle.alignment = .natural + var attrKey: [NSAttributedString.Key: AnyObject] = [ + .font: NSFont.monospacedDigitSystemFont(ofSize: size * 0.7, weight: .regular), + .paragraphStyle: paraStyleKey, + ] + if isSelected { + attrKey[.foregroundColor] = NSColor.white.withAlphaComponent(0.8) + } else { + attrKey[.foregroundColor] = NSColor.secondaryLabelColor + } + let attrStrKey = NSMutableAttributedString(string: key, attributes: attrKey) + return attrStrKey + } + + public var attributedString: NSAttributedString { + let paraStyleKey = NSMutableParagraphStyle() + paraStyleKey.setParagraphStyle(NSParagraphStyle.default) + paraStyleKey.alignment = .natural + let paraStyle = NSMutableParagraphStyle() + paraStyle.setParagraphStyle(NSParagraphStyle.default) + paraStyle.alignment = .natural + var attrCandidate: [NSAttributedString.Key: AnyObject] = [ + .font: NSFont.monospacedDigitSystemFont(ofSize: size, weight: .regular), + .paragraphStyle: paraStyle, + ] + if isSelected { + attrCandidate[.foregroundColor] = NSColor.white + } else { + attrCandidate[.foregroundColor] = NSColor.labelColor + } + if #available(macOS 12, *) { + if UserDefaults.standard.bool(forKey: UserDef.kHandleDefaultCandidateFontsByLangIdentifier.rawValue) { + attrCandidate[.languageIdentifier] = self.locale as AnyObject + } + } + let attrStrCandidate = NSMutableAttributedString(string: displayedText, attributes: attrCandidate) + return attrStrCandidate + } + + public static func == (lhs: CandidateCellData, rhs: CandidateCellData) -> Bool { + lhs.key == rhs.key && lhs.displayedText == rhs.displayedText + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(key) + hasher.combine(displayedText) + } +} diff --git a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift index f7ab9800..b066f4c2 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift @@ -9,7 +9,7 @@ import Cocoa public protocol CtlCandidateDelegate: AnyObject { - func candidatePairs() -> [(String, String)] + func candidatePairs(conv: Bool) -> [(String, String)] func candidatePairAt(_ index: Int) -> (String, String) func candidatePairSelected(at index: Int) func buzz() @@ -17,21 +17,23 @@ public protocol CtlCandidateDelegate: AnyObject { } public protocol CtlCandidateProtocol { + var hint: String { get set } var locale: String { get set } - var currentLayout: CandidateLayout { get set } + var currentLayout: NSUserInterfaceLayoutOrientation { get set } var delegate: CtlCandidateDelegate? { get set } var selectedCandidateIndex: Int { get set } var visible: Bool { get set } var windowTopLeftPoint: NSPoint { get set } - var keyLabels: [CandidateKeyLabel] { get set } + var keyLabels: [CandidateCellData] { get set } var keyLabelFont: NSFont { get set } var candidateFont: NSFont { get set } var tooltip: String { get set } var useLangIdentifier: Bool { get set } var showPageButtons: Bool { get set } - init(_ layout: CandidateLayout) + init(_ layout: NSUserInterfaceLayoutOrientation) func reloadData() + func updateDisplay() func showNextPage() -> Bool func showPreviousPage() -> Bool func highlightNextCandidate() -> Bool diff --git a/Packages/vChewing_Shared/Sources/Shared/Shared.swift b/Packages/vChewing_Shared/Sources/Shared/Shared.swift index 78c63280..d7ba66e8 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Shared.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Shared.swift @@ -103,23 +103,6 @@ public enum UserDef: String, CaseIterable { } } -// MARK: - Enums and Structs used by Candidate Window - -public enum CandidateLayout { - case horizontal - case vertical -} - -public struct CandidateKeyLabel { - public private(set) var key: String - public private(set) var displayedText: String - - public init(key: String, displayedText: String) { - self.key = key - self.displayedText = displayedText - } -} - // MARK: - Tooltip Color States public enum TooltipColorState { diff --git a/Source/Modules/KeyHandler_HandleCandidate.swift b/Source/Modules/KeyHandler_HandleCandidate.swift index b449d2bc..0c54ed88 100644 --- a/Source/Modules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/KeyHandler_HandleCandidate.swift @@ -133,6 +133,8 @@ extension KeyHandler { if !ctlCandidate.showPreviousPage() { errorCallback("1919810D") } + @unknown default: + break } return true } @@ -149,6 +151,8 @@ extension KeyHandler { if !ctlCandidate.showNextPage() { errorCallback("9244908D") } + @unknown default: + break } return true } @@ -165,6 +169,8 @@ extension KeyHandler { if !ctlCandidate.highlightPreviousCandidate() { errorCallback("ASD9908D") } + @unknown default: + break } return true } @@ -181,6 +187,8 @@ extension KeyHandler { if !ctlCandidate.highlightNextCandidate() { errorCallback("6B99908D") } + @unknown default: + break } return true } @@ -223,7 +231,7 @@ extension KeyHandler { (state.type == .ofAssociates) ? input.inputTextIgnoringModifiers ?? "" : input.text for j in 0.. [(String, String)] { - state.isCandidateContainer ? state.candidates : [] + func candidatePairs(conv: Bool = false) -> [(String, String)] { + if !state.isCandidateContainer { return [] } + if !conv { return state.candidates } + let convertedCandidates: [(String, String)] = state.candidates.map { theCandidatePair -> (String, String) in + let theCandidate = theCandidatePair.1 + let theConverted = ChineseConverter.kanjiConversionIfRequired(theCandidate) + let result = (theCandidate == theConverted) ? theCandidate : "\(theConverted)(\(theCandidate))" + return (theCandidatePair.0, result) + } + return convertedCandidates } func candidatePairAt(_ index: Int) -> (String, String) { diff --git a/Source/Modules/SessionCtl_HandleDisplay.swift b/Source/Modules/SessionCtl_HandleDisplay.swift index 574cdfc4..21b412c8 100644 --- a/Source/Modules/SessionCtl_HandleDisplay.swift +++ b/Source/Modules/SessionCtl_HandleDisplay.swift @@ -6,9 +6,9 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. +import CandidateWindow import NSAttributedTextView import Shared -import Voltaire // MARK: - Tooltip Display and Candidate Display Methods @@ -72,21 +72,20 @@ extension SessionCtl { func showCandidates() { guard let client = client() else { return } var isCandidateWindowVertical: Bool { - var candidates: [(String, String)] = .init() - if state.isCandidateContainer { - candidates = state.candidates - } + // var candidates: [(String, String)] = .init() + // if state.isCandidateContainer { candidates = state.candidates } if isVerticalTyping { return true } // 接下來的判斷並非適用於 IMK 選字窗,所以先插入排除語句。 - guard Self.ctlCandidateCurrent is CtlCandidateUniversal else { return false } + // guard Self.ctlCandidateCurrent is CtlCandidateUniversal else { return false } // 以上是通用情形。接下來決定橫排輸入時是否使用縱排選字窗。 // 因為在拿候選字陣列時已經排序過了,所以這裡不用再多排序。 // 測量每頁顯示候選字的累計總長度。如果太長的話就強制使用縱排候選字窗。 // 範例:「屬實牛逼」(會有一大串各種各樣的「鼠食牛Beer」的 emoji)。 - let maxCandidatesPerPage = PrefMgr.shared.candidateKeys.count - let firstPageCandidates = candidates[0.. Int(round(Double(maxCandidatesPerPage) * 1.8)) + // let maxCandidatesPerPage = PrefMgr.shared.candidateKeys.count + // let firstPageCandidates = candidates[0.. Int(round(Double(maxCandidatesPerPage) * 1.8)) // 上面這句如果是 true 的話,就會是縱排;反之則為橫排。 + return false } state.isVerticalCandidateWindow = (isCandidateWindowVertical || !PrefMgr.shared.useHorizontalCandidateList) @@ -99,14 +98,18 @@ extension SessionCtl { /// layoutCandidateView 在這裡無法起到糾正作用。 /// 該問題徹底解決的價值並不大,直接等到 macOS 10.x 全線淘汰之後用 SwiftUI 重寫選字窗吧。 - let candidateLayout: CandidateLayout = + let candidateLayout: NSUserInterfaceLayoutOrientation = ((isCandidateWindowVertical || !PrefMgr.shared.useHorizontalCandidateList) - ? CandidateLayout.vertical - : CandidateLayout.horizontal) + ? .vertical + : .horizontal) - Self.ctlCandidateCurrent = - PrefMgr.shared.useIMKCandidateWindow - ? CtlCandidateIMK(candidateLayout) : CtlCandidateUniversal(candidateLayout) + if #available(macOS 12, *) { + Self.ctlCandidateCurrent = + PrefMgr.shared.useIMKCandidateWindow + ? CtlCandidateIMK(candidateLayout) : CtlCandidateTDK(candidateLayout) + } else { + Self.ctlCandidateCurrent = CtlCandidateIMK(candidateLayout) + } // set the attributes for the candidate panel (which uses NSAttributedString) let textSize = PrefMgr.shared.candidateListTextSize @@ -132,7 +135,11 @@ extension SessionCtl { candidateKeys.count > 4 ? Array(candidateKeys) : Array(CandidateKey.defaultKeys) let keyLabelSuffix = state.type == .ofAssociates ? "^" : "" Self.ctlCandidateCurrent.keyLabels = keyLabels.map { - CandidateKeyLabel(key: String($0), displayedText: String($0) + keyLabelSuffix) + CandidateCellData(key: String($0), displayedText: String($0) + keyLabelSuffix) + } + + if state.type == .ofAssociates { + Self.ctlCandidateCurrent.hint = NSLocalizedString("Hold ⇧ to choose associates.", comment: "") } Self.ctlCandidateCurrent.delegate = self diff --git a/Source/Modules/SessionCtl_HandleEvent.swift b/Source/Modules/SessionCtl_HandleEvent.swift index 5b55c778..801c7242 100644 --- a/Source/Modules/SessionCtl_HandleEvent.swift +++ b/Source/Modules/SessionCtl_HandleEvent.swift @@ -196,6 +196,7 @@ extension SessionCtl { switch imkC.currentLayout { case .horizontal: _ = event.isShiftHold ? imkC.moveUp(self) : imkC.moveDown(self) case .vertical: _ = event.isShiftHold ? imkC.moveLeft(self) : imkC.moveRight(self) + @unknown default: break } return true } else if event.isSpace { diff --git a/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift index 40dce0b2..5257ab85 100644 --- a/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift +++ b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift @@ -11,10 +11,11 @@ import Shared /// 威注音自用的 IMKCandidates 型別。因為有用到 bridging header,所以無法弄成 Swift Package。 public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { + public var hint: String = "" public var showPageButtons: Bool = false public var locale: String = "" public var useLangIdentifier: Bool = false - public var currentLayout: CandidateLayout = .horizontal + public var currentLayout: NSUserInterfaceLayoutOrientation = .horizontal public static let defaultIMKSelectionKey: [UInt16: String] = [ 18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6", 26: "7", 28: "8", 25: "9", ] @@ -38,9 +39,9 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { } } - public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] + public var keyLabels: [CandidateCellData] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] .map { - CandidateKeyLabel(key: $0, displayedText: $0) + CandidateCellData(key: $0, displayedText: $0) } public var keyLabelFont = NSFont.monospacedDigitSystemFont( @@ -68,7 +69,7 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { var keyCount = 0 var displayedCandidates = [String]() - public func specifyLayout(_ layout: CandidateLayout = .horizontal) { + public func specifyLayout(_ layout: NSUserInterfaceLayoutOrientation = .horizontal) { currentLayout = layout switch currentLayout { case .horizontal: @@ -80,10 +81,14 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { } case .vertical: setPanelType(kIMKSingleColumnScrollingCandidatePanel) + @unknown default: + setPanelType(kIMKSingleRowSteppingCandidatePanel) } } - public required init(_ layout: CandidateLayout = .horizontal) { + public func updateDisplay() {} + + public required init(_ layout: NSUserInterfaceLayoutOrientation = .horizontal) { super.init(server: theServer, panelType: kIMKScrollingGridCandidatePanel) specifyLayout(layout) // 設為 true 表示先交給 ctlIME 處理 @@ -115,7 +120,7 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { private var pageCount: Int { guard let delegate = delegate else { return 0 } - let totalCount = delegate.candidatePairs().count + let totalCount = delegate.candidatePairs(conv: false).count let keyLabelCount = keyLabels.count return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0) } @@ -147,7 +152,7 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { public func candidateIndexAtKeyLabelIndex(_ index: Int) -> Int { guard let delegate = delegate else { return Int.max } let result = currentPageIndex * keyLabels.count + index - return result < delegate.candidatePairs().count ? result : Int.max + return result < delegate.candidatePairs(conv: false).count ? result : Int.max } public var selectedCandidateIndex: Int { diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 09705640..e8d6aee9 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -82,6 +82,7 @@ "Optimize Memorized Phrases" = "Optimize Memorized Phrases"; "Clear Memorized Phrases" = "Clear Memorized Phrases"; "Currency Numeral Output" = "Currency Numeral Output"; +"Hold ⇧ to choose associates." = "Hold ⇧ to choose associates."; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "CommonSymbols"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 09705640..e8d6aee9 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -82,6 +82,7 @@ "Optimize Memorized Phrases" = "Optimize Memorized Phrases"; "Clear Memorized Phrases" = "Clear Memorized Phrases"; "Currency Numeral Output" = "Currency Numeral Output"; +"Hold ⇧ to choose associates." = "Hold ⇧ to choose associates."; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "CommonSymbols"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index 3ce6f421..a442a596 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -82,6 +82,7 @@ "Optimize Memorized Phrases" = "臨時記憶資料を整う"; "Clear Memorized Phrases" = "臨時記憶資料を削除"; "Currency Numeral Output" = "数字大字変換"; +"Hold ⇧ to choose associates." = "⇧を押しながら連想候補をご選択ください。"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index a79cc57d..f9c74258 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -82,6 +82,7 @@ "Optimize Memorized Phrases" = "精简临时记忆语汇资料"; "Clear Memorized Phrases" = "清除临时记忆语汇资料"; "Currency Numeral Output" = "大写汉字数字输出"; +"Hold ⇧ to choose associates." = "摁住⇧以选取联想词。"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 23a3f5db..350b2751 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -82,6 +82,7 @@ "Optimize Memorized Phrases" = "精簡臨時記憶語彙資料"; "Clear Memorized Phrases" = "清除臨時記憶語彙資料"; "Currency Numeral Output" = "大寫漢字數字輸出"; +"Hold ⇧ to choose associates." = "摁住⇧以選取聯想詞。"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; From e5364ce06a39e2cecc3e536ac74a08f34c3d47c2 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 28 Sep 2022 23:26:57 +0800 Subject: [PATCH 11/23] CtlCandidateTDK // Performance boost. --- Packages/vChewing_CandidateWindow/README.md | 1 + .../CandidateWindow/CandidatePool.swift | 10 +++-- .../CandidateWindow/CtlCandidateTDK.swift | 22 +++++----- .../CandidateWindow/VwrCandidateTDK.swift | 40 +++++++++---------- .../Sources/Shared/CandidateBasicUnits.swift | 14 +++++-- .../Protocols/CtlCandidateProtocol.swift | 3 +- Source/Modules/SessionCtl_Delegates.swift | 12 +++--- Source/Modules/SessionCtl_HandleDisplay.swift | 3 +- 8 files changed, 56 insertions(+), 49 deletions(-) diff --git a/Packages/vChewing_CandidateWindow/README.md b/Packages/vChewing_CandidateWindow/README.md index e929a70b..31da3503 100644 --- a/Packages/vChewing_CandidateWindow/README.md +++ b/Packages/vChewing_CandidateWindow/README.md @@ -9,6 +9,7 @@ TDK 選字窗以純 SwiftUI 構築,用以取代此前自上游繼承來的 Vol 然而,TDK 選字窗目前有下述侷限: - 因 SwiftUI 自身特性所導致的嚴重的效能問題。基本上來講,如果您經常使用全字庫模式的話,請在偏好設定內啟用效能更高的 IMK 選字窗。 +- 同樣出於上述原因,為了讓田所選字窗至少處於可在生產力環境下正常使用的狀態,就犧牲了捲動檢視的功能。也就是說,每次只顯示六行,但顯示內容則隨著使用者的游標操作而更新。 - TDK 選字窗目前僅完成了橫版矩陣陳列模式的實作,且尚未引入對縱排選字窗陳列佈局的支援。 因為這些問題恐怕需要很久才能全部解決,所以威注音會在這段時間內推薦使用者們優先使用 IMK 選字窗。 diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift index dcdae5b8..2c090641 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift @@ -9,8 +9,8 @@ import Cocoa import Shared -/// 候選字窗會用到的資料池單位。用 class 型別會更方便一些。 -public class CandidatePool { +/// 候選字窗會用到的資料池單位。 +public struct CandidatePool { public var currentRowNumber = 0 public private(set) var selectionKeys: String public private(set) var highlightedIndex: Int = 0 @@ -24,6 +24,8 @@ public class CandidatePool { ceil(Double(maxColumnCapacity + 3) * 2.7 * ceil(CandidateCellData.unifiedSize) * 1.2) } + public var rangeForCurrentPage: Range { currentRowNumber.. Bool { diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift index adbbda0e..45a32632 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift @@ -21,7 +21,7 @@ struct CandidatePoolViewUI_Previews: PreviewProvider { "吹", "大", "地", "草", "枝", "擺", ] static var thePool: CandidatePool { - let result = CandidatePool(candidates: testCandidates, columnCapacity: 6) + var result = CandidatePool(candidates: testCandidates, columnCapacity: 6) // 下一行待解決:無論這裡怎麼指定高亮選中項是哪一筆,其所在行都得被卷動到使用者眼前。 result.highlight(at: 14) return result @@ -50,28 +50,24 @@ public struct VwrCandidateTDK: View { public var body: some View { VStack(alignment: .leading, spacing: 0) { - ScrollViewReader { proxy in - ScrollView(.vertical, showsIndicators: true) { - VStack(alignment: .leading, spacing: 1.6) { - ForEach(thePool.candidateRows.indices, id: \.self) { columnIndex in - HStack(spacing: 10) { - ForEach(Array(thePool.candidateRows[columnIndex]), id: \.self) { currentCandidate in - currentCandidate.attributedStringForSwiftUI.fixedSize() - .frame(maxWidth: .infinity, alignment: .topLeading) - .contentShape(Rectangle()) - .onTapGesture { didSelectCandidateAt(currentCandidate.index) } - } - Spacer() - }.frame( - minWidth: 0, - maxWidth: .infinity, - alignment: .topLeading - ).id(columnIndex) - Divider() - } + ScrollView(.vertical, showsIndicators: true) { + VStack(alignment: .leading, spacing: 1.6) { + ForEach(thePool.rangeForCurrentPage, id: \.self) { columnIndex in + HStack(spacing: 10) { + ForEach(Array(thePool.candidateRows[columnIndex]), id: \.self) { currentCandidate in + currentCandidate.attributedStringForSwiftUI.fixedSize() + .frame(maxWidth: .infinity, alignment: .topLeading) + .contentShape(Rectangle()) + .onTapGesture { didSelectCandidateAt(currentCandidate.index) } + } + Spacer() + }.frame( + minWidth: 0, + maxWidth: .infinity, + alignment: .topLeading + ).id(columnIndex) + Divider() } - }.onAppear { - proxy.scrollTo(thePool.currentRowNumber) } } .frame(minHeight: thePool.maxWindowHeight, maxHeight: thePool.maxWindowHeight).padding(5) diff --git a/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift b/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift index 74f57af4..deacf56c 100644 --- a/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift +++ b/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift @@ -44,9 +44,9 @@ public class CandidateCellData: Hashable { } public var cellLength: Int { - let rect = attributedString.boundingRect( - with: NSSize(width: 1600.0, height: 1600.0), - options: [.usesLineFragmentOrigin] + if displayedText.count <= 2 { return Int(ceil(size * 3)) } + let rect = attributedStringForLengthCalculation.boundingRect( + with: NSSize(width: 1600.0, height: 1600.0), options: [.usesLineFragmentOrigin] ) let rawResult = ceil(rect.width + size / size) return Int(rawResult) @@ -72,6 +72,14 @@ public class CandidateCellData: Hashable { return attrStrKey } + public var attributedStringForLengthCalculation: NSAttributedString { + let attrCandidate: [NSAttributedString.Key: AnyObject] = [ + .font: NSFont.monospacedDigitSystemFont(ofSize: size, weight: .regular) + ] + let attrStrCandidate = NSMutableAttributedString(string: displayedText + " ", attributes: attrCandidate) + return attrStrCandidate + } + public var attributedString: NSAttributedString { let paraStyleKey = NSMutableParagraphStyle() paraStyleKey.setParagraphStyle(NSParagraphStyle.default) diff --git a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift index b066f4c2..358e986a 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift @@ -12,8 +12,9 @@ public protocol CtlCandidateDelegate: AnyObject { func candidatePairs(conv: Bool) -> [(String, String)] func candidatePairAt(_ index: Int) -> (String, String) func candidatePairSelected(at index: Int) + func candidates(_ sender: Any!) -> [Any]! func buzz() - func kanjiConversionIfRequired(_ target: String) -> String + var selectionKeys: String { get } } public protocol CtlCandidateProtocol { diff --git a/Source/Modules/SessionCtl_Delegates.swift b/Source/Modules/SessionCtl_Delegates.swift index 1255bb1d..05df8620 100644 --- a/Source/Modules/SessionCtl_Delegates.swift +++ b/Source/Modules/SessionCtl_Delegates.swift @@ -46,17 +46,17 @@ extension SessionCtl: KeyHandlerDelegate { // MARK: - Candidate Controller Delegate extension SessionCtl: CtlCandidateDelegate { + var selectionKeys: String { PrefMgr.shared.candidateKeys } + func buzz() { IMEApp.buzz() } - func kanjiConversionIfRequired(_ target: String) -> String { - ChineseConverter.kanjiConversionIfRequired(target) - } - func candidatePairs(conv: Bool = false) -> [(String, String)] { - if !state.isCandidateContainer { return [] } - if !conv { return state.candidates } + if !state.isCandidateContainer || state.candidates.isEmpty { return [] } + if !conv || PrefMgr.shared.cns11643Enabled || state.candidates[0].0.contains("_punctuation") { + return state.candidates + } let convertedCandidates: [(String, String)] = state.candidates.map { theCandidatePair -> (String, String) in let theCandidate = theCandidatePair.1 let theConverted = ChineseConverter.kanjiConversionIfRequired(theCandidate) diff --git a/Source/Modules/SessionCtl_HandleDisplay.swift b/Source/Modules/SessionCtl_HandleDisplay.swift index 21b412c8..a0ff1734 100644 --- a/Source/Modules/SessionCtl_HandleDisplay.swift +++ b/Source/Modules/SessionCtl_HandleDisplay.swift @@ -142,7 +142,6 @@ extension SessionCtl { Self.ctlCandidateCurrent.hint = NSLocalizedString("Hold ⇧ to choose associates.", comment: "") } - Self.ctlCandidateCurrent.delegate = self Self.ctlCandidateCurrent.showPageButtons = PrefMgr.shared.showPageButtonsInCandidateWindow Self.ctlCandidateCurrent.useLangIdentifier = PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier Self.ctlCandidateCurrent.locale = { @@ -156,7 +155,6 @@ extension SessionCtl { default: return "" } }() - Self.ctlCandidateCurrent.reloadData() if #available(macOS 10.14, *) { // Spotlight 視窗會擋住 IMK 選字窗,所以需要特殊處理。 @@ -167,6 +165,7 @@ extension SessionCtl { } } + Self.ctlCandidateCurrent.delegate = self Self.ctlCandidateCurrent.visible = true if isVerticalTyping { From 2259167415c456c79bdc497a34ad25b594333e7e Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 28 Sep 2022 23:56:12 +0800 Subject: [PATCH 12/23] CtlCandidateTDK // DIfferentiate page flipping and row flipping. --- .../CandidateWindow/CtlCandidateTDK.swift | 18 ++++++- .../Modules/KeyHandler_HandleCandidate.swift | 47 ++++++++++++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidateTDK.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidateTDK.swift index 3a6653bb..c434d82d 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidateTDK.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidateTDK.swift @@ -65,12 +65,28 @@ public class CtlCandidateTDK: CtlCandidate { } @discardableResult override public func showNextPage() -> Bool { - thePool.selectNewNeighborRow(direction: .down) + for _ in 0..<6 { + thePool.selectNewNeighborRow(direction: .down) + } updateDisplay() return true } @discardableResult override public func showPreviousPage() -> Bool { + for _ in 0..<6 { + thePool.selectNewNeighborRow(direction: .up) + } + updateDisplay() + return true + } + + @discardableResult public func showNextLine() -> Bool { + thePool.selectNewNeighborRow(direction: .down) + updateDisplay() + return true + } + + @discardableResult public func showPreviousLine() -> Bool { thePool.selectNewNeighborRow(direction: .up) updateDisplay() return true diff --git a/Source/Modules/KeyHandler_HandleCandidate.swift b/Source/Modules/KeyHandler_HandleCandidate.swift index 0c54ed88..d70e10d6 100644 --- a/Source/Modules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/KeyHandler_HandleCandidate.swift @@ -8,6 +8,7 @@ /// 該檔案乃按鍵調度模組當中「用來規定在選字窗出現時的按鍵行為」的部分。 +import CandidateWindow import Shared // MARK: - § 對選字狀態進行調度 (Handle Candidate State). @@ -162,8 +163,21 @@ extension KeyHandler { if input.isUp { switch ctlCandidate.currentLayout { case .horizontal: - if !ctlCandidate.showPreviousPage() { - errorCallback("9B614524") + if #available(macOS 12, *) { + if let ctlCandidate = ctlCandidate as? CtlCandidateTDK { + ctlCandidate.showPreviousLine() + break + } else { + if !ctlCandidate.showPreviousPage() { + errorCallback("9B614524") + break + } + } + } else { + if !ctlCandidate.showPreviousPage() { + errorCallback("9B614524") + break + } } case .vertical: if !ctlCandidate.highlightPreviousCandidate() { @@ -180,8 +194,21 @@ extension KeyHandler { if input.isDown { switch ctlCandidate.currentLayout { case .horizontal: - if !ctlCandidate.showNextPage() { - errorCallback("92B990DD") + if #available(macOS 12, *) { + if let ctlCandidate = ctlCandidate as? CtlCandidateTDK { + ctlCandidate.showNextLine() + break + } else { + if !ctlCandidate.showNextPage() { + errorCallback("92B990DD") + break + } + } + } else { + if !ctlCandidate.showNextPage() { + errorCallback("92B990DD") + break + } } case .vertical: if !ctlCandidate.highlightNextCandidate() { @@ -294,8 +321,16 @@ extension KeyHandler { // MARK: - Flipping pages by using symbol menu keys (when they are not occupied). if input.isSymbolMenuPhysicalKey { - let updated: Bool = - input.isShiftHold ? ctlCandidate.showPreviousPage() : ctlCandidate.showNextPage() + var updated = true + if #available(macOS 12, *) { + if let ctlCandidate = ctlCandidate as? CtlCandidateTDK { + updated = input.isShiftHold ? ctlCandidate.showPreviousLine() : ctlCandidate.showNextLine() + } else { + updated = input.isShiftHold ? ctlCandidate.showPreviousPage() : ctlCandidate.showNextPage() + } + } else { + updated = input.isShiftHold ? ctlCandidate.showPreviousPage() : ctlCandidate.showNextPage() + } if !updated { errorCallback("66F3477B") } From b0cbd4008ebf2a286b62e38dcd2404633463c002 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Thu, 29 Sep 2022 11:21:29 +0800 Subject: [PATCH 13/23] CtlCandidateTDK // Limit rows per page to 3. --- Packages/vChewing_CandidateWindow/README.md | 2 +- .../Sources/CandidateWindow/CandidatePool.swift | 13 ++++++++----- .../Sources/CandidateWindow/CtlCandidateTDK.swift | 4 ++-- .../Sources/CandidateWindow/VwrCandidateTDK.swift | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Packages/vChewing_CandidateWindow/README.md b/Packages/vChewing_CandidateWindow/README.md index 31da3503..f4370531 100644 --- a/Packages/vChewing_CandidateWindow/README.md +++ b/Packages/vChewing_CandidateWindow/README.md @@ -9,7 +9,7 @@ TDK 選字窗以純 SwiftUI 構築,用以取代此前自上游繼承來的 Vol 然而,TDK 選字窗目前有下述侷限: - 因 SwiftUI 自身特性所導致的嚴重的效能問題。基本上來講,如果您經常使用全字庫模式的話,請在偏好設定內啟用效能更高的 IMK 選字窗。 -- 同樣出於上述原因,為了讓田所選字窗至少處於可在生產力環境下正常使用的狀態,就犧牲了捲動檢視的功能。也就是說,每次只顯示六行,但顯示內容則隨著使用者的游標操作而更新。 +- 同樣出於上述原因,為了讓田所選字窗至少處於可在生產力環境下正常使用的狀態,就犧牲了捲動檢視的功能。也就是說,每次只顯示三行,但顯示內容則隨著使用者的游標操作而更新。 - TDK 選字窗目前僅完成了橫版矩陣陳列模式的實作,且尚未引入對縱排選字窗陳列佈局的支援。 因為這些問題恐怕需要很久才能全部解決,所以威注音會在這段時間內推薦使用者們優先使用 IMK 選字窗。 diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift index 2c090641..31d67658 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift @@ -10,21 +10,24 @@ import Cocoa import Shared /// 候選字窗會用到的資料池單位。 -public struct CandidatePool { +public class CandidatePool { public var currentRowNumber = 0 + public var maximumLinesPerPage = 3 public private(set) var selectionKeys: String public private(set) var highlightedIndex: Int = 0 public private(set) var maxColumnCapacity: Int = 6 public private(set) var candidateDataAll: [CandidateCellData] = [] public private(set) var candidateRows: [[CandidateCellData]] = [] - public var maxWindowHeight: Double { ceil(maxWindowWidth * 0.4) } public var isVerticalLayout: Bool { maxColumnCapacity == 1 } public var maxColumnWidth: Int { Int(Double(maxColumnCapacity + 3) * 2) * Int(ceil(CandidateCellData.unifiedSize)) } + public var maxWindowHeight: Double { ceil(maxWindowWidth * 0.4 / 2) } public var maxWindowWidth: Double { ceil(Double(maxColumnCapacity + 3) * 2.7 * ceil(CandidateCellData.unifiedSize) * 1.2) } - public var rangeForCurrentPage: Range { currentRowNumber.. { + currentRowNumber.. Bool { - for _ in 0..<6 { + for _ in 0.. Bool { - for _ in 0..<6 { + for _ in 0.. Date: Thu, 29 Sep 2022 13:31:39 +0800 Subject: [PATCH 14/23] CtlCandidateTDK // Auto-adjust window height. --- .../Sources/CandidateWindow/CandidatePool.swift | 4 +++- .../CandidateWindow/VwrCandidateTDK.swift | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift index 31d67658..e0a6fd23 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift @@ -11,6 +11,7 @@ import Shared /// 候選字窗會用到的資料池單位。 public class CandidatePool { + public let blankCell = CandidateCellData(key: " ", displayedText: " ", isSelected: false) public var currentRowNumber = 0 public var maximumLinesPerPage = 3 public private(set) var selectionKeys: String @@ -20,7 +21,6 @@ public class CandidatePool { public private(set) var candidateRows: [[CandidateCellData]] = [] public var isVerticalLayout: Bool { maxColumnCapacity == 1 } public var maxColumnWidth: Int { Int(Double(maxColumnCapacity + 3) * 2) * Int(ceil(CandidateCellData.unifiedSize)) } - public var maxWindowHeight: Double { ceil(maxWindowWidth * 0.4 / 2) } public var maxWindowWidth: Double { ceil(Double(maxColumnCapacity + 3) * 2.7 * ceil(CandidateCellData.unifiedSize) * 1.2) } @@ -29,6 +29,8 @@ public class CandidatePool { currentRowNumber.. { 0..<(maximumLinesPerPage - rangeForCurrentPage.count) } + public enum VerticalDirection { case up case down diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift index e5bc40d9..414d156c 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift @@ -68,9 +68,24 @@ public struct VwrCandidateTDK: View { ).id(columnIndex) Divider() } + if thePool.maximumLinesPerPage - thePool.rangeForCurrentPage.count > 0 { + ForEach(thePool.rangeForLastPageBlanked, id: \.self) { _ in + HStack(spacing: 0) { + thePool.blankCell.attributedStringForSwiftUI.fixedSize() + .frame(maxWidth: .infinity, alignment: .topLeading) + .contentShape(Rectangle()) + Spacer() + }.frame( + minWidth: 0, + maxWidth: .infinity, + alignment: .topLeading + ) + Divider() + } + } } } - .frame(minHeight: thePool.maxWindowHeight, maxHeight: thePool.maxWindowHeight).padding(5) + .fixedSize(horizontal: false, vertical: true).padding(5) .background(Color(nsColor: NSColor.controlBackgroundColor).ignoresSafeArea()) HStack(alignment: .bottom) { Text(hint).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold)).lineLimit(1) From 4b16d7dda71a473b31ee6214e899345a14f508f2 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 28 Sep 2022 22:09:13 +0800 Subject: [PATCH 15/23] PrefWindow // Disable Voltaire-related settings. --- Source/Modules/WindowControllers/ctlPrefWindow.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Modules/WindowControllers/ctlPrefWindow.swift b/Source/Modules/WindowControllers/ctlPrefWindow.swift index 37856bdf..0272d84e 100644 --- a/Source/Modules/WindowControllers/ctlPrefWindow.swift +++ b/Source/Modules/WindowControllers/ctlPrefWindow.swift @@ -135,6 +135,7 @@ class ctlPrefWindow: NSWindowController { } selectionKeyComboBox.stringValue = candidateSelectionKeys + selectionKeyComboBox.isEnabled = false // 無法與 IMKCandidates 協作,故禁用。 } // 這裡有必要加上這段處理,用來確保藉由偏好設定介面動過的 CNS 開關能夠立刻生效。 @@ -281,8 +282,9 @@ extension ctlPrefWindow: NSToolbarDelegate { } var toolbarIdentifiers: [NSToolbarItem.Identifier] { - if #unavailable(macOS 10.13) { return [.ofGeneral, .ofExperience, .ofDictionary, .ofKeyboard] } - return [.ofGeneral, .ofExperience, .ofDictionary, .ofKeyboard, .ofDevZone] + // if #unavailable(macOS 10.13) { return [.ofGeneral, .ofExperience, .ofDictionary, .ofKeyboard] } + // return [.ofGeneral, .ofExperience, .ofDictionary, .ofKeyboard, .ofDevZone] + [.ofGeneral, .ofExperience, .ofDictionary, .ofKeyboard] } func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { From e66476e7a5d128c0d8fd84242688b825de7dfc0f Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 28 Sep 2022 22:24:31 +0800 Subject: [PATCH 16/23] Prefs // Update settings regarding candidate window, etc. --- Source/Modules/PrefMgr_Core.swift | 2 -- Source/Modules/PrefMgr_Extension.swift | 2 +- .../UIModules/PrefUI/suiPrefPaneDevZone.swift | 30 ++++++++++--------- .../UIModules/PrefUI/suiPrefPaneGeneral.swift | 20 +++++++------ .../Resources/Base.lproj/Localizable.strings | 7 +++-- Source/Resources/en.lproj/Localizable.strings | 7 +++-- Source/Resources/ja.lproj/Localizable.strings | 7 +++-- .../zh-Hans.lproj/Localizable.strings | 7 +++-- .../zh-Hant.lproj/Localizable.strings | 7 +++-- .../WindowNIBs/Base.lproj/frmPrefWindow.xib | 7 +++-- .../WindowNIBs/en.lproj/frmPrefWindow.strings | 4 +-- 11 files changed, 54 insertions(+), 46 deletions(-) diff --git a/Source/Modules/PrefMgr_Core.swift b/Source/Modules/PrefMgr_Core.swift index 9a49fb51..47051530 100644 --- a/Source/Modules/PrefMgr_Core.swift +++ b/Source/Modules/PrefMgr_Core.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) diff --git a/Source/Modules/PrefMgr_Extension.swift b/Source/Modules/PrefMgr_Extension.swift index 1e3dedd8..5f52af93 100644 --- a/Source/Modules/PrefMgr_Extension.swift +++ b/Source/Modules/PrefMgr_Extension.swift @@ -15,7 +15,7 @@ extension PrefMgr { // 防呆。macOS 10.11 用 IMK 選字窗會崩潰,macOS 10.13 的 IMK 選字窗仍有問題。 // 一般人想用的 IMK 選字窗基於 macOS 10.09 系統內建的注音輸入法的那種矩陣選字窗。 // 然而,該選字窗的體驗直到 macOS 10.14 開始才在 IMKCandidates 當中正式提供。 - if #unavailable(macOS 10.13) { useIMKCandidateWindow = false } + if #unavailable(macOS 12) { useIMKCandidateWindow = true } if #unavailable(macOS 10.15) { handleDefaultCandidateFontsByLangIdentifier = false shiftKeyAccommodationBehavior = 0 diff --git a/Source/Modules/UIModules/PrefUI/suiPrefPaneDevZone.swift b/Source/Modules/UIModules/PrefUI/suiPrefPaneDevZone.swift index 454d3716..3c0851f9 100644 --- a/Source/Modules/UIModules/PrefUI/suiPrefPaneDevZone.swift +++ b/Source/Modules/UIModules/PrefUI/suiPrefPaneDevZone.swift @@ -50,20 +50,22 @@ struct suiPrefPaneDevZone: View { ) .fixedSize(horizontal: false, vertical: true) Divider() - Toggle( - LocalizedStringKey("Use IMK Candidate Window instead (will reboot the IME)"), - isOn: $selUseIMKCandidateWindow.onChange { - PrefMgr.shared.useIMKCandidateWindow = selUseIMKCandidateWindow - NSLog("vChewing App self-terminated due to enabling / disabling IMK candidate window.") - NSApplication.shared.terminate(nil) - } - ) - Text( - LocalizedStringKey( - "IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases." + if #available(macOS 12, *) { + Toggle( + LocalizedStringKey("Use IMK Candidate Window instead of Tadokoro (will reboot the IME)"), + isOn: $selUseIMKCandidateWindow.onChange { + PrefMgr.shared.useIMKCandidateWindow = selUseIMKCandidateWindow + NSLog("vChewing App self-terminated due to enabling / disabling IMK candidate window.") + NSApplication.shared.terminate(nil) + } ) - ) - .preferenceDescription().fixedSize(horizontal: false, vertical: true) + Text( + LocalizedStringKey( + "IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements." + ) + ) + .preferenceDescription().fixedSize(horizontal: false, vertical: true) + } Toggle( LocalizedStringKey("Use .langIdentifier to handle UI fonts in candidate window"), isOn: $selHandleDefaultCandidateFontsByLangIdentifier.onChange { @@ -74,7 +76,7 @@ struct suiPrefPaneDevZone: View { .disabled(!isMontereyOrAbove) Text( LocalizedStringKey( - "This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later." + "This only works with Tadokoro candidate window." ) ) .preferenceDescription().fixedSize(horizontal: false, vertical: true) diff --git a/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift b/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift index 1908f4dd..5a344ed7 100644 --- a/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift +++ b/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift @@ -129,16 +129,18 @@ struct suiPrefPaneGeneral: View { .labelsHidden() .horizontalRadioGroupLayout() .pickerStyle(RadioGroupPickerStyle()) - Text(LocalizedStringKey("Choose your preferred layout of the candidate window.")) + .disabled(!PrefMgr.shared.useIMKCandidateWindow) + if PrefMgr.shared.useIMKCandidateWindow { + Text(LocalizedStringKey("Choose your preferred layout of the candidate window.")) + .preferenceDescription() + } else { + Text( + LocalizedStringKey( + "Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." + ) + ) .preferenceDescription() - Toggle( - LocalizedStringKey("Show page buttons in candidate window"), - isOn: $selShowPageButtonsInCandidateUI.onChange { - PrefMgr.shared.showPageButtonsInCandidateWindow = selShowPageButtonsInCandidateUI - } - ) - .controlSize(.small) - .disabled(PrefMgr.shared.useIMKCandidateWindow) + } } Preferences.Section(label: { Text(LocalizedStringKey("Output Settings:")) }) { Toggle( diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index e8d6aee9..61bbdeed 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -185,7 +185,7 @@ "Hsu" = "Hsu"; "Hualuo Pinyin with Numeral Intonation" = "Hualuo Pinyin with Numeral Intonation"; "IBM" = "IBM"; -"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases." = "IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases."; +"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements." = "IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements."; "in front of the phrase (like macOS built-in Zhuyin IME)" = "in front of the phrase (like macOS built-in Zhuyin IME)"; "Intonation Key:" = "Intonation Key:"; "Japanese" = "Japanese"; @@ -217,7 +217,8 @@ "Specify the behavior of intonation key when syllable composer is empty." = "Specify the behavior of intonation key when syllable composer is empty."; "Starlight" = "Starlight"; "Stop farting (when typed phonetic combination is invalid, etc.)" = "Stop farting (when typed phonetic combination is invalid, etc.)"; -"This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later." = "This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later."; +"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window."; +"This only works with Tadokoro candidate window." = "This only works with Tadokoro candidate window."; "Traditional Chinese" = "Traditional Chinese"; "Trim unfinished readings on commit" = "Trim unfinished readings on commit"; "Type them into inline composition buffer" = "Type them into inline composition buffer"; @@ -226,7 +227,7 @@ "Universal Pinyin with Numeral Intonation" = "Universal Pinyin with Numeral Intonation"; "Use .langIdentifier to handle UI fonts in candidate window" = "Use .langIdentifier to handle UI fonts in candidate window"; "Use ESC key to clear the entire input buffer" = "Use ESC key to clear the entire input buffer"; -"Use IMK Candidate Window instead (will reboot the IME)" = "Use IMK Candidate Window instead (will reboot the IME)"; +"Use IMK Candidate Window instead of Tadokoro (will reboot the IME)" = "Use IMK Candidate Window instead of Tadokoro (will reboot the IME)"; "Use Shift key accommodation in all cases" = "Use Shift key accommodation in all cases"; "Vertical" = "Vertical"; "Warning: This page is for testing future features. \nFeatures listed here may not work as expected." = "Warning: This page is for testing future features. \nFeatures listed here may not work as expected."; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index e8d6aee9..61bbdeed 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -185,7 +185,7 @@ "Hsu" = "Hsu"; "Hualuo Pinyin with Numeral Intonation" = "Hualuo Pinyin with Numeral Intonation"; "IBM" = "IBM"; -"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases." = "IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases."; +"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements." = "IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements."; "in front of the phrase (like macOS built-in Zhuyin IME)" = "in front of the phrase (like macOS built-in Zhuyin IME)"; "Intonation Key:" = "Intonation Key:"; "Japanese" = "Japanese"; @@ -217,7 +217,8 @@ "Specify the behavior of intonation key when syllable composer is empty." = "Specify the behavior of intonation key when syllable composer is empty."; "Starlight" = "Starlight"; "Stop farting (when typed phonetic combination is invalid, etc.)" = "Stop farting (when typed phonetic combination is invalid, etc.)"; -"This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later." = "This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later."; +"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window."; +"This only works with Tadokoro candidate window." = "This only works with Tadokoro candidate window."; "Traditional Chinese" = "Traditional Chinese"; "Trim unfinished readings on commit" = "Trim unfinished readings on commit"; "Type them into inline composition buffer" = "Type them into inline composition buffer"; @@ -226,7 +227,7 @@ "Universal Pinyin with Numeral Intonation" = "Universal Pinyin with Numeral Intonation"; "Use .langIdentifier to handle UI fonts in candidate window" = "Use .langIdentifier to handle UI fonts in candidate window"; "Use ESC key to clear the entire input buffer" = "Use ESC key to clear the entire input buffer"; -"Use IMK Candidate Window instead (will reboot the IME)" = "Use IMK Candidate Window instead (will reboot the IME)"; +"Use IMK Candidate Window instead of Tadokoro (will reboot the IME)" = "Use IMK Candidate Window instead of Tadokoro (will reboot the IME)"; "Use Shift key accommodation in all cases" = "Use Shift key accommodation in all cases"; "Vertical" = "Vertical"; "Warning: This page is for testing future features. \nFeatures listed here may not work as expected." = "Warning: This page is for testing future features. \nFeatures listed here may not work as expected."; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index a442a596..42dae03e 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -185,7 +185,7 @@ "Hsu" = "許氏国音自然配列"; "Hualuo Pinyin with Numeral Intonation" = "中華ローマ弁音 (ローマ字+数字音調)"; "IBM" = "IBM 配列"; -"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases." = "IMK 候補陳列ウィンドウは、Apple の未公開のAPIを「bridging-header」という強引的な手段で引き出して利用した機能である。現時点で macOS 10.14 Mojave から macOS 13 Ventura まで利用可能であるが、その後の新しい macOS との互換性の話はまだ早い。"; +"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements." = "IMK 候補陳列ウィンドウは、Apple の未公開のAPIを「bridging-header」という強引的な手段で引き出して利用した機能である。現時点で macOS 10.14 Mojave から macOS 13 Ventura まで利用可能であるが、その後の新しい macOS との互換性の話はまだ早い。とはいえ、田所候補陳列ウィンドウにはまだ改善できるところがあるため、IMK 候補陳列ウィンドウは現時点で推奨選択とする。"; "in front of the phrase (like macOS built-in Zhuyin IME)" = "単語の前で // macOS 内蔵注音入力のやり方"; "Intonation Key:" = "音調キー:"; "Japanese" = "和語"; @@ -217,7 +217,8 @@ "Specify the behavior of intonation key when syllable composer is empty." = "音読組立緩衝列が空かされた時の音調キーの行為をご指定ください。"; "Starlight" = "星光配列"; "Stop farting (when typed phonetic combination is invalid, etc.)" = "マナーモード // 外すと入力間違った時に変な声が出る"; -"This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later." = "これは Apple Bug Report #FB10978412 の臨時対策であり、macOS 12 からの macOS に効き、IMK 以外の候補陳列ウィンドウに作用する。Apple は macOS 11 からの macOS のために該当 Bug を修復すべきである。"; +"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "田所候補陳列ウィンドウは格子状横型陳列しかできません。開発道場のページで IMK 候補陳列ウィンドウを起用してから、今のこのページで縦型・横型陳列の選択はできます。"; +"This only works with Tadokoro candidate window." = "これは田所候補陳列ウィンドウだけに効ける機能である。"; "Traditional Chinese" = "繁体中国語"; "Trim unfinished readings on commit" = "送り出す緩衝列内容から未完成な音読みを除く"; "Type them into inline composition buffer" = "入力緩衝列にローマ字入力"; @@ -226,7 +227,7 @@ "Universal Pinyin with Numeral Intonation" = "汎用弁音 (ローマ字+数字音調)"; "Use .langIdentifier to handle UI fonts in candidate window" = "「.langIdentifier」を使って候補陳列ウィンドウのフォントを取り扱う"; "Use ESC key to clear the entire input buffer" = "ESC キーで入力緩衝列を消す"; -"Use IMK Candidate Window instead (will reboot the IME)" = "IMK 候補陳列ウィンドウを起用(入力アプリは自動的に再起動)"; +"Use IMK Candidate Window instead of Tadokoro (will reboot the IME)" = "IMK 候補陳列ウィンドウを起用(入力アプリは自動的に再起動)"; "Use Shift key accommodation in all cases" = "いずれの客体アプリにも Shift キーの互換性措置を起用"; "Vertical" = "縦型陳列"; "Warning: This page is for testing future features. \nFeatures listed here may not work as expected." = "警告:これからの新機能テストのために作ったページですから、\nここで陳列されている諸機能は予想通り動けるだと思わないでください。"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index f9c74258..c59fc1a9 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -185,7 +185,7 @@ "Hsu" = "许氏国音自然排列"; "Hualuo Pinyin with Numeral Intonation" = "华罗拼音+数字标调"; "IBM" = "IBM 排列"; -"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases." = "IMK 选字窗依赖于 Apple 的私有 API,而且是借由桥接档案头强制曝露的方法使用的。目前,该功能仅在 macOS 10.14 Mojave 至 macOS 13 Ventura 系统内有测试过可用性。至于在未来的 macOS 发行版当中是否可用,则需要另行测试、才能下结论。"; +"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements." = "IMK 选字窗依赖于 Apple 的私有 API,而且是借由桥接档案头强制曝露的方法使用的。目前,该功能仅在 macOS 10.14 Mojave 至 macOS 13 Ventura 系统内有测试过可用性。至于在未来的 macOS 发行版当中是否可用,则需要另行测试、才能下结论。然而,目前 IMK 选字窗属于建议启用的功能,因为田所选字窗仍需改善。"; "in front of the phrase (like macOS built-in Zhuyin IME)" = "将游标置于词语前方 // macOS 内建注音风格"; "Intonation Key:" = "声调键:"; "Japanese" = "和语"; @@ -217,7 +217,8 @@ "Specify the behavior of intonation key when syllable composer is empty." = "指定声调键(在注拼槽为「空」状态时)的行为。"; "Starlight" = "星光排列"; "Stop farting (when typed phonetic combination is invalid, etc.)" = "廉耻模式 // 取消勾选的话,敲错字时会有异音"; -"This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later." = "该方法是 Apple Bug Report #FB10978412 的保守治疗方案,用来仅针对 macOS 12 开始的系统,且仅对非 IMK 选字窗起作用。Apple 应该对 macOS 11 开始的系统修复这个 Bug。"; +"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "田所选字窗仅支援横排矩阵布局模式。如欲使用纵排布局模式者,请在开发道场内先启用 IMK 选字窗。"; +"This only works with Tadokoro candidate window." = "该方法仅对田所选字窗起作用。"; "Traditional Chinese" = "繁体中文"; "Trim unfinished readings on commit" = "在递交时清理未完成拼写的读音"; "Type them into inline composition buffer" = "直接键入内文组字区"; @@ -226,7 +227,7 @@ "Universal Pinyin with Numeral Intonation" = "通用拼音+数字标调"; "Use .langIdentifier to handle UI fonts in candidate window" = "使用 .langIdentifier 来管理选字窗的预设介面字型"; "Use ESC key to clear the entire input buffer" = "敲 ESC 键以清空整个组字缓冲区"; -"Use IMK Candidate Window instead (will reboot the IME)" = "启用与 macOS 内建输入法相同的 IMK 选字窗(会自动重启输入法)"; +"Use IMK Candidate Window instead of Tadokoro (will reboot the IME)" = "启用与 macOS 内建输入法相同的 IMK 选字窗(会自动重启输入法)"; "Use Shift key accommodation in all cases" = "对任何客体应用均启用 Shift 键相容性措施"; "Vertical" = "纵向布局"; "Warning: This page is for testing future features. \nFeatures listed here may not work as expected." = "警告:该页面仅作未来功能测试所用。\n在此列出的功能并非处于完全可用之状态。"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 350b2751..c2a2141d 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -185,7 +185,7 @@ "Hsu" = "許氏國音自然排列"; "Hualuo Pinyin with Numeral Intonation" = "華羅拼音+數字標調"; "IBM" = "IBM 排列"; -"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases." = "IMK 選字窗依賴於 Apple 的私有 API,而且是藉由橋接檔案頭強制曝露的方法使用的。目前,該功能僅在 macOS 10.14 Mojave 至 macOS 13 Ventura 系統內有測試過可用性。至於在未來的 macOS 發行版當中是否可用,則需要另行測試、才能下結論。"; +"IMK candidate window relies on certain Apple private APIs which are force-exposed by using bridging headers. Its usability, at this moment, is only guaranteed from macOS 10.14 Mojave to macOS 13 Ventura. Further tests are required in the future in order to tell whether it is usable in newer macOS releases. However, this mode is recommended at this moment since Tadokoro candidate window still needs possible improvements." = "IMK 選字窗依賴於 Apple 的私有 API,而且是藉由橋接檔案頭強制曝露的方法使用的。目前,該功能僅在 macOS 10.14 Mojave 至 macOS 13 Ventura 系統內有測試過可用性。至於在未來的 macOS 發行版當中是否可用,則需要另行測試、才能下結論。然而,目前 IMK 選字窗屬於建議啟用的功能,因為田所選字窗仍需改善。"; "in front of the phrase (like macOS built-in Zhuyin IME)" = "將游標置於詞語前方 // macOS 內建注音風格"; "Intonation Key:" = "聲調鍵:"; "Japanese" = "和語"; @@ -217,7 +217,8 @@ "Specify the behavior of intonation key when syllable composer is empty." = "指定聲調鍵(在注拼槽為「空」狀態時)的行為。"; "Starlight" = "星光排列"; "Stop farting (when typed phonetic combination is invalid, etc.)" = "廉恥模式 // 取消勾選的話,敲錯字時會有異音"; -"This only works since macOS 12 with non-IMK candidate window as an alternative wordaround of Apple Bug Report #FB10978412. Apple should patch that for macOS 11 and later." = "該方法是 Apple Bug Report #FB10978412 的保守治療方案,用來僅針對 macOS 12 開始的系統,且僅對非 IMK 選字窗起作用。Apple 應該對 macOS 11 開始的系統修復這個 Bug。"; +"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "田所選字窗僅支援橫排矩陣佈局模式。如欲使用縱排佈局模式者,請在開發道場內先啟用 IMK 選字窗。"; +"This only works with Tadokoro candidate window." = "該方法僅對田所選字窗起作用。"; "Traditional Chinese" = "繁體中文"; "Trim unfinished readings on commit" = "在遞交時清理未完成拼寫的讀音"; "Type them into inline composition buffer" = "直接鍵入內文組字區"; @@ -226,7 +227,7 @@ "Universal Pinyin with Numeral Intonation" = "通用拼音+數字標調"; "Use .langIdentifier to handle UI fonts in candidate window" = "使用 .langIdentifier 來管理選字窗的預設介面字型"; "Use ESC key to clear the entire input buffer" = "敲 ESC 鍵以清空整個組字緩衝區"; -"Use IMK Candidate Window instead (will reboot the IME)" = "啟用與 macOS 內建輸入法相同的 IMK 選字窗(會自動重啟輸入法)"; +"Use IMK Candidate Window instead of Tadokoro (will reboot the IME)" = "啟用與 macOS 內建輸入法相同的 IMK 選字窗(會自動重啟輸入法)"; "Use Shift key accommodation in all cases" = "對任何客體應用均啟用 Shift 鍵相容性措施"; "Vertical" = "縱向佈局"; "Warning: This page is for testing future features. \nFeatures listed here may not work as expected." = "警告:該頁面僅作未來功能測試所用。\n在此列出的功能並非處於完全可用之狀態。"; diff --git a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib index 4ca6507c..4fb2f69f 100644 --- a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib +++ b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib @@ -687,6 +687,7 @@ + @@ -1375,7 +1376,7 @@ - + @@ -1394,11 +1395,11 @@ Features listed here may not work as expected.