From 47e15d9cbdee887a868ccb4e0acb3fdce8336702 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Thu, 12 May 2022 22:08:43 +0800 Subject: [PATCH 01/19] Readme // Update credits. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d6221872..d489ecac 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ - 僅供研發人員調試方便而使用的 App 版安裝程式 (by Zonble Yang)。 +- Voltaire MK2 選字窗、飄雲通知視窗、工具提示 (by Zonble Yang),有大幅度修改。 + 威注音輸入法 macOS 版以 MIT-NTL License 授權釋出 (與 MIT 相容):© 2021-2022 vChewing 專案。 - 威注音輸入法 macOS 版程式維護:Shiki Suen。特別感謝 Isaac Xen 與 Hiraku Wong 等人的技術協力。 From 9813dd19d2c8c41e85382a459973561501f707d4 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Thu, 12 May 2022 22:25:18 +0800 Subject: [PATCH 02/19] Megrez::Builder // Allow defining max span length on init. - Also use for loop in lieu of while loop in Megrez. --- .../Megrez/1_BlockReadingBuilder.swift | 27 +++++++++---------- .../LanguageParsers/Megrez/1_Walker.swift | 15 ++++++----- .../LanguageParsers/Megrez/2_Grid.swift | 25 +++++------------ 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift b/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift index 652ddc4c..d07d3af9 100644 --- a/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift +++ b/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift @@ -25,15 +25,16 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extension Megrez { public class BlockReadingBuilder { - let kMaximumBuildSpanLength = 10 // 規定最多可以組成的詞的字數上限為 10 + var mutMaximumBuildSpanLength = 10 var mutCursorIndex: Int = 0 var mutReadings: [String] = [] var mutGrid: Grid = .init() var mutLM: LanguageModel var mutJoinSeparator: String = "" - public init(lm: LanguageModel) { + public init(lm: LanguageModel, length: Int = 10) { mutLM = lm + mutMaximumBuildSpanLength = length } public func clear() { @@ -87,15 +88,13 @@ extension Megrez { return false } - var i = 0 - while i < count { - if mutCursorIndex != 0 { + for _ in 0.. 0 { mutCursorIndex -= 1 } mutReadings.removeFirst() mutGrid.shrinkGridByOneAt(location: 0) build() - i += 1 } return true @@ -113,15 +112,17 @@ extension Megrez { // if (mutLM == nil) { return } // 這個出不了 nil,所以註釋掉。 let itrBegin: Int = - (mutCursorIndex < kMaximumBuildSpanLength) ? 0 : mutCursorIndex - kMaximumBuildSpanLength - let itrEnd: Int = min(mutCursorIndex + kMaximumBuildSpanLength, mutReadings.count) + (mutCursorIndex < mutMaximumBuildSpanLength) ? 0 : mutCursorIndex - mutMaximumBuildSpanLength + let itrEnd: Int = min(mutCursorIndex + mutMaximumBuildSpanLength, mutReadings.count) - var p = itrBegin - while p < itrEnd { - var q = 1 - while q <= kMaximumBuildSpanLength, p + q <= itrEnd { + for p in itrBegin.. itrEnd { + break + } let strSlice = mutReadings[p..<(p + q)] let combinedReading: String = join(slice: strSlice, separator: mutJoinSeparator) + if !mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) { let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading) if !unigrams.isEmpty { @@ -129,9 +130,7 @@ extension Megrez { mutGrid.insertNode(node: n, location: p, spanningLength: q) } } - q += 1 } - p += 1 } } diff --git a/Source/Modules/LanguageParsers/Megrez/1_Walker.swift b/Source/Modules/LanguageParsers/Megrez/1_Walker.swift index bf98aaea..d0f68b3a 100644 --- a/Source/Modules/LanguageParsers/Megrez/1_Walker.swift +++ b/Source/Modules/LanguageParsers/Megrez/1_Walker.swift @@ -50,20 +50,20 @@ extension Megrez { } } - // 只檢查前 X 個 NodeAnchor 是否有 node。 - // 這裡有 abs 是為了防止有白癡填負數。 - var border: Int = nodes.count - if nodesLimit > 0 { - border = min(nodes.count, abs(nodesLimit)) - } + for (i, n) in nodes.enumerated() { + // 只檢查前 X 個 NodeAnchor 是否有 node。 + // 這裡有 abs 是為了防止有白癡填負數。 + if abs(nodesLimit) > 0, i == abs(nodesLimit) - 1 { + break + } - for n in nodes[0..= mutSpans.count { let diff = location - mutSpans.count + 1 - var i = 0 - while i < diff { + for _ in 0.. 0, location < mutSpans.count { - var i = 0 - while i < location { + for i in 0.. [NodeAnchor] { var results: [NodeAnchor] = [] if !mutSpans.isEmpty, location <= mutSpans.count { - var i = 0 - while i < location { + for i in 0..= location { if let np = span.node(length: location - i) { @@ -101,7 +94,6 @@ extension Megrez { ) } } - i += 1 } } return results @@ -110,14 +102,11 @@ extension Megrez { public func nodesCrossingOrEndingAt(location: Int) -> [NodeAnchor] { var results: [NodeAnchor] = [] if !mutSpans.isEmpty, location <= mutSpans.count { - var i = 0 - while i < location { + for i in 0..= location { - var j = 1 - while j <= span.maximumLength { + for j in 1...span.maximumLength { if i + j < location { - j += 1 continue } if let np = span.node(length: j) { @@ -129,10 +118,8 @@ extension Megrez { ) ) } - j += 1 } } - i += 1 } } return results From e736ae0ca1b962e33c03ca1a52a307748ee521ce Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 13 May 2022 10:01:25 +0800 Subject: [PATCH 03/19] KeyHandler & Megrez // Nomenclature updates. --- Source/Modules/ControllerModules/KeyHandler_Core.swift | 6 +++--- Source/Modules/ControllerModules/KeyHandler_States.swift | 2 +- .../LanguageParsers/Megrez/1_BlockReadingBuilder.swift | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index 9c790668..c42d0440 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -312,11 +312,11 @@ class KeyHandler: NSObject { } func deleteBuilderReadingInFrontOfCursor() { - _builder.deleteReadingBeforeCursor() + _builder.deleteReadingAtTheRearOfCursor() } - func deleteBuilderReadingAfterCursor() { - _builder.deleteReadingAfterCursor() + func deleteBuilderReadingToTheFrontOfCursor() { + _builder.deleteReadingToTheFrontOfCursor() } func getKeyLengthAtIndexZero() -> Int { diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 454db395..2d7f8cfd 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -372,7 +372,7 @@ extension KeyHandler { if _composer.isEmpty { if getBuilderCursorIndex() != getBuilderLength() { - deleteBuilderReadingAfterCursor() + deleteBuilderReadingToTheFrontOfCursor() walk() let inputting = buildInputtingState() // 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。 diff --git a/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift b/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift index d07d3af9..78b659f0 100644 --- a/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift +++ b/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift @@ -60,7 +60,7 @@ extension Megrez { public func readings() -> [String] { mutReadings } - @discardableResult public func deleteReadingBeforeCursor() -> Bool { + @discardableResult public func deleteReadingAtTheRearOfCursor() -> Bool { if mutCursorIndex == 0 { return false } @@ -72,7 +72,7 @@ extension Megrez { return true } - @discardableResult public func deleteReadingAfterCursor() -> Bool { + @discardableResult public func deleteReadingToTheFrontOfCursor() -> Bool { if mutCursorIndex == mutReadings.count { return false } From b64e48239e15d10dda40c16090900b97887f53fe Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 13 May 2022 13:49:35 +0800 Subject: [PATCH 04/19] KeyHandler // Reenable advanced features of reverseWalk(). --- Source/Modules/ControllerModules/KeyHandler_Core.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index c42d0440..d2f59cb4 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -122,7 +122,7 @@ class KeyHandler: NSObject { let walker = Megrez.Walker(grid: _builder.grid()) // the reverse walk traces the grid from the end - let walked: [Megrez.NodeAnchor] = walker.reverseWalk(at: _builder.grid().width(), balanced: true) + let walked: [Megrez.NodeAnchor] = walker.reverseWalk(at: _builder.grid().width(), nodesLimit: 3, balanced: true) // then we use ".reversed()" to reverse the nodes so that we get the forward-walked nodes _walkedNodes.removeAll() From f31373a104a8d457acf5ac8172cfc6192307bde5 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 13 May 2022 12:32:25 +0800 Subject: [PATCH 05/19] KeyHandler // Refactoring a little. --- .../ControllerModules/KeyHandler_Core.swift | 3 +- .../KeyHandler_HandleInput.swift | 54 +++++++++---------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index d2f59cb4..2a15095b 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -84,7 +84,6 @@ class KeyHandler: NSObject { _walkedNodes.removeAll() } - // 這個函數得獨立出來給 ObjC 使用。 func setInputMode(_ value: String) { // 下面這句的「isKindOfClass」是做類型檢查, // 為了應對出現輸入法 plist 被改壞掉這樣的極端情況。 @@ -122,7 +121,7 @@ class KeyHandler: NSObject { let walker = Megrez.Walker(grid: _builder.grid()) // the reverse walk traces the grid from the end - let walked: [Megrez.NodeAnchor] = walker.reverseWalk(at: _builder.grid().width(), nodesLimit: 3, balanced: true) + let walked = walker.reverseWalk(at: _builder.grid().width(), nodesLimit: 3, balanced: true) // then we use ".reversed()" to reverse the nodes so that we get the forward-walked nodes _walkedNodes.removeAll() diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index 9928654d..96e944f6 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -37,12 +37,10 @@ extension KeyHandler { ) -> Bool { let charCode: UniChar = input.charCode var state = state // Turn this incoming constant into variable. - let inputText: String = input.inputText ?? "" // Ignore the input if its inputText is empty. // Reason: such inputs may be functional key combinations. - - if inputText.isEmpty { + guard let inputText: String = input.inputText, !inputText.isEmpty else { return false } @@ -238,36 +236,34 @@ extension KeyHandler { // MARK: Calling candidate window using Space or Down or PageUp / PageDn. - if let currentState = state as? InputState.NotEmpty { - if _composer.isEmpty, - input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace - || input.isPageDown || input.isPageUp || input.isTab - || (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey)) - { - if input.isSpace { - // If the Space key is NOT set to be a selection key - if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace { - if getBuilderCursorIndex() >= getBuilderLength() { - let composingBuffer = currentState.composingBuffer - if (composingBuffer.count) != 0 { - stateCallback(InputState.Committing(poppedText: composingBuffer)) - } - clear() - stateCallback(InputState.Committing(poppedText: " ")) - stateCallback(InputState.Empty()) - } else if ifLangModelHasUnigrams(forKey: " ") { - insertReadingToBuilderAtCursor(reading: " ") - let poppedText = popOverflowComposingTextAndWalk() - let inputting = buildInputtingState() - inputting.poppedText = poppedText - stateCallback(inputting) + if let currentState = state as? InputState.NotEmpty, _composer.isEmpty, + input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace + || input.isPageDown || input.isPageUp || input.isTab + || (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey)) + { + if input.isSpace { + // If the Space key is NOT set to be a selection key + if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace { + if getBuilderCursorIndex() >= getBuilderLength() { + let composingBuffer = currentState.composingBuffer + if !composingBuffer.isEmpty { + stateCallback(InputState.Committing(poppedText: composingBuffer)) } - return true + clear() + stateCallback(InputState.Committing(poppedText: " ")) + stateCallback(InputState.Empty()) + } else if ifLangModelHasUnigrams(forKey: " ") { + insertReadingToBuilderAtCursor(reading: " ") + let poppedText = popOverflowComposingTextAndWalk() + let inputting = buildInputtingState() + inputting.poppedText = poppedText + stateCallback(inputting) } + return true } - stateCallback(buildCandidate(state: currentState, useVerticalMode: input.useVerticalMode)) - return true } + stateCallback(buildCandidate(state: currentState, useVerticalMode: input.useVerticalMode)) + return true } // MARK: - From f44546202641776bef90ff1551ae04ae02e84f29 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 13 May 2022 13:20:33 +0800 Subject: [PATCH 06/19] KeyHandler // Fix wrong callbacks in handleDelete() in v1.5.1 update. --- Source/Modules/ControllerModules/KeyHandler_States.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 2d7f8cfd..e5e473be 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -376,7 +376,7 @@ extension KeyHandler { walk() let inputting = buildInputtingState() // 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。 - if !inputting.composingBuffer.isEmpty { + if inputting.composingBuffer.isEmpty { stateCallback(InputState.EmptyIgnoringPreviousState()) } else { stateCallback(inputting) From 164e8d7b327746287a9dcb70c11018d525de3f66 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Thu, 12 May 2022 22:38:24 +0800 Subject: [PATCH 07/19] KeyHandler // Deprecating NSString in buildInputtingState(). --- .../ControllerModules/KeyHandler_States.swift | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index e5e473be..78fc8c1f 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -37,58 +37,60 @@ extension KeyHandler { var composingBuffer = "" var composedStringCursorIndex = 0 - var readingCursorIndex: size_t = 0 - let builderCursorIndex: size_t = getBuilderCursorIndex() + var readingCursorIndex = 0 + let builderCursorIndex = getBuilderCursorIndex() - // We must do some Unicode codepoint counting to find the actual cursor location for the client - // i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars - // locations. These processes are inherited from the ObjC++ version of this class and might be - // unnecessary in Swift, but this deduction requires further experiments. - for walkedNode in _walkedNodes { - if let theNode = walkedNode.node { - let strNodeValue = theNode.currentKeyValue().value - composingBuffer += strNodeValue + for theAnchor in _walkedNodes { + guard let node = theAnchor.node else { + continue + } - let arrSplit: [NSString] = (strNodeValue as NSString).split() - let codepointCount = arrSplit.count + let valueString = node.currentKeyValue().value + composingBuffer += valueString + let codepointCount = valueString.count - // This re-aligns the cursor index in the composed string - // (the actual cursor on the screen) with the builder's logical - // cursor (reading) cursor; each built node has a "spanning length" - // (e.g. two reading blocks has a spanning length of 2), and we - // accumulate those lengths to calculate the displayed cursor - // index. - let spanningLength: Int = walkedNode.spanningLength - if readingCursorIndex + spanningLength <= builderCursorIndex { - composedStringCursorIndex += (strNodeValue as NSString).length - readingCursorIndex += spanningLength - } else { - if codepointCount == spanningLength { - var i = 0 - while i < codepointCount, readingCursorIndex < builderCursorIndex { - composedStringCursorIndex += arrSplit[i].length - readingCursorIndex += 1 - i += 1 - } - } else { + let spanningLength = theAnchor.spanningLength + if readingCursorIndex + spanningLength <= builderCursorIndex { + composedStringCursorIndex += valueString.count + readingCursorIndex += spanningLength + } else { + if codepointCount == spanningLength { + for _ in 0.. builderCursorIndex { - readingCursorIndex = builderCursorIndex - } + composedStringCursorIndex += 1 + readingCursorIndex += 1 + } + } + } else { + if readingCursorIndex < builderCursorIndex { + composedStringCursorIndex += valueString.count + readingCursorIndex += spanningLength + if readingCursorIndex > builderCursorIndex { + readingCursorIndex = builderCursorIndex } } } } } + // Now, we gather all the intel, separate the composing buffer to two parts (head and tail), // and insert the reading text (the Mandarin syllable) in between them. // The reading text is what the user is typing. - let head = String((composingBuffer as NSString).substring(to: composedStringCursorIndex)) + var rawHead = "" + var rawEnd = "" + + for (i, n) in composingBuffer.enumerated() { + if i < composedStringCursorIndex { + rawHead += String(n) + } else { + rawEnd += String(n) + } + } + + let head = rawHead let reading = _composer.getDisplayedComposition() - let tail = String((composingBuffer as NSString).substring(from: composedStringCursorIndex)) + let tail = rawEnd let composedText = head + reading + tail let cursorIndex = composedStringCursorIndex + reading.count From c2dba51a50835ba91834a47bfc7d7d84c8738ff2 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 13 May 2022 17:10:33 +0800 Subject: [PATCH 08/19] LMInstantiator // Remove debug messages. --- Source/Modules/LangModelRelated/LMInstantiator.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Source/Modules/LangModelRelated/LMInstantiator.swift b/Source/Modules/LangModelRelated/LMInstantiator.swift index c9312cf4..583a942f 100644 --- a/Source/Modules/LangModelRelated/LMInstantiator.swift +++ b/Source/Modules/LangModelRelated/LMInstantiator.swift @@ -215,9 +215,6 @@ extension vChewing { // 這樣一來就可以在就地新增語彙時徹底複寫優先權。 // 將兩句差分也是為了讓 rawUserUnigrams 的類型不受可能的影響。 rawAllUnigrams += lmUserPhrases.unigramsFor(key: key).reversed() - if lmUserPhrases.unigramsFor(key: key).isEmpty { - IME.prtDebugIntel("Not found in UserPhrasesUnigram(\(lmUserPhrases.count)): \(key)") - } // LMMisc 與 LMCore 的 score 在 (-10.0, 0.0) 這個區間內。 rawAllUnigrams += lmMisc.unigramsFor(key: key) @@ -229,10 +226,6 @@ extension vChewing { if isSymbolEnabled { rawAllUnigrams += lmUserSymbols.unigramsFor(key: key) - if lmUserSymbols.unigramsFor(key: key).isEmpty { - IME.prtDebugIntel("Not found in UserSymbolUnigram(\(lmUserSymbols.count)): \(key)") - } - rawAllUnigrams += lmSymbols.unigramsFor(key: key) } From 4d8cbb2db5cb1e6c4f165e61071f3af3962716da Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 00:36:23 +0800 Subject: [PATCH 09/19] Megrez::Grid // Fix wrong conditioning in expandGridByOneAt(). --- Source/Modules/LanguageParsers/Megrez/1_Walker.swift | 2 +- Source/Modules/LanguageParsers/Megrez/2_Grid.swift | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Modules/LanguageParsers/Megrez/1_Walker.swift b/Source/Modules/LanguageParsers/Megrez/1_Walker.swift index d0f68b3a..26e4502d 100644 --- a/Source/Modules/LanguageParsers/Megrez/1_Walker.swift +++ b/Source/Modules/LanguageParsers/Megrez/1_Walker.swift @@ -46,7 +46,7 @@ extension Megrez { if balanced { nodes.sort { - $0.balancedScore > $1.balancedScore // 排序規則已經在 NodeAnchor 內定義了。 + $0.balancedScore > $1.balancedScore } } diff --git a/Source/Modules/LanguageParsers/Megrez/2_Grid.swift b/Source/Modules/LanguageParsers/Megrez/2_Grid.swift index 961a03a3..ada025f4 100644 --- a/Source/Modules/LanguageParsers/Megrez/2_Grid.swift +++ b/Source/Modules/LanguageParsers/Megrez/2_Grid.swift @@ -55,11 +55,12 @@ extension Megrez { } public func expandGridByOneAt(location: Int) { - mutSpans.append(Span()) - if location > 0, location < mutSpans.count { - for i in 0.. Date: Sat, 14 May 2022 13:33:22 +0800 Subject: [PATCH 10/19] Tekkon // Hsu parser fix & HYPY output support, etc. --- .../KeyHandler_HandleInput.swift | 2 +- .../ControllerModules/SyllableComposer.swift | 336 +++++++++++++++--- 2 files changed, 283 insertions(+), 55 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index 96e944f6..d75239db 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -166,7 +166,7 @@ extension KeyHandler { // However, Swift does not support "|=". composeReading = composeReading || (!_composer.isEmpty && (input.isSpace || input.isEnter)) if composeReading { - let reading = _composer.getRealComposition() + let reading = _composer.realComposition // See whether we have a unigram for this... if !ifLangModelHasUnigrams(forKey: reading) { diff --git a/Source/Modules/ControllerModules/SyllableComposer.swift b/Source/Modules/ControllerModules/SyllableComposer.swift index add9d9f3..f0305763 100644 --- a/Source/Modules/ControllerModules/SyllableComposer.swift +++ b/Source/Modules/ControllerModules/SyllableComposer.swift @@ -28,14 +28,16 @@ import Foundation public struct Tekkon { // MARK: - Static Constants and Basic Enums + /// 定義注音符號的種類 public enum PhoneType: Int { case null = 0 // 假 case consonant = 1 // 聲 - case semivowel = 2 // 韻 - case vowel = 3 // 介 + case semivowel = 2 // 介 + case vowel = 3 // 韻 case intonation = 4 // 調 } + /// 定義注音排列的類型 public enum MandarinParser: Int { case ofDachen = 0 case ofEten = 1 @@ -45,7 +47,7 @@ public struct Tekkon { case ofMiTAC = 5 case ofFakeSeigyou = 6 case ofSeigyou = 7 - case ofHanyuPinyin = 10 + case ofHanyuPinyin = 10 // 目前暫時沒有漢語拼音支援 var name: String { switch self { @@ -71,52 +73,75 @@ public struct Tekkon { } } + /// 引擎僅接受這些記號作為聲母 public static let allowedConsonants = [ "ㄅ", "ㄆ", "ㄇ", "ㄈ", "ㄉ", "ㄊ", "ㄋ", "ㄌ", "ㄍ", "ㄎ", "ㄏ", "ㄐ", "ㄑ", "ㄒ", "ㄓ", "ㄔ", "ㄕ", "ㄖ", "ㄗ", "ㄘ", "ㄙ", ] + /// 引擎僅接受這些記號作為介母 public static let allowedsemivowels = ["ㄧ", "ㄨ", "ㄩ"] + /// 引擎僅接受這些記號作為韻母 public static let allowedVowels = [ "ㄚ", "ㄛ", "ㄜ", "ㄝ", "ㄞ", "ㄟ", "ㄠ", "ㄡ", "ㄢ", "ㄣ", "ㄤ", "ㄥ", "ㄦ", ] + /// 引擎僅接受這些記號作為聲調 public static let allowedIntonations = [" ", "ˊ", "ˇ", "ˋ", "˙"] + /// 引擎僅接受這些記號作為注音(聲介韻調四個集合加起來) public static var allowedPhonabets: [String] { allowedConsonants + allowedsemivowels + allowedVowels + allowedIntonations } // MARK: - Phonabet Structure + /// 注音符號型別。本身與字串差不多,但卻只能被設定成一個注音符號字符。 + /// 然後會根據自身的 value 的內容值自動計算自身的 PhoneType 類型(聲介韻調假)。 + /// 如果遇到被設為多個字符、或者字符不對的情況的話,value 會被清空、PhoneType 會變成 null。 + /// 賦值時最好直接重新 init 且一直用 let 來初期化 Phonabet。 + /// 其實 value 對外只讀,對內的話另有 valueStorage 代為存儲內容。這樣比較安全一些。 @frozen public struct Phonabet: Equatable, Hashable, ExpressibleByStringLiteral { public var type: PhoneType = .null - public var value: String = "" + private var valueStorage = "" + public var value: String { valueStorage } public var isEmpty: Bool { value.isEmpty } + /// 初期化,會根據傳入的 input 字串參數來自動判定自身的 PhoneType 類型屬性值。 public init(_ input: String = "") { if !input.isEmpty { if allowedPhonabets.contains(String(input.reversed()[0])) { - value = String(input.reversed()[0]) - if Tekkon.allowedConsonants.contains(value) { type = .consonant } - if Tekkon.allowedsemivowels.contains(value) { type = .semivowel } - if Tekkon.allowedVowels.contains(value) { type = .vowel } - if Tekkon.allowedIntonations.contains(value) { type = .intonation } + valueStorage = String(input.reversed()[0]) + if Tekkon.allowedConsonants.contains(value) { + type = .consonant + } else if Tekkon.allowedsemivowels.contains(value) { + type = .semivowel + } else if Tekkon.allowedVowels.contains(value) { + type = .vowel + } else if Tekkon.allowedIntonations.contains(value) { + type = .intonation + } else { + type = .null + valueStorage = "" + } } } } + /// 自我清空內容。 public mutating func clear() { - value = "" + valueStorage = "" } // MARK: - Misc Definitions + /// 這些內容用來滿足 "Equatable, Hashable, ExpressibleByStringLiteral" 需求。 + public static func == (lhs: Phonabet, rhs: Phonabet) -> Bool { lhs.value == rhs.value } @@ -141,25 +166,76 @@ public struct Tekkon { // MARK: - Syllable Composer + /// 注音並擊處理的對外介面以注拼槽(Syllable Composer)的形式存在。 + /// 使用時需要單獨初期化為一個副本變數(因為是 Struct 所以必須得是變數)。 + /// 注拼槽只有四格:聲、介、韻、調。 + /// @--DISCUSSION--@ + /// 因為是 String Literal,所以初期化時可以藉由 @input 參數指定初期已經傳入的按鍵訊號。 + /// 還可以在初期化時藉由 @arrange 參數來指定注音排列(預設為「.ofDachen」大千佈局)。 @frozen public struct Composer: Equatable, Hashable, ExpressibleByStringLiteral { + /// 聲母。 public var consonant: Phonabet = "" + + /// 介母。 public var semivowel: Phonabet = "" + + /// 韻母。 public var vowel: Phonabet = "" + + /// 聲調。 public var intonation: Phonabet = "" + + /// 注音排列種類。預設情況下是大千排列(Windows / macOS 預設注音排列)。 public var parser: MandarinParser = .ofDachen + + /// 內容值,會直接按照正確的順序拼裝自己的聲介韻調內容、再回傳。 + /// 注意:直接取這個參數的內容的話,陰平聲調會成為一個空格。 + /// 如果是要取不帶空格的注音的話,請使用「.realComposition」而非「.value」。 public var value: String { - consonant.value + semivowel.value + vowel.value + intonation.value.replacingOccurrences(of: " ", with: "") + consonant.value + semivowel.value + vowel.value + intonation.value } + /// 這是專門用來「生成用以進行詞庫檢索的 Key」的函數。 + public var realComposition: String { + value.replacingOccurrences(of: " ", with: "") + } + + /// 與 value 類似。這個函數就是用來決定輸入法組字區內顯示的注音/拼音內容。 + /// 但可以指定是否輸出教科書格式(拼音的調號在字母上方、注音的輕聲寫在左側)。 + public func getComposition(isHanyuPinyin: Bool = false, isTextBookStyle: Bool = false) -> String { + switch isHanyuPinyin { + case false: + var valReturnZhuyin = value.replacingOccurrences(of: " ", with: "") + if isTextBookStyle, valReturnZhuyin.contains("˙") { + valReturnZhuyin = String(valReturnZhuyin.dropLast()) + valReturnZhuyin.insert("˙", at: valReturnZhuyin.startIndex) + } + return valReturnZhuyin + case true: + var valReturnPinyin = Tekkon.cnvPhonaToHanyuPinyin(target: value) + if isTextBookStyle { + valReturnPinyin = Tekkon.cnvHanyuPinyinToTextbookStyle(target: valReturnPinyin) + } + return valReturnPinyin + } + } + + /// 注拼槽內容是否為空。 public var isEmpty: Bool { intonation.isEmpty && vowel.isEmpty && semivowel.isEmpty && consonant.isEmpty } + // MARK: 注拼槽對外處理函數 + + /// 初期化一個新的注拼槽。可以藉由 @input 參數指定初期已經傳入的按鍵訊號。 + /// 還可以在初期化時藉由 @arrange 參數來指定注音排列(預設為「.ofDachen」大千佈局)。 public init(_ input: String = "", arrange parser: MandarinParser = .ofDachen) { - receiveKey(fromString: input) ensureParser(arrange: parser) + receiveKey(fromString: input) } + /// 清除自身的內容,就是將聲介韻調全部清空。 + /// 嚴格而言,「注音排列」這個屬性沒有需要清空的概念,只能用 ensureParser 參數變更之。 public mutating func clear() { consonant.clear() semivowel.clear() @@ -170,9 +246,10 @@ public struct Tekkon { // MARK: - Public Functions /// 用於檢測「某個輸入字符訊號的合規性」的函數。 - /// Phonabet 是一個特殊的 String 類 Struct, - /// 只會接受正確的注音符號資料、且根據其類型自動回報其類型。 - /// 類型只有「聲、韻、介、調」這四類。 + /// @--DISCUSSION--@ + /// 注意:回傳結果會受到當前注音排列 parser 屬性的影響。 + /// - Parameters: + /// - key: 傳入的 UniChar 內容。 public func inputValidityCheck(key inputKey: UniChar = 0) -> Bool { if let scalar = UnicodeScalar(inputKey) { let input = String(scalar) @@ -200,6 +277,12 @@ public struct Tekkon { return false } + /// 接受傳入的按鍵訊號時的處理,處理對象為 String。 + /// 另有同名函數可處理 UniChar 訊號。 + /// @--DISCUSSION--@ + /// 如果是諸如複合型注音排列的話,翻譯結果有可能為空,但翻譯過程已經處理好聲介韻調分配了。 + /// - Parameters: + /// - fromString: 傳入的 String 內容。 public mutating func receiveKey(fromString input: String = "") { let translatedInput = translate(key: String(input)) let thePhone: Phonabet = .init(translatedInput) @@ -212,32 +295,22 @@ public struct Tekkon { } } + /// 接受傳入的按鍵訊號時的處理,處理對象為 UniChar。 + /// 其實也就是先將 UniChar 轉為 String 再交給某個同名異參的函數來處理而已。 + /// @--DISCUSSION--@ + /// 如果是諸如複合型注音排列的話,翻譯結果有可能為空,但翻譯過程已經處理好聲介韻調分配了。 + /// - Parameters: + /// - fromCharCode: 傳入的 UniChar 內容。 public mutating func receiveKey(fromCharCode inputCharCode: UniChar = 0) { if let scalar = UnicodeScalar(inputCharCode) { - let translatedInput = translate(key: String(scalar)) - let thePhone: Phonabet = .init(translatedInput) - switch thePhone.type { - case .consonant: consonant = thePhone - case .semivowel: semivowel = thePhone - case .vowel: vowel = thePhone - case .intonation: intonation = thePhone - default: break - } + receiveKey(fromString: String(scalar)) } } - /// 本來這個函數是不需要的,但將來在做漢語拼音功能時、這裡得回傳別的東西。 - /// 也就是說,這個函數就是用來決定輸入法組字區內顯示的注音/拼音內容。 - public func getDisplayedComposition() -> String { - value - } - - /// 這是專門用來「生成用以進行詞庫檢索的 Key」的函數。 - public func getRealComposition() -> String { - value - } - /// 專門用來響應使用者摁下 BackSpace 按鍵時的行為。 + /// 刪除順序:調、韻、介、聲。 + /// @--DISCUSSION--@ + /// 基本上就是按順序從游標前方開始往後刪。 public mutating func doBackSpace() { if !intonation.isEmpty { intonation.clear() @@ -250,7 +323,9 @@ public struct Tekkon { } } - /// 用來檢測是否有調號 + /// 用來檢測是否有調號的函數,預設情況下不判定聲調以外的內容的存無。 + /// - Parameters: + /// - withNothingElse: 追加判定「槽內是否僅有調號」。 public func hasToneMarker(withNothingElse: Bool = false) -> Bool { if !withNothingElse { return !intonation.isEmpty @@ -258,23 +333,22 @@ public struct Tekkon { return !intonation.isEmpty && vowel.isEmpty && semivowel.isEmpty && consonant.isEmpty } - /// 當接收按鍵輸入時的處理。本來 Phonabet 就會自動使輸入的無效數值被忽略掉。 - /// 然後再根據 Phonabet 初期化後自身的 type 屬性來決定「聲、韻、介、調」到底哪個該更新。 - public mutating func receiveCharCode(_ inputKey: UniChar = 0) { - // TODO: 在這裡補上鍵盤轉換工序 - if let scalar = UnicodeScalar(inputKey) { - let input = String(scalar) - receiveKey(fromString: input) - } - } - - // 設定該 Composer 處於何種鍵盤排列分析模式 + // 設定該 Composer 處於何種鍵盤排列分析模式。 + /// - Parameters: + /// - arrange: 給該注拼槽指定注音排列。 public mutating func ensureParser(arrange: MandarinParser = .ofDachen) { parser = arrange } // MARK: - Parser Processings + // 注拼槽對內處理用函數都在這一小節。 + + /// 根據目前的注音排列設定來翻譯傳入的 String 訊號。 + /// @--DISCUSSION--@ + /// 倚天或許氏鍵盤的處理函數會將分配過程代為處理過,此時回傳結果為空字串。 + /// - Parameters: + /// - key: 傳入的 String 訊號。 mutating func translate(key: String = "") -> String { switch parser { case .ofDachen: @@ -299,6 +373,10 @@ public struct Tekkon { } /// 倚天忘形注音排列比較麻煩,需要單獨處理。 + /// @--DISCUSSION--@ + /// 回傳結果是空字串的話,不要緊,因為該函數內部已經處理過分配過程了。 + /// - Parameters: + /// - key: 傳入的 String 訊號。 mutating func handleEten26(key: String = "") -> String { var strReturn = "" strReturn = Tekkon.mapEten26StaticKeys[key] ?? "" @@ -348,34 +426,54 @@ public struct Tekkon { } } + // 這些按鍵在上文處理過了,就不要再回傳了。 + if "dfhjklmnpqtw".contains(key) { strReturn = "" } + + // 回傳結果是空字串的話,不要緊,因為上文已經代處理過分配過程了。 return strReturn } /// 許氏鍵盤與倚天忘形一樣同樣也比較麻煩,需要單獨處理。 + /// @--DISCUSSION--@ + /// 回傳結果是空的話,不要緊,因為該函數內部已經處理過分配過程了。 + /// - Parameters: + /// - key: 傳入的 String 訊號。 mutating func handleHsu(key: String = "") -> String { var strReturn = "" strReturn = Tekkon.mapHsuStaticKeys[key] ?? "" let incomingPhonabet = Phonabet(strReturn) switch key { - case "e": if semivowel.isEmpty { semivowel = "ㄧ" } else { vowel = "ㄝ" } case "a": if consonant.isEmpty { consonant = "ㄘ" } else { vowel = "ㄟ" } + case "d": if consonant.isEmpty { consonant = "ㄉ" } else { intonation = "ˊ" } + case "e": if semivowel.isEmpty { semivowel = "ㄧ" } else { vowel = "ㄝ" } + case "f": if consonant.isEmpty { consonant = "ㄈ" } else { intonation = "ˇ" } case "g": if consonant.isEmpty { consonant = "ㄍ" } else { vowel = "ㄜ" } case "h": if consonant.isEmpty { consonant = "ㄏ" } else { vowel = "ㄛ" } case "k": if consonant.isEmpty { consonant = "ㄎ" } else { vowel = "ㄤ" } + case "l": + if value.isEmpty, !consonant.isEmpty, !semivowel.isEmpty { + vowel = "ㄦ" + } else if consonant.isEmpty { + consonant = "ㄌ" + } else { + vowel = "ㄥ" + } case "m": if consonant.isEmpty { consonant = "ㄇ" } else { vowel = "ㄢ" } case "n": if consonant.isEmpty { consonant = "ㄋ" } else { vowel = "ㄣ" } case "s": if consonant.isEmpty { consonant = "ㄙ" } else { intonation = "˙" } - case "d": if consonant.isEmpty { consonant = "ㄉ" } else { intonation = "ˊ" } - case "f": if consonant.isEmpty { consonant = "ㄈ" } else { intonation = "ˇ" } - case "l": if value.isEmpty { vowel = "ㄦ" } else if consonant.isEmpty { consonant = "ㄌ" } else { vowel = "ㄥ" } - case "j": if !consonant.isEmpty { intonation = "ˋ" } default: break } - // 處理「一個按鍵對應兩個聲母」的情形。 if !consonant.isEmpty, incomingPhonabet.type == .semivowel { switch consonant { + case "ㄍ": // 許氏鍵盤應該也需要這個自動糾正 + switch incomingPhonabet { + case "ㄧ": consonant = "ㄑ" // ㄑㄧ + case "ㄨ": consonant = "ㄍ" // ㄍㄨ + case "ㄩ": consonant = "ㄑ" // ㄑㄩ + default: break + } case "ㄓ": if intonation.isEmpty { switch incomingPhonabet { @@ -396,11 +494,21 @@ public struct Tekkon { } } + if key == "j" { // 對該按鍵作為調號的處理得放在最後 + if !consonant.isEmpty { intonation = "ˋ" } + } + + // 這些按鍵在上文處理過了,就不要再回傳了。 + if "adefghklmns".contains(key) { strReturn = "" } + + // 回傳結果是空的話,不要緊,因為上文已經代處理過分配過程了。 return strReturn } // MARK: - Misc Definitions + /// 這些內容用來滿足 "Equatable, Hashable, ExpressibleByStringLiteral" 需求。 + public static func == (lhs: Composer, rhs: Composer) -> Bool { lhs.value == rhs.value } @@ -427,6 +535,7 @@ public struct Tekkon { // MARK: - Phonabets (Enum) + /// 該 Enum 羅列了所有合理的注音符號,將來做漢語拼音功能支援時可能會用到。 enum Phonabets: Phonabet { case ofBO = "ㄅ" case ofPO = "ㄆ" @@ -471,12 +580,129 @@ public struct Tekkon { case ofT5 = "˙" } + // MARK: - Phonabet to Hanyu-Pinyin Conversion Processing + + /// 注音轉拼音,要求陰平必須是空格。 + /// - Parameters: + /// - target: 傳入的 String 對象物件。 + static func cnvPhonaToHanyuPinyin(target: String) -> String { + var targetConverted = target + for pair in arrPhonaToHanyuPinyin { + targetConverted = targetConverted.replacingOccurrences(of: pair[0], with: pair[1]) + } + return targetConverted + } + + static func cnvHanyuPinyinToTextbookStyle(target: String) -> String { + var targetConverted = target + for pair in arrHanyuPinyinTextbookStyleConversionTable { + targetConverted = targetConverted.replacingOccurrences(of: pair[0], with: pair[1]) + } + return targetConverted + } + + /// 原始轉換對照表資料貯存專用佇列(數字標調格式) + static let arrPhonaToHanyuPinyin = [ // 排序很重要。先處理最長的,再處理短的。不然會出亂子。 + [" ", "1"], ["ˊ", "2"], ["ˇ", "3"], ["ˋ", "4"], ["˙", "5"], ["ㄔㄨㄤ", "chuang"], ["ㄕㄨㄤ", "shuang"], + ["ㄓㄨㄤ", "zhuang"], ["ㄔㄨㄥ", "chong"], ["ㄔㄨㄞ", "chuai"], ["ㄔㄨㄢ", "chuan"], ["ㄍㄨㄤ", "guang"], ["ㄏㄨㄤ", "huang"], + ["ㄐㄧㄤ", "jiang"], ["ㄐㄩㄥ", "jiong"], ["ㄎㄨㄤ", "kuang"], ["ㄌㄧㄤ", "liang"], ["ㄋㄧㄤ", "niang"], ["ㄑㄧㄤ", "qiang"], + ["ㄑㄩㄥ", "qiong"], ["ㄕㄨㄞ", "shuai"], ["ㄕㄨㄢ", "shuan"], ["ㄒㄧㄤ", "xiang"], ["ㄒㄩㄥ", "xiong"], ["ㄓㄨㄥ", "zhong"], + ["ㄓㄨㄞ", "zhuai"], ["ㄓㄨㄢ", "zhuan"], ["ㄅㄧㄢ", "bian"], ["ㄅㄧㄠ", "biao"], ["ㄅㄧㄥ", "bing"], ["ㄔㄨㄚ", "chua"], + ["ㄔㄨㄟ", "chui"], ["ㄔㄨㄣ", "chun"], ["ㄔㄨㄛ", "chuo"], ["ㄘㄨㄥ", "cong"], ["ㄘㄨㄢ", "cuan"], ["ㄉㄧㄢ", "dian"], + ["ㄉㄧㄠ", "diao"], ["ㄉㄧㄥ", "ding"], ["ㄉㄨㄥ", "dong"], ["ㄉㄨㄢ", "duan"], ["ㄈㄧㄠ", "fiao"], ["ㄍㄧㄠ", "giao"], + ["ㄍㄧㄣ", "gin"], ["ㄍㄨㄥ", "gong"], ["ㄍㄨㄞ", "guai"], ["ㄍㄨㄢ", "guan"], ["ㄏㄨㄥ", "hong"], ["ㄏㄨㄞ", "huai"], + ["ㄏㄨㄢ", "huan"], ["ㄐㄧㄢ", "jian"], ["ㄐㄧㄠ", "jiao"], ["ㄐㄧㄥ", "jing"], ["ㄐㄩㄢ", "juan"], ["ㄎㄧㄡ", "kiu"], + ["ㄎㄨㄥ", "kong"], ["ㄎㄨㄞ", "kuai"], ["ㄎㄨㄢ", "kuan"], ["ㄌㄧㄢ", "lian"], ["ㄌㄧㄠ", "liao"], ["ㄌㄧㄥ", "ling"], + ["ㄌㄨㄥ", "long"], ["ㄌㄨㄢ", "luan"], ["ㄌㄩㄢ", "lvan"], ["ㄇㄧㄢ", "mian"], ["ㄇㄧㄠ", "miao"], ["ㄇㄧㄥ", "ming"], + ["ㄋㄧㄢ", "nian"], ["ㄋㄧㄠ", "niao"], ["ㄋㄧㄥ", "ning"], ["ㄋㄨㄥ", "nong"], ["ㄋㄨㄢ", "nuan"], ["ㄆㄧㄢ", "pian"], + ["ㄆㄧㄠ", "piao"], ["ㄆㄧㄥ", "ping"], ["ㄑㄧㄢ", "qian"], ["ㄑㄧㄠ", "qiao"], ["ㄑㄧㄥ", "qing"], ["ㄑㄩㄢ", "quan"], + ["ㄖㄨㄥ", "rong"], ["ㄖㄨㄢ", "ruan"], ["ㄕㄨㄚ", "shua"], ["ㄕㄨㄟ", "shui"], ["ㄕㄨㄣ", "shun"], ["ㄕㄨㄛ", "shuo"], + ["ㄙㄨㄥ", "song"], ["ㄙㄨㄢ", "suan"], ["ㄊㄧㄢ", "tian"], ["ㄊㄧㄠ", "tiao"], ["ㄊㄧㄥ", "ting"], ["ㄊㄨㄥ", "tong"], + ["ㄊㄨㄢ", "tuan"], ["ㄒㄧㄢ", "xian"], ["ㄒㄧㄠ", "xiao"], ["ㄒㄧㄥ", "xing"], ["ㄒㄩㄢ", "xuan"], ["ㄓㄨㄚ", "zhua"], + ["ㄓㄨㄟ", "zhui"], ["ㄓㄨㄣ", "zhun"], ["ㄓㄨㄛ", "zhuo"], ["ㄗㄨㄥ", "zong"], ["ㄗㄨㄢ", "zuan"], ["ㄈㄨㄥ", "fong"], + ["ㄐㄩㄣ", "jun"], ["ㄅㄧㄝ", "bie"], ["ㄅㄧㄣ", "bin"], ["ㄘㄨㄟ", "cui"], ["ㄘㄨㄣ", "cun"], ["ㄘㄨㄛ", "cuo"], ["ㄉㄧㄚ", "dia"], + ["ㄉㄧㄝ", "die"], ["ㄉㄧㄡ", "diu"], ["ㄉㄨㄟ", "dui"], ["ㄉㄨㄣ", "dun"], ["ㄉㄨㄛ", "duo"], ["ㄍㄨㄚ", "gua"], ["ㄍㄨㄜ", "gue"], + ["ㄍㄨㄟ", "gui"], ["ㄍㄨㄣ", "gun"], ["ㄍㄨㄛ", "guo"], ["ㄏㄨㄚ", "hua"], ["ㄏㄨㄟ", "hui"], ["ㄏㄨㄣ", "hun"], ["ㄏㄨㄛ", "huo"], + ["ㄐㄧㄚ", "jia"], ["ㄐㄧㄝ", "jie"], ["ㄐㄧㄣ", "jin"], ["ㄐㄧㄡ", "jiu"], ["ㄐㄩㄝ", "jue"], ["ㄎㄨㄚ", "kua"], ["ㄎㄨㄟ", "kui"], + ["ㄎㄨㄣ", "kun"], ["ㄎㄨㄛ", "kuo"], ["ㄌㄧㄚ", "lia"], ["ㄌㄧㄝ", "lie"], ["ㄌㄧㄣ", "lin"], ["ㄌㄧㄡ", "liu"], ["ㄌㄨㄣ", "lun"], + ["ㄌㄨㄛ", "luo"], ["ㄌㄩㄝ", "lve"], ["ㄇㄧㄝ", "mie"], ["ㄇㄧㄣ", "min"], ["ㄇㄧㄡ", "miu"], ["ㄋㄧㄝ", "nie"], ["ㄋㄧㄣ", "nin"], + ["ㄋㄧㄡ", "niu"], ["ㄋㄨㄟ", "nui"], ["ㄋㄨㄣ", "nun"], ["ㄋㄨㄛ", "nuo"], ["ㄋㄩㄝ", "nve"], ["ㄆㄧㄚ", "pia"], ["ㄆㄧㄝ", "pie"], + ["ㄆㄧㄣ", "pin"], ["ㄑㄧㄚ", "qia"], ["ㄑㄧㄝ", "qie"], ["ㄑㄧㄣ", "qin"], ["ㄑㄧㄡ", "qiu"], ["ㄑㄩㄝ", "que"], ["ㄑㄩㄣ", "qun"], + ["ㄖㄨㄟ", "rui"], ["ㄖㄨㄣ", "run"], ["ㄖㄨㄛ", "ruo"], ["ㄙㄨㄟ", "sui"], ["ㄙㄨㄣ", "sun"], ["ㄙㄨㄛ", "suo"], ["ㄊㄧㄝ", "tie"], + ["ㄊㄨㄟ", "tui"], ["ㄊㄨㄣ", "tun"], ["ㄊㄨㄛ", "tuo"], ["ㄒㄧㄚ", "xia"], ["ㄒㄧㄝ", "xie"], ["ㄒㄧㄣ", "xin"], ["ㄒㄧㄡ", "xiu"], + ["ㄒㄩㄝ", "xue"], ["ㄒㄩㄣ", "xun"], ["ㄗㄨㄟ", "zui"], ["ㄗㄨㄣ", "zun"], ["ㄗㄨㄛ", "zuo"], ["ㄘㄟ", "cei"], ["ㄔㄤ", "chang"], + ["ㄔㄥ", "cheng"], ["ㄕㄤ", "shang"], ["ㄕㄥ", "sheng"], ["ㄓㄤ", "zhang"], ["ㄓㄥ", "zheng"], ["ㄅㄤ", "bang"], + ["ㄅㄥ", "beng"], ["ㄘㄤ", "cang"], ["ㄘㄥ", "ceng"], ["ㄔㄞ", "chai"], ["ㄔㄢ", "chan"], ["ㄔㄠ", "chao"], ["ㄔㄣ", "chen"], + ["ㄔㄡ", "chou"], ["ㄉㄤ", "dang"], ["ㄉㄥ", "deng"], ["ㄈㄤ", "fang"], ["ㄈㄥ", "feng"], ["ㄍㄤ", "gang"], ["ㄍㄥ", "geng"], + ["ㄏㄤ", "hang"], ["ㄏㄥ", "heng"], ["ㄎㄤ", "kang"], ["ㄎㄥ", "keng"], ["ㄌㄤ", "lang"], ["ㄌㄥ", "leng"], ["ㄇㄤ", "mang"], + ["ㄇㄥ", "meng"], ["ㄋㄤ", "nang"], ["ㄋㄥ", "neng"], ["ㄆㄤ", "pang"], ["ㄆㄥ", "peng"], ["ㄖㄤ", "rang"], ["ㄖㄥ", "reng"], + ["ㄙㄤ", "sang"], ["ㄙㄥ", "seng"], ["ㄕㄞ", "shai"], ["ㄕㄢ", "shan"], ["ㄕㄠ", "shao"], ["ㄕㄟ", "shei"], ["ㄕㄣ", "shen"], + ["ㄕㄡ", "shou"], ["ㄊㄤ", "tang"], ["ㄊㄥ", "teng"], ["ㄨㄤ", "wang"], ["ㄨㄥ", "weng"], ["ㄧㄤ", "yang"], ["ㄧㄥ", "ying"], + ["ㄩㄥ", "yong"], ["ㄩㄢ", "yuan"], ["ㄗㄤ", "zang"], ["ㄗㄥ", "zeng"], ["ㄓㄞ", "zhai"], ["ㄓㄢ", "zhan"], ["ㄓㄠ", "zhao"], + ["ㄓㄟ", "zhei"], ["ㄓㄣ", "zhen"], ["ㄓㄡ", "zhou"], ["ㄅㄞ", "bai"], ["ㄅㄢ", "ban"], ["ㄅㄠ", "bao"], ["ㄅㄟ", "bei"], + ["ㄅㄣ", "ben"], ["ㄘㄞ", "cai"], ["ㄘㄢ", "can"], ["ㄘㄠ", "cao"], ["ㄘㄣ", "cen"], ["ㄔㄚ", "cha"], ["ㄔㄜ", "che"], + ["ㄔㄨ", "chu"], ["ㄘㄡ", "cou"], ["ㄉㄞ", "dai"], ["ㄉㄢ", "dan"], ["ㄉㄠ", "dao"], ["ㄉㄟ", "dei"], ["ㄉㄣ", "den"], + ["ㄉㄡ", "dou"], ["ㄈㄢ", "fan"], ["ㄈㄟ", "fei"], ["ㄈㄣ", "fen"], ["ㄈㄡ", "fou"], ["ㄍㄞ", "gai"], ["ㄍㄢ", "gan"], + ["ㄍㄠ", "gao"], ["ㄍㄟ", "gei"], ["ㄍㄣ", "gen"], ["ㄍㄡ", "gou"], ["ㄏㄞ", "hai"], ["ㄏㄢ", "han"], ["ㄏㄠ", "hao"], + ["ㄏㄟ", "hei"], ["ㄏㄣ", "hen"], ["ㄏㄡ", "hou"], ["ㄎㄞ", "kai"], ["ㄎㄢ", "kan"], ["ㄎㄠ", "kao"], ["ㄎㄣ", "ken"], + ["ㄎㄡ", "kou"], ["ㄌㄞ", "lai"], ["ㄌㄢ", "lan"], ["ㄌㄠ", "lao"], ["ㄌㄟ", "lei"], ["ㄌㄡ", "lou"], ["ㄇㄞ", "mai"], + ["ㄇㄢ", "man"], ["ㄇㄠ", "mao"], ["ㄇㄟ", "mei"], ["ㄇㄣ", "men"], ["ㄇㄡ", "mou"], ["ㄋㄞ", "nai"], ["ㄋㄢ", "nan"], + ["ㄋㄠ", "nao"], ["ㄋㄟ", "nei"], ["ㄋㄣ", "nen"], ["ㄋㄡ", "nou"], ["ㄆㄞ", "pai"], ["ㄆㄢ", "pan"], ["ㄆㄠ", "pao"], + ["ㄆㄟ", "pei"], ["ㄆㄣ", "pen"], ["ㄆㄡ", "pou"], ["ㄖㄢ", "ran"], ["ㄖㄠ", "rao"], ["ㄖㄣ", "ren"], ["ㄖㄡ", "rou"], + ["ㄙㄞ", "sai"], ["ㄙㄢ", "san"], ["ㄙㄠ", "sao"], ["ㄙㄟ", "sei"], ["ㄙㄣ", "sen"], ["ㄕㄚ", "sha"], ["ㄕㄜ", "she"], + ["ㄕㄨ", "shu"], ["ㄙㄡ", "sou"], ["ㄊㄞ", "tai"], ["ㄊㄢ", "tan"], ["ㄊㄠ", "tao"], ["ㄊㄡ", "tou"], ["ㄨㄞ", "wai"], + ["ㄨㄢ", "wan"], ["ㄨㄟ", "wei"], ["ㄨㄣ", "wen"], ["ㄧㄞ", "yai"], ["ㄧㄢ", "yan"], ["ㄧㄠ", "yao"], ["ㄧㄣ", "yin"], + ["ㄧㄡ", "you"], ["ㄩㄝ", "yue"], ["ㄩㄣ", "yun"], ["ㄗㄞ", "zai"], ["ㄗㄢ", "zan"], ["ㄗㄠ", "zao"], ["ㄗㄟ", "zei"], + ["ㄗㄣ", "zen"], ["ㄓㄚ", "zha"], ["ㄓㄜ", "zhe"], ["ㄓㄨ", "zhu"], ["ㄗㄡ", "zou"], ["ㄅㄚ", "ba"], ["ㄅㄧ", "bi"], + ["ㄅㄛ", "bo"], ["ㄅㄨ", "bu"], ["ㄘㄚ", "ca"], ["ㄘㄜ", "ce"], ["ㄘㄨ", "cu"], ["ㄉㄚ", "da"], ["ㄉㄜ", "de"], ["ㄉㄧ", "di"], + ["ㄉㄨ", "du"], ["ㄈㄚ", "fa"], ["ㄈㄛ", "fo"], ["ㄈㄨ", "fu"], ["ㄍㄚ", "ga"], ["ㄍㄜ", "ge"], ["ㄍㄧ", "gi"], ["ㄍㄨ", "gu"], + ["ㄏㄚ", "ha"], ["ㄏㄜ", "he"], ["ㄏㄨ", "hu"], ["ㄐㄧ", "ji"], ["ㄐㄩ", "ju"], ["ㄎㄚ", "ka"], ["ㄎㄜ", "ke"], ["ㄎㄨ", "ku"], + ["ㄌㄚ", "la"], ["ㄌㄜ", "le"], ["ㄌㄧ", "li"], ["ㄌㄛ", "lo"], ["ㄌㄨ", "lu"], ["ㄌㄩ", "lv"], ["ㄇㄚ", "ma"], ["ㄇㄜ", "me"], + ["ㄇㄧ", "mi"], ["ㄇㄛ", "mo"], ["ㄇㄨ", "mu"], ["ㄋㄚ", "na"], ["ㄋㄜ", "ne"], ["ㄋㄧ", "ni"], ["ㄋㄨ", "nu"], ["ㄋㄩ", "nv"], + ["ㄆㄚ", "pa"], ["ㄆㄧ", "pi"], ["ㄆㄛ", "po"], ["ㄆㄨ", "pu"], ["ㄑㄧ", "qi"], ["ㄑㄩ", "qu"], ["ㄖㄜ", "re"], ["ㄖㄨ", "ru"], + ["ㄙㄚ", "sa"], ["ㄙㄜ", "se"], ["ㄙㄨ", "su"], ["ㄊㄚ", "ta"], ["ㄊㄜ", "te"], ["ㄊㄧ", "ti"], ["ㄊㄨ", "tu"], ["ㄨㄚ", "wa"], + ["ㄨㄛ", "wo"], ["ㄒㄧ", "xi"], ["ㄒㄩ", "xu"], ["ㄧㄚ", "ya"], ["ㄧㄝ", "ye"], ["ㄧㄛ", "yo"], ["ㄗㄚ", "za"], ["ㄗㄜ", "ze"], + ["ㄗㄨ", "zu"], ["ㄅ", "b"], ["ㄆ", "p"], ["ㄇ", "m"], ["ㄈ", "f"], ["ㄉ", "d"], ["ㄊ", "t"], ["ㄋ", "n"], + ["ㄌ", "l"], ["ㄍ", "g"], ["ㄎ", "k"], ["ㄏ", "h"], ["ㄐ", "j"], ["ㄑ", "q"], ["ㄒ", "x"], ["ㄓ", "zhi"], + ["ㄔ", "chi"], ["ㄕ", "shi"], ["ㄖ", "ri"], ["ㄗ", "zi"], ["ㄘ", "ci"], ["ㄙ", "si"], ["ㄚ", "a"], ["ㄛ", "o"], ["ㄜ", "e"], + ["ㄝ", "eh"], ["ㄞ", "ai"], ["ㄟ", "ei"], ["ㄠ", "ao"], ["ㄡ", "ou"], ["ㄢ", "an"], ["ㄣ", "en"], ["ㄤ", "ang"], + ["ㄥ", "eng"], ["ㄦ", "er"], ["ㄧ", "yi"], ["ㄨ", "wu"], ["ㄩ", "yu"], + ] + + /// 漢語拼音韻母轉換對照表資料貯存專用佇列 + static let arrHanyuPinyinTextbookStyleConversionTable = [ // 排序很重要。先處理最長的,再處理短的。不然會出亂子。 + ["iang1", "iāng"], ["iang2", "iáng"], ["iang3", "iǎng"], ["iang4", "iàng"], ["iong1", "iōng"], ["iong2", "ióng"], + ["iong3", "iǒng"], ["iong4", "iòng"], ["uang1", "uāng"], ["uang2", "uáng"], ["uang3", "uǎng"], ["uang4", "uàng"], + ["uang5", "uang"], ["ang1", "āng"], ["ang2", "áng"], ["ang3", "ǎng"], ["ang4", "àng"], ["ang5", "ang"], + ["eng1", "ēng"], ["eng2", "éng"], ["eng3", "ěng"], ["eng4", "èng"], ["ian1", "iān"], ["ian2", "ián"], + ["ian3", "iǎn"], ["ian4", "iàn"], ["iao1", "iāo"], ["iao2", "iáo"], ["iao3", "iǎo"], ["iao4", "iào"], + ["ing1", "īng"], ["ing2", "íng"], ["ing3", "ǐng"], ["ing4", "ìng"], ["ong1", "ōng"], ["ong2", "óng"], + ["ong3", "ǒng"], ["ong4", "òng"], ["uai1", "uāi"], ["uai2", "uái"], ["uai3", "uǎi"], ["uai4", "uài"], + ["uan1", "uān"], ["uan2", "uán"], ["uan3", "uǎn"], ["uan4", "uàn"], ["van2", "üán"], ["van3", "üǎn"], + ["ai1", "āi"], ["ai2", "ái"], ["ai3", "ǎi"], ["ai4", "ài"], ["ai5", "ai"], ["an1", "ān"], ["an2", "án"], + ["an3", "ǎn"], ["an4", "àn"], ["ao1", "āo"], ["ao2", "áo"], ["ao3", "ǎo"], ["ao4", "ào"], ["ao5", "ao"], + ["eh2", "ế"], ["eh3", "êˇ"], ["eh4", "ề"], ["eh5", "ê"], ["ei1", "ēi"], ["ei2", "éi"], ["ei3", "ěi"], + ["ei4", "èi"], ["ei5", "ei"], ["en1", "ēn"], ["en2", "én"], ["en3", "ěn"], ["en4", "èn"], ["en5", "en"], + ["er1", "ēr"], ["er2", "ér"], ["er3", "ěr"], ["er4", "èr"], ["er5", "er"], ["ia1", "iā"], ["ia2", "iá"], + ["ia3", "iǎ"], ["ia4", "ià"], ["ie1", "iē"], ["ie2", "ié"], ["ie3", "iě"], ["ie4", "iè"], ["ie5", "ie"], + ["in1", "īn"], ["in2", "ín"], ["in3", "ǐn"], ["in4", "ìn"], ["iu1", "iū"], ["iu2", "iú"], ["iu3", "iǔ"], + ["iu4", "iù"], ["ou1", "ōu"], ["ou2", "óu"], ["ou3", "ǒu"], ["ou4", "òu"], ["ou5", "ou"], ["ua1", "uā"], + ["ua2", "uá"], ["ua3", "uǎ"], ["ua4", "uà"], ["ue1", "uē"], ["ue2", "ué"], ["ue3", "uě"], ["ue4", "uè"], + ["ui1", "uī"], ["ui2", "uí"], ["ui3", "uǐ"], ["ui4", "uì"], ["un1", "ūn"], ["un2", "ún"], ["un3", "ǔn"], + ["un4", "ùn"], ["uo1", "uō"], ["uo2", "uó"], ["uo3", "uǒ"], ["uo4", "uò"], ["uo5", "uo"], ["ve1", "üē"], + ["ve3", "üě"], ["ve4", "üè"], ["a1", "ā"], ["a2", "á"], ["a3", "ǎ"], ["a4", "à"], ["a5", "a"], ["e1", "ē"], + ["e2", "é"], ["e3", "ě"], ["e4", "è"], ["e5", "e"], ["i1", "ī"], ["i2", "í"], ["i3", "ǐ"], ["i4", "ì"], + ["i5", "i"], ["o1", "ō"], ["o2", "ó"], ["o3", "ǒ"], ["o4", "ò"], ["o5", "o"], ["u1", "ū"], ["u2", "ú"], + ["u3", "ǔ"], ["u4", "ù"], ["v1", "ǖ"], ["v2", "ǘ"], ["v3", "ǚ"], ["v4", "ǜ"], + ] + // MARK: - Maps for Keyboard-to-Phonabet parsers - // 任何形式的拼音排列都會用到的陣列,用 Strings 反而省事一些。 + /// 任何形式的拼音排列都會用到的陣列,用 Strings 反而省事一些。 static let mapArayuruPinyin: String = "abcdefghijklmnopqrstuvwxyz12345 " /// 標準大千排列專用處理陣列。 + /// @--DISCUSSION--@ /// 威注音輸入法 macOS 版使用了 Ukelele 佈局來完成對諸如倚天傳統等其它注音鍵盤排列的支援。 /// 如果要將鐵恨模組拿給別的平台的輸入法使用的話,恐怕需要針對這些注音鍵盤排列各自新增專用陣列才可以。 static let mapQwertyDachen: [String: String] = [ @@ -487,6 +713,7 @@ public struct Tekkon { ] /// 許氏排列專用處理陣列,但未包含全部的映射內容。 + /// @--DISCUSSION--@ /// 在這裡將二十六個字母寫全,也只是為了方便做 validity check。 /// 這裡提前對複音按鍵做處理,然後再用程式判斷介母類型、據此判斷是否需要做複音切換。 static let mapHsuStaticKeys: [String: String] = [ @@ -496,6 +723,7 @@ public struct Tekkon { ] /// 倚天忘形排列預處理專用陣列,但未包含全部的映射內容。 + /// @--DISCUSSION--@ /// 在這裡將二十六個字母寫全,也只是為了方便做 validity check。 /// 這裡提前對ㄓ/ㄍ/ㄕ做處理,然後再用程式判斷介母類型、據此判斷是否需要換成ㄒ/ㄑ/ㄐ。 static let mapEten26StaticKeys: [String: String] = [ From 94fcf061d25fe930b1e7841ca530f192d3a513c2 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 17:00:44 +0800 Subject: [PATCH 11/19] mgrPrefs // +showHanyuPinyinInCompositionBuffer. --- Source/Modules/IMEModules/mgrPrefs.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index 1a38a935..bf345dd5 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -53,6 +53,7 @@ struct UserDef { static let kUseSCPCTypingMode = "UseSCPCTypingMode" static let kMaxCandidateLength = "MaxCandidateLength" static let kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep" + static let kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer" static let kCandidateTextFontName = "CandidateTextFontName" static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" @@ -230,6 +231,7 @@ public class mgrPrefs: NSObject { UserDef.kUseSCPCTypingMode, UserDef.kMaxCandidateLength, UserDef.kShouldNotFartInLieuOfBeep, + UserDef.kShowHanyuPinyinInCompositionBuffer, UserDef.kAssociatedPhrasesEnabled, ] } @@ -273,6 +275,8 @@ public class mgrPrefs: NSObject { ) UserDefaults.standard.setDefault(mgrPrefs.phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled) UserDefaults.standard.setDefault(mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) + UserDefaults.standard.setDefault( + mgrPrefs.showHanyuPinyinInCompositionBuffer, forKey: UserDef.kShowHanyuPinyinInCompositionBuffer) UserDefaults.standard.synchronize() } @@ -352,6 +356,9 @@ public class mgrPrefs: NSObject { @UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep, defaultValue: true) static var shouldNotFartInLieuOfBeep: Bool + @UserDefault(key: UserDef.kShowHanyuPinyinInCompositionBuffer, defaultValue: false) + static var showHanyuPinyinInCompositionBuffer: Bool + static func toggleShouldNotFartInLieuOfBeep() -> Bool { shouldNotFartInLieuOfBeep = !shouldNotFartInLieuOfBeep UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) From 878c53ab44adbb0e144fde2cce615f9c00a9f4d1 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 18:45:57 +0800 Subject: [PATCH 12/19] mgrPref // +InlineDumpPinyinInLieuOfZhuyin. --- Source/Modules/IMEModules/mgrPrefs.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index bf345dd5..8a99c805 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -54,6 +54,7 @@ struct UserDef { static let kMaxCandidateLength = "MaxCandidateLength" static let kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep" static let kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer" + static let kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin" static let kCandidateTextFontName = "CandidateTextFontName" static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" @@ -232,6 +233,7 @@ public class mgrPrefs: NSObject { UserDef.kMaxCandidateLength, UserDef.kShouldNotFartInLieuOfBeep, UserDef.kShowHanyuPinyinInCompositionBuffer, + UserDef.kInlineDumpPinyinInLieuOfZhuyin, UserDef.kAssociatedPhrasesEnabled, ] } @@ -277,6 +279,8 @@ public class mgrPrefs: NSObject { UserDefaults.standard.setDefault(mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) UserDefaults.standard.setDefault( mgrPrefs.showHanyuPinyinInCompositionBuffer, forKey: UserDef.kShowHanyuPinyinInCompositionBuffer) + UserDefaults.standard.setDefault( + mgrPrefs.inlineDumpPinyinInLieuOfZhuyin, forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin) UserDefaults.standard.synchronize() } @@ -359,6 +363,9 @@ public class mgrPrefs: NSObject { @UserDefault(key: UserDef.kShowHanyuPinyinInCompositionBuffer, defaultValue: false) static var showHanyuPinyinInCompositionBuffer: Bool + @UserDefault(key: UserDef.kInlineDumpPinyinInLieuOfZhuyin, defaultValue: false) + static var inlineDumpPinyinInLieuOfZhuyin: Bool + static func toggleShouldNotFartInLieuOfBeep() -> Bool { shouldNotFartInLieuOfBeep = !shouldNotFartInLieuOfBeep UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) From 61b25630620c3a69506f6d226332374f46fd920e Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 17:01:57 +0800 Subject: [PATCH 13/19] KeyHandler // Integrate showHanyuPinyinInCompositionBuffer. --- Source/Modules/ControllerModules/KeyHandler_States.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 78fc8c1f..97312be3 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -89,7 +89,7 @@ extension KeyHandler { } let head = rawHead - let reading = _composer.getDisplayedComposition() + let reading = _composer.getComposition(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer) let tail = rawEnd let composedText = head + reading + tail let cursorIndex = composedStringCursorIndex + reading.count From cf3d400858600423529e8856e2dc34ab015ae30d Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 19:16:26 +0800 Subject: [PATCH 14/19] KeyHandler // Integrate InlineDumpPinyinInLieuOfZhuyin. --- .../ControllerModules/KeyHandler_Misc.swift | 27 +++++++++++++++++++ .../ControllerModules/KeyHandler_States.swift | 25 ++++++++++++----- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Misc.swift b/Source/Modules/ControllerModules/KeyHandler_Misc.swift index ad15ee8e..449a43bd 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Misc.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Misc.swift @@ -51,4 +51,31 @@ extension KeyHandler { } return cursorIndex } + + // 用於網頁 Ruby 的注音需要按照教科書印刷的方式來顯示輕聲,所以這裡處理一下。 + func cnvZhuyinKeyToTextbookReading(target: String, newSeparator: String = "-") -> String { + var arrReturn: [String] = [] + for neta in target.split(separator: "-") { + var newString = String(neta) + if String(neta.reversed()[0]) == "˙" { + newString = String(neta.dropLast()) + newString.insert("˙", at: newString.startIndex) + } + arrReturn.append(newString) + } + return arrReturn.joined(separator: newSeparator) + } + + // 用於網頁 Ruby 的拼音的陰平必須顯示,這裡處理一下。 + func restoreToneOneInZhuyinKey(target: String, newSeparator: String = "-") -> String { + var arrReturn: [String] = [] + for neta in target.split(separator: "-") { + var newNeta = String(neta) + if !"ˊˇˋ˙".contains(String(neta.reversed()[0])), !neta.contains("_") { + newNeta += "1" + } + arrReturn.append(newNeta) + } + return arrReturn.joined(separator: newSeparator) + } } diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 97312be3..f547322b 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -281,11 +281,15 @@ extension KeyHandler { return false } - let readings: [String] = currentReadings() - let composingBuffer = - (IME.areWeUsingOurOwnPhraseEditor) - ? readings.joined(separator: "-") - : readings.joined(separator: " ") + var composingBuffer = currentReadings().joined(separator: "-") + if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin { + composingBuffer = restoreToneOneInZhuyinKey(target: composingBuffer) // 恢復陰平標記 + composingBuffer = Tekkon.cnvPhonaToHanyuPinyin(target: composingBuffer) // 注音轉拼音 + } + + if !IME.areWeUsingOurOwnPhraseEditor { + composingBuffer = composingBuffer.replacingOccurrences(of: "-", with: " ") + } clear() @@ -309,7 +313,16 @@ extension KeyHandler { for theAnchor in _walkedNodes { if let node = theAnchor.node { - let key = node.currentKeyValue().key.replacingOccurrences(of: "-", with: " ") + var key = node.currentKeyValue().key + if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin { + key = restoreToneOneInZhuyinKey(target: key) // 恢復陰平標記 + key = Tekkon.cnvPhonaToHanyuPinyin(target: key) // 注音轉拼音 + key = Tekkon.cnvHanyuPinyinToTextbookStyle(target: key) // 轉教科書式標調 + key = key.replacingOccurrences(of: "-", with: " ") + } else { + key = cnvZhuyinKeyToTextbookReading(target: key, newSeparator: " ") + } + let value = node.currentKeyValue().value if key.contains("_") { // 不要給標點符號等特殊元素加注音 composed += value From 4e5e10dfbbcb7db0916f42fe363efd76e7110e70 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 17:05:49 +0800 Subject: [PATCH 15/19] PrefUI // +showHanyuPinyinInCompositionBuffer. --- 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 + Source/UI/PrefUI/suiPrefPaneGeneral.swift | 9 +++++++++ 6 files changed, 14 insertions(+) diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 223c3dfd..6861371e 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -132,6 +132,7 @@ "Phonetic Parser:" = "Phonetic Parser:"; "Push the cursor in front of the phrase after selection" = "Push the cursor in front of the phrase after selection"; "Selection Keys:" = "Selection Keys:"; +"Show Hanyu-Pinyin in the inline composition buffer" = "Show Hanyu-Pinyin in the inline composition buffer"; "Show page buttons in candidate window" = "Show page buttons in candidate window"; "Simplified Chinese" = "Simplified Chinese"; "Space & ESC Key:" = "Space & ESC Key:"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 223c3dfd..6861371e 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -132,6 +132,7 @@ "Phonetic Parser:" = "Phonetic Parser:"; "Push the cursor in front of the phrase after selection" = "Push the cursor in front of the phrase after selection"; "Selection Keys:" = "Selection Keys:"; +"Show Hanyu-Pinyin in the inline composition buffer" = "Show Hanyu-Pinyin in the inline composition buffer"; "Show page buttons in candidate window" = "Show page buttons in candidate window"; "Simplified Chinese" = "Simplified Chinese"; "Space & ESC Key:" = "Space & ESC Key:"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index 50be3fbf..6efa35c3 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -132,6 +132,7 @@ "Phonetic Parser:" = "注音配列:"; "Push the cursor in front of the phrase after selection" = "候補選択の直後、すぐカーソルを単語の向こうに推し進める"; "Selection Keys:" = "言選り用キー:"; +"Show Hanyu-Pinyin in the inline composition buffer" = "弁音合併入力(入力緩衝列で代わりに漢語弁音の音読み)"; "Show page buttons in candidate window" = "入力候補陳列の側にページボタンを表示"; "Simplified Chinese" = "簡体中国語"; "Space & ESC Key:" = "ESC と Space:"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 2d33f0be..6de43112 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -132,6 +132,7 @@ "Phonetic Parser:" = "注音排列:"; "Push the cursor in front of the phrase after selection" = "在选字后将游标置于该字词的前方"; "Selection Keys:" = "选字键:"; +"Show Hanyu-Pinyin in the inline composition buffer" = "拼音并击模式(组字区内看到的是汉语拼音)"; "Show page buttons in candidate window" = "在选字窗内显示翻页按钮"; "Simplified Chinese" = "简体中文"; "Space & ESC Key:" = "ESC 与空格键:"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 983f74ac..69f289a4 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -132,6 +132,7 @@ "Phonetic Parser:" = "注音排列:"; "Push the cursor in front of the phrase after selection" = "在選字後將游標置於該字詞的前方"; "Selection Keys:" = "選字鍵:"; +"Show Hanyu-Pinyin in the inline composition buffer" = "拼音並擊模式(組字區內看到的是漢語拼音)"; "Show page buttons in candidate window" = "在選字窗內顯示翻頁按鈕"; "Simplified Chinese" = "簡體中文"; "Space & ESC Key:" = "ESC 與空格鍵:"; diff --git a/Source/UI/PrefUI/suiPrefPaneGeneral.swift b/Source/UI/PrefUI/suiPrefPaneGeneral.swift index 83497d62..db1a3507 100644 --- a/Source/UI/PrefUI/suiPrefPaneGeneral.swift +++ b/Source/UI/PrefUI/suiPrefPaneGeneral.swift @@ -43,6 +43,8 @@ struct suiPrefPaneGeneral: View { forKey: UserDef.kChineseConversionEnabled) @State private var selEnableKanjiConvToJIS = UserDefaults.standard.bool( forKey: UserDef.kShiftJISShinjitaiOutputEnabled) + @State private var selShowHanyuPinyinInCompositionBuffer = UserDefaults.standard.bool( + forKey: UserDef.kShowHanyuPinyinInCompositionBuffer) @State private var selEnableFartSuppressor = UserDefaults.standard.bool(forKey: UserDef.kShouldNotFartInLieuOfBeep) @State private var selEnableAutoUpdateCheck = UserDefaults.standard.bool(forKey: UserDef.kCheckUpdateAutomatically) @State private var selEnableDebugMode = UserDefaults.standard.bool(forKey: UserDef.kIsDebugModeEnabled) @@ -147,6 +149,13 @@ struct suiPrefPaneGeneral: View { selEnableKanjiConvToKangXi = !value } } + Toggle( + LocalizedStringKey("Show Hanyu-Pinyin in the inline composition buffer"), + isOn: $selShowHanyuPinyinInCompositionBuffer + ).onChange(of: selShowHanyuPinyinInCompositionBuffer) { value in + mgrPrefs.showHanyuPinyinInCompositionBuffer = value + selShowHanyuPinyinInCompositionBuffer = value + } Toggle( LocalizedStringKey("Stop farting (when typed phonetic combination is invalid, etc.)"), isOn: $selEnableFartSuppressor From 00f91e1f10875e94b696f5940b57bb131bc07438 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 19:18:37 +0800 Subject: [PATCH 16/19] PrefUI // +InlineDumpPinyinInLieuOfZhuyin. --- 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 + Source/UI/PrefUI/suiPrefPaneGeneral.swift | 9 +++++++++ 6 files changed, 14 insertions(+) diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 6861371e..743c812f 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -128,6 +128,7 @@ "Misc Settings:" = "Misc Settings:"; "MiTAC" = "MiTAC"; "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only." = "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only."; +"Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter" = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"; "Output Settings:" = "Output Settings:"; "Phonetic Parser:" = "Phonetic Parser:"; "Push the cursor in front of the phrase after selection" = "Push the cursor in front of the phrase after selection"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 6861371e..743c812f 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -128,6 +128,7 @@ "Misc Settings:" = "Misc Settings:"; "MiTAC" = "MiTAC"; "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only." = "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only."; +"Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter" = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"; "Output Settings:" = "Output Settings:"; "Phonetic Parser:" = "Phonetic Parser:"; "Push the cursor in front of the phrase after selection" = "Push the cursor in front of the phrase after selection"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index 6efa35c3..65baca8c 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -128,6 +128,7 @@ "Misc Settings:" = "他の設定:"; "MiTAC" = "神通配列"; "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only." = "QWERTY 以外の英数キーボードは漢語弁音以外の配列に不適用。"; +"Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter" = "Ctrl(+Alt)+CMD+Enter で出すのを漢語弁音と変換"; "Output Settings:" = "出力設定:"; "Phonetic Parser:" = "注音配列:"; "Push the cursor in front of the phrase after selection" = "候補選択の直後、すぐカーソルを単語の向こうに推し進める"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 6de43112..8e53b332 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -128,6 +128,7 @@ "Misc Settings:" = "杂项:"; "MiTAC" = "神通排列"; "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only." = "QWERTY 以外的英数布局是为了汉语拼音排列使用者而准备的。"; +"Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter" = "Ctrl(+Alt)+CMD+Enter 输出汉语拼音而非注音"; "Output Settings:" = "输出设定:"; "Phonetic Parser:" = "注音排列:"; "Push the cursor in front of the phrase after selection" = "在选字后将游标置于该字词的前方"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 69f289a4..bd7d11a4 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -128,6 +128,7 @@ "Misc Settings:" = "雜項:"; "MiTAC" = "神通排列"; "Non-QWERTY alphanumeral keyboard layouts are for Hanyu Pinyin parser only." = "QWERTY 以外的英數佈局是為了漢語拼音排列使用者而準備的。"; +"Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter" = "Ctrl(+Alt)+CMD+Enter 輸出漢語拼音而非注音"; "Output Settings:" = "輸出設定:"; "Phonetic Parser:" = "注音排列:"; "Push the cursor in front of the phrase after selection" = "在選字後將游標置於該字詞的前方"; diff --git a/Source/UI/PrefUI/suiPrefPaneGeneral.swift b/Source/UI/PrefUI/suiPrefPaneGeneral.swift index db1a3507..c77cf25c 100644 --- a/Source/UI/PrefUI/suiPrefPaneGeneral.swift +++ b/Source/UI/PrefUI/suiPrefPaneGeneral.swift @@ -45,6 +45,8 @@ struct suiPrefPaneGeneral: View { forKey: UserDef.kShiftJISShinjitaiOutputEnabled) @State private var selShowHanyuPinyinInCompositionBuffer = UserDefaults.standard.bool( forKey: UserDef.kShowHanyuPinyinInCompositionBuffer) + @State private var selInlineDumpPinyinInLieuOfZhuyin = UserDefaults.standard.bool( + forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin) @State private var selEnableFartSuppressor = UserDefaults.standard.bool(forKey: UserDef.kShouldNotFartInLieuOfBeep) @State private var selEnableAutoUpdateCheck = UserDefaults.standard.bool(forKey: UserDef.kCheckUpdateAutomatically) @State private var selEnableDebugMode = UserDefaults.standard.bool(forKey: UserDef.kIsDebugModeEnabled) @@ -156,6 +158,13 @@ struct suiPrefPaneGeneral: View { mgrPrefs.showHanyuPinyinInCompositionBuffer = value selShowHanyuPinyinInCompositionBuffer = value } + Toggle( + LocalizedStringKey("Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"), + isOn: $selInlineDumpPinyinInLieuOfZhuyin + ).onChange(of: selInlineDumpPinyinInLieuOfZhuyin) { value in + mgrPrefs.inlineDumpPinyinInLieuOfZhuyin = value + selInlineDumpPinyinInLieuOfZhuyin = value + } Toggle( LocalizedStringKey("Stop farting (when typed phonetic combination is invalid, etc.)"), isOn: $selEnableFartSuppressor From a05ecdff839add0e46d88792228f9235d8eb260f Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 21:38:08 +0800 Subject: [PATCH 17/19] PreWindow // Sync recent changes from PrefUI. --- .../WindowNIBs/Base.lproj/frmPrefWindow.xib | 55 ++++++++++++++----- .../WindowNIBs/en.lproj/frmPrefWindow.strings | 6 ++ .../WindowNIBs/ja.lproj/frmPrefWindow.strings | 6 ++ .../zh-Hans.lproj/frmPrefWindow.strings | 6 ++ .../zh-Hant.lproj/frmPrefWindow.strings | 6 ++ 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib index c0a57c04..dd35eeab 100644 --- a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib +++ b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib @@ -1,6 +1,7 @@ + @@ -162,7 +163,7 @@ + + - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/Source/WindowNIBs/en.lproj/frmPrefWindow.strings b/Source/WindowNIBs/en.lproj/frmPrefWindow.strings index cdb7a9f6..1175822a 100644 --- a/Source/WindowNIBs/en.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/en.lproj/frmPrefWindow.strings @@ -226,3 +226,9 @@ /* Class = "NSTextFieldCell"; title = "Choose your desired user data folder path. Will be omitted if invalid."; ObjectID = "wN3-k3-b2a"; */ "wN3-k3-b2a.title" = "Choose your desired user data folder path. Will be omitted if invalid."; + +/* Class = "NSButtonCell"; title = "Show Hanyu-Pinyin in the inline composition buffer"; ObjectID = "wFR-zX-M8H"; */ +"wFR-zX-M8H.title" = "Show Hanyu-Pinyin in the inline composition buffer"; + +/* Class = "NSButtonCell"; title = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"; ObjectID = "iWy-Nw-QKB"; */ +"iWy-Nw-QKB.title" = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"; diff --git a/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings b/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings index 44fb5fb7..2a891d5d 100644 --- a/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings @@ -226,3 +226,9 @@ /* Class = "NSTextFieldCell"; title = "Choose your desired user data folder path. Will be omitted if invalid."; ObjectID = "wN3-k3-b2a"; */ "wN3-k3-b2a.title" = "欲しがるユーザー辞書保存先をご指定ください。無効の保存先設定は効かぬ。"; + +/* Class = "NSButtonCell"; title = "Show Hanyu-Pinyin in the inline composition buffer"; ObjectID = "wFR-zX-M8H"; */ +"wFR-zX-M8H.title" = "弁音合併入力(入力緩衝列で代わりに漢語弁音の音読み)"; + +/* Class = "NSButtonCell"; title = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"; ObjectID = "iWy-Nw-QKB"; */ +"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter で出すのを漢語弁音と変換"; diff --git a/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings b/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings index dcacae51..d660e0f4 100644 --- a/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings @@ -226,3 +226,9 @@ /* Class = "NSTextFieldCell"; title = "Choose your desired user data folder path. Will be omitted if invalid."; ObjectID = "wN3-k3-b2a"; */ "wN3-k3-b2a.title" = "请在此指定您想指定的使用者语汇档案目录。无效值会被忽略。"; + +/* Class = "NSButtonCell"; title = "Show Hanyu-Pinyin in the inline composition buffer"; ObjectID = "wFR-zX-M8H"; */ +"wFR-zX-M8H.title" = "拼音并击模式(组字区内看到的是汉语拼音)"; + +/* Class = "NSButtonCell"; title = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"; ObjectID = "iWy-Nw-QKB"; */ +"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter 输出汉语拼音而非注音"; diff --git a/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings b/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings index 7f33d2f9..efc928c4 100644 --- a/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings @@ -226,3 +226,9 @@ /* Class = "NSTextFieldCell"; title = "Choose your desired user data folder path. Will be omitted if invalid."; ObjectID = "wN3-k3-b2a"; */ "wN3-k3-b2a.title" = "請在此指定您想指定的使用者語彙檔案目錄。無效值會被忽略。"; + +/* Class = "NSButtonCell"; title = "Show Hanyu-Pinyin in the inline composition buffer"; ObjectID = "wFR-zX-M8H"; */ +"wFR-zX-M8H.title" = "拼音並擊模式(組字區內看到的是漢語拼音)"; + +/* Class = "NSButtonCell"; title = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter"; ObjectID = "iWy-Nw-QKB"; */ +"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter 輸出漢語拼音而非注音"; From 8685d9a3290948c11acb21421fd73794ea6054ec Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 22:16:09 +0800 Subject: [PATCH 18/19] Update Data - 20220514 --- Source/Data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Data b/Source/Data index 5abc6821..383ad9af 160000 --- a/Source/Data +++ b/Source/Data @@ -1 +1 @@ -Subproject commit 5abc68212bba31114197bdfee08dd615d89878bd +Subproject commit 383ad9af7dd6f9ffa58ad2560e186bf06ffea3ad From 81aa6a1f0be62e393ac515d64319161149b0bfcb Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 14 May 2022 22:16:37 +0800 Subject: [PATCH 19/19] Bump version to 1.5.8 Build 1958. --- Update-Info.plist | 4 ++-- vChewing.pkgproj | 2 +- vChewing.xcodeproj/project.pbxproj | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Update-Info.plist b/Update-Info.plist index 8e23f3f1..c718e716 100644 --- a/Update-Info.plist +++ b/Update-Info.plist @@ -3,9 +3,9 @@ CFBundleShortVersionString - 1.5.7 + 1.5.8 CFBundleVersion - 1957 + 1958 UpdateInfoEndpoint https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist UpdateInfoSite diff --git a/vChewing.pkgproj b/vChewing.pkgproj index 5b1183d0..eab600c5 100644 --- a/vChewing.pkgproj +++ b/vChewing.pkgproj @@ -726,7 +726,7 @@ USE_HFS+_COMPRESSION VERSION - 1.5.7 + 1.5.8 TYPE 0 diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 2dbab490..dea262d5 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -1308,7 +1308,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1957; + CURRENT_PROJECT_VERSION = 1958; DEBUG_INFORMATION_FORMAT = dwarf; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; @@ -1331,7 +1331,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11.5; - MARKETING_VERSION = 1.5.7; + MARKETING_VERSION = 1.5.8; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1364,7 +1364,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1957; + CURRENT_PROJECT_VERSION = 1958; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1383,7 +1383,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11.5; - MARKETING_VERSION = 1.5.7; + MARKETING_VERSION = 1.5.8; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1498,7 +1498,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1957; + CURRENT_PROJECT_VERSION = 1958; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1533,7 +1533,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11.5; - MARKETING_VERSION = 1.5.7; + MARKETING_VERSION = 1.5.8; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1565,7 +1565,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1957; + CURRENT_PROJECT_VERSION = 1958; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -1595,7 +1595,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11.5; - MARKETING_VERSION = 1.5.7; + MARKETING_VERSION = 1.5.8; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1678,7 +1678,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1957; + CURRENT_PROJECT_VERSION = 1958; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -1703,7 +1703,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11.5; - MARKETING_VERSION = 1.5.7; + MARKETING_VERSION = 1.5.8; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1730,7 +1730,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1957; + CURRENT_PROJECT_VERSION = 1958; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1750,7 +1750,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.11.5; - MARKETING_VERSION = 1.5.7; + MARKETING_VERSION = 1.5.8; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "";