Repo // Massive nomenclature changes, etc.
This commit is contained in:
parent
458e7fa613
commit
6168cc32d0
2
FAQ.md
2
FAQ.md
|
@ -90,6 +90,6 @@ xattr -dr com.apple.quarantine ~/Downloads/vChewingInstaller.app
|
|||
|
||||
### 威注音 2.7.0 版好像改很兇,但又幾乎沒有新功能。究竟改了哪些內容?
|
||||
|
||||
威注音 2.7.0 是威注音的 Dezonblization 行動的一部分,旨在讓在此之後的研發流程更能順利進行。藉由將整個專案大卸八塊、拆成各個以 Swift Package 為單位的各種組件,以及對大中心派發技術(Grand Central Dispatch)的合理運用,威注音得以做到更快的專案建置速度、更快的辭典載入速度、更清晰且更易於維護的專案結構框架。至於放棄 macOS 10.11-10.12 的支援,則是因為 NSGridView 以及 Xcode 14 的雙雙限制使然。藉由這些改動,威注音今後在理論上就可以引入對 KeyHandler 等元件的更有效的單元測試。
|
||||
威注音 2.7.0 是威注音的 Dezonblization 行動的一部分,旨在讓在此之後的研發流程更能順利進行。藉由將整個專案大卸八塊、拆成各個以 Swift Package 為單位的各種組件,以及對大中心派發技術(Grand Central Dispatch)的合理運用,威注音得以做到更快的專案建置速度、更快的辭典載入速度、更清晰且更易於維護的專案結構框架。至於放棄 macOS 10.11-10.12 的支援,則是因為 NSGridView 以及 Xcode 14 的雙雙限制使然。藉由這些改動,威注音今後在理論上就可以引入對 InputHandler 等元件的更有效的單元測試。
|
||||
|
||||
$ EOF.
|
||||
|
|
|
@ -106,7 +106,7 @@ extension NSEvent {
|
|||
// 這句用來返回換算結果。
|
||||
public var mainAreaNumKeyChar: String? { mapMainAreaNumKey[keyCode] }
|
||||
|
||||
// 除了 ANSI charCode 以外,其餘一律過濾掉,免得純 Swift 版 KeyHandler 被餵屎。
|
||||
// 除了 ANSI charCode 以外,其餘一律過濾掉,免得 InputHandler 被餵屎。
|
||||
public var isInvalid: Bool {
|
||||
(0x20...0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted)
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ Megrez Engine is a module made for processing lingual data of an input method. T
|
|||
|
||||
### §1. 初期化
|
||||
|
||||
在你的 IMKInputController 或者 KeyHandler 內初期化一份 Megrez.Compositor 組字器副本(這裡將該副本命名為「`compositor`」)。由於 Megrez.Compositor 的型別是 Struct 型別(為了讓 Compositor 可以 deep copy),所以其副本可以用 var 來宣告。
|
||||
在你的 IMKInputController 或者 InputHandler 內初期化一份 Megrez.Compositor 組字器副本(這裡將該副本命名為「`compositor`」)。由於 Megrez.Compositor 的型別是 Struct 型別(為了讓 Compositor 可以 deep copy),所以其副本可以用 var 來宣告。
|
||||
|
||||
以 KeyHandler 為例:
|
||||
以 InputHandler 為例:
|
||||
```swift
|
||||
class KeyHandler {
|
||||
class InputHandler {
|
||||
// 先設定好變數
|
||||
var compositor: Megrez.Compositor = .init()
|
||||
...
|
||||
|
@ -34,7 +34,7 @@ class IMKMyInputController: IMKInputController {
|
|||
}
|
||||
```
|
||||
|
||||
由於 Swift 會在某個大副本(KeyHandler 或者 IMKInputController 副本)被銷毀的時候自動銷毀其中的全部副本,所以 Megrez.Compositor 的副本初期化沒必要寫在 init() 當中。但你很可能會想在 init() 時指定 Tekkon.Composer 所對接的語言模組型別、以及其可以允許的最大詞長。
|
||||
由於 Swift 會在某個大副本(InputHandler 或者 IMKInputController 副本)被銷毀的時候自動銷毀其中的全部副本,所以 Megrez.Compositor 的副本初期化沒必要寫在 init() 當中。但你很可能會想在 init() 時指定 Tekkon.Composer 所對接的語言模組型別、以及其可以允許的最大詞長。
|
||||
|
||||
這裡就需要在 init() 時使用參數:
|
||||
```swift
|
||||
|
|
|
@ -11,7 +11,7 @@ import Cocoa
|
|||
// 所有 IMEState 均遵守該協定:
|
||||
public protocol IMEStateProtocol {
|
||||
var type: StateType { get }
|
||||
var data: StateDataProtocol { get }
|
||||
var data: IMEStateDataProtocol { get }
|
||||
var isASCIIMode: Bool { get set }
|
||||
var isVerticalTyping: Bool { get set }
|
||||
var isVerticalCandidateWindow: Bool { get set }
|
||||
|
@ -36,7 +36,7 @@ public protocol IMEStateProtocol {
|
|||
var marker: Int { get set }
|
||||
}
|
||||
|
||||
public protocol StateDataProtocol {
|
||||
public protocol IMEStateDataProtocol {
|
||||
var cursor: Int { get set }
|
||||
var marker: Int { get set }
|
||||
var markedRange: Range<Int> { get }
|
||||
|
|
|
@ -23,11 +23,11 @@ Regarding pinyin input support, we only support: Hanyu Pinyin, Secondary Pinyin,
|
|||
|
||||
### §1. 初期化
|
||||
|
||||
在你的 IMKInputController (InputMethodController) 或者 KeyHandler 內初期化一份 Tekkon.Composer 注拼槽副本(這裡將該副本命名為「`_composer`」)。由於 Tekkon.Composer 的型別是 Struct 型別,所以其副本必須為變數(var),否則無法自我 mutate。
|
||||
在你的 IMKInputController (InputMethodController) 或者 InputHandler 內初期化一份 Tekkon.Composer 注拼槽副本(這裡將該副本命名為「`_composer`」)。由於 Tekkon.Composer 的型別是 Struct 型別,所以其副本必須為變數(var),否則無法自我 mutate。
|
||||
|
||||
以 KeyHandler 為例:
|
||||
以 InputHandler 為例:
|
||||
```swift
|
||||
class KeyHandler: NSObject {
|
||||
class InputHandler: NSObject {
|
||||
// 先設定好變數
|
||||
var _composer: Tekkon.Composer = .init()
|
||||
...
|
||||
|
@ -45,7 +45,7 @@ class IMKMyInputController: IMKInputController {
|
|||
```
|
||||
|
||||
|
||||
由於 Swift 會在某個大副本(KeyHandler 或者 IMKInputController 副本)被銷毀的時候自動銷毀其中的全部副本,所以 Tekkon.Composer 的副本初期化沒必要寫在 init() 當中。但你很可能會想在 init() 時指定 Tekkon.Composer 所使用的注音排列(是大千?還是倚天傳統?還是神通?等)。
|
||||
由於 Swift 會在某個大副本(InputHandler 或者 IMKInputController 副本)被銷毀的時候自動銷毀其中的全部副本,所以 Tekkon.Composer 的副本初期化沒必要寫在 init() 當中。但你很可能會想在 init() 時指定 Tekkon.Composer 所使用的注音排列(是大千?還是倚天傳統?還是神通?等)。
|
||||
|
||||
這裡就需要在 _composer 這個副本所在的型別當中額外寫一個過程函式。
|
||||
|
||||
|
@ -159,7 +159,7 @@ final class TekkonTests: XCTestCase {
|
|||
|
||||
#### // 2. 訊號處理
|
||||
|
||||
無論是 KeyHandler 還是 IMKInputController 都得要處理被傳入的 NSEvent 當中的 charCode 訊號。
|
||||
無論是 InputHandler 還是 IMKInputController 都得要處理被傳入的 NSEvent 當中的 charCode 訊號。
|
||||
|
||||
比如 IMKInputController 內:
|
||||
```swift
|
||||
|
@ -168,9 +168,9 @@ func handleInputText(_ inputText: String?, key keyCode: Int, modifiers flags: In
|
|||
}
|
||||
```
|
||||
|
||||
或者 KeyHandler 內:
|
||||
或者 InputHandler 內:
|
||||
```swift
|
||||
extension KeyHandler {
|
||||
extension InputHandler {
|
||||
func handle(
|
||||
input: InputHandler,
|
||||
state: InputState,
|
||||
|
@ -184,7 +184,7 @@ extension KeyHandler {
|
|||
|
||||
但對注拼槽的處理都是一樣的。
|
||||
|
||||
這裡分享一下小麥注音輸入法與威注音輸入法在 KeyHandler 內對 _composer 的用法。
|
||||
這裡分享一下威注音輸入法在 InputHandler 內對 _composer 的用法。
|
||||
|
||||
如果收到的按鍵訊號是 BackSpace 的話,可以用 _composer.doBackSpace() 來移除注拼槽內最前方的元素。
|
||||
|
||||
|
@ -194,8 +194,6 @@ extension KeyHandler {
|
|||
|
||||
> (這裡的範例取自威注音,只用作演示用途。威注音實際的 codebase 可能會有出入。請留意這一段內的漢語註解。)
|
||||
>
|
||||
> (小麥注音 2.2 沒在用鐵恨引擎,而是在用 OVMandarin 引擎與 Objective-Cpp,所以語法會有一些出入。)
|
||||
>
|
||||
> (不是所有輸入法都有狀態管理引擎,請根據各自專案的實際情況來結合理解這段程式碼。)
|
||||
|
||||
```swift
|
||||
|
@ -219,7 +217,7 @@ if !skipPhoneticHandling && _composer.inputValidityCheck(key: charCode) {
|
|||
// 有調號的話,則不需要這樣處理,轉而繼續在此之後的處理。
|
||||
let composeReading = _composer.hasToneMarker()
|
||||
if !composeReading {
|
||||
stateCallback(buildInputtingState())
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +234,7 @@ composeReading = composeReading || (!_composer.isEmpty && (input.isSpace || inpu
|
|||
if composeReading { // 符合按鍵組合條件
|
||||
if input.isSpace && !_composer.hasToneMarker() {
|
||||
_composer.receiveKey(fromString: " ") // 補上空格,否則倚天忘形與許氏排列某些音無法響應不了陰平聲調。
|
||||
// 小麥注音因為使用 OVMandarin 而不是鐵恨引擎,所以不需要這樣補。但鐵恨引擎對所有聲調一視同仁。
|
||||
// 某些輸入法使用 OVMandarin 而不是鐵恨引擎,所以不需要這樣補。但鐵恨引擎對所有聲調一視同仁。
|
||||
}
|
||||
let reading = _composer.getComposition() // 拿取用來進行索引檢索用的注音
|
||||
// 如果輸入法的辭典索引是漢語拼音的話,要注意上一行拿到的內容得是漢語拼音。
|
||||
|
@ -249,7 +247,7 @@ if composeReading { // 符合按鍵組合條件
|
|||
_composer.clear() // 清空注拼槽的內容
|
||||
// 根據「天權星引擎 (威注音) 或 Gramambular (小麥) 的組字器是否為空」來判定回呼哪一種狀態
|
||||
stateCallback(
|
||||
(getCompositorLength() == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState())
|
||||
(getCompositorLength() == 0) ? InputState.EmptyIgnoringPreviousState() : generateStateOfInputting())
|
||||
return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了
|
||||
}
|
||||
|
||||
|
@ -269,7 +267,7 @@ if composeReading { // 符合按鍵組合條件
|
|||
// 之後就是更新組字區了。先清空注拼槽的內容。
|
||||
_composer.clear()
|
||||
// 再以回呼組字狀態的方式來執行updateClientComposingBuffer()
|
||||
let inputting = buildInputtingState()
|
||||
let inputting = generateStateOfInputting()
|
||||
inputting.poppedText = poppedText
|
||||
stateCallback(inputting)
|
||||
|
||||
|
|
|
@ -42,17 +42,20 @@ import Shared
|
|||
/// - .SymbolTable: 波浪鍵符號選單專用的狀態,有自身的特殊處理。
|
||||
public struct IMEState: IMEStateProtocol {
|
||||
public var type: StateType = .ofEmpty
|
||||
public var data: StateDataProtocol = StateData() as StateDataProtocol
|
||||
public var data: IMEStateDataProtocol = IMEStateData() as IMEStateDataProtocol
|
||||
public var node: CandidateNode = .init(name: "")
|
||||
public var isASCIIMode = false
|
||||
public var isVerticalCandidateWindow = false
|
||||
init(_ data: StateDataProtocol = StateData() as StateDataProtocol, type: StateType = .ofEmpty) {
|
||||
init(_ data: IMEStateDataProtocol = IMEStateData() as IMEStateDataProtocol, type: StateType = .ofEmpty) {
|
||||
self.data = data
|
||||
self.type = type
|
||||
isVerticalTyping = SessionCtl.isVerticalTyping
|
||||
}
|
||||
|
||||
init(_ data: StateDataProtocol = StateData() as StateDataProtocol, type: StateType = .ofEmpty, node: CandidateNode) {
|
||||
init(
|
||||
_ data: IMEStateDataProtocol = IMEStateData() as IMEStateDataProtocol, type: StateType = .ofEmpty,
|
||||
node: CandidateNode
|
||||
) {
|
||||
self.data = data
|
||||
self.type = type
|
||||
self.node = node
|
||||
|
|
|
@ -10,7 +10,7 @@ import Shared
|
|||
import Tekkon
|
||||
import TooltipUI
|
||||
|
||||
public struct StateData: StateDataProtocol {
|
||||
public struct IMEStateData: IMEStateDataProtocol {
|
||||
private static var minCandidateLength: Int {
|
||||
PrefMgr.shared.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ public struct StateData: StateDataProtocol {
|
|||
|
||||
// MARK: - IMEState 工具函式
|
||||
|
||||
extension StateData {
|
||||
extension IMEStateData {
|
||||
public var doesUserPhraseExist: Bool {
|
||||
let text = displayedText.charComponents[markedRange].joined()
|
||||
let joined = markedReadings.joined(separator: "-")
|
||||
|
@ -229,19 +229,19 @@ extension StateData {
|
|||
|
||||
let text = pair.1
|
||||
let readingDisplay = readingThreadForDisplay
|
||||
if markedRange.count < StateData.allowedMarkLengthRange.lowerBound {
|
||||
if markedRange.count < IMEStateData.allowedMarkLengthRange.lowerBound {
|
||||
tooltipColorState = .denialInsufficiency
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"\"%@\" length must ≥ 2 for a user phrase.", comment: ""
|
||||
) + "\n◆ " + readingDisplay, text
|
||||
)
|
||||
} else if markedRange.count > StateData.allowedMarkLengthRange.upperBound {
|
||||
} else if markedRange.count > IMEStateData.allowedMarkLengthRange.upperBound {
|
||||
tooltipColorState = .denialOverflow
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"\"%@\" length should ≤ %d for a user phrase.", comment: ""
|
||||
) + "\n◆ " + readingDisplay, text, StateData.allowedMarkLengthRange.upperBound
|
||||
) + "\n◆ " + readingDisplay, text, IMEStateData.allowedMarkLengthRange.upperBound
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,22 +17,22 @@ import Tekkon
|
|||
|
||||
// MARK: - 委任協定 (Delegate).
|
||||
|
||||
/// KeyHandler 委任協定
|
||||
public protocol KeyHandlerDelegate {
|
||||
/// InputHandler 委任協定
|
||||
public protocol InputHandlerDelegate {
|
||||
var selectionKeys: String { get }
|
||||
var clientBundleIdentifier: String { get }
|
||||
func candidateController() -> CtlCandidateProtocol
|
||||
func candidateSelectionCalledByKeyHandler(at index: Int)
|
||||
func candidateSelectionCalledByInputHandler(at index: Int)
|
||||
func performUserPhraseOperation(with state: IMEStateProtocol, addToFilter: Bool)
|
||||
-> Bool
|
||||
}
|
||||
|
||||
// MARK: - 核心 (Kernel).
|
||||
|
||||
/// KeyHandler 按鍵調度模組。
|
||||
public class KeyHandler {
|
||||
/// InputHandler 按鍵調度模組。
|
||||
public class InputHandler {
|
||||
/// 委任物件 (SessionCtl),以便呼叫其中的函式。
|
||||
public var delegate: KeyHandlerDelegate?
|
||||
public var delegate: InputHandlerDelegate?
|
||||
public var prefs: PrefMgrProtocol
|
||||
|
||||
/// 半衰模組的衰減指數
|
||||
|
@ -74,7 +74,7 @@ public class KeyHandler {
|
|||
min(compositor.cursor, compositor.marker)..<max(compositor.cursor, compositor.marker)
|
||||
}
|
||||
|
||||
/// 檢測是否出現游標切斷組字圈內字符的情況
|
||||
/// 檢測是否出現游標切斷組字區內字符的情況
|
||||
func isCursorCuttingChar(isMarker: Bool = false) -> Bool {
|
||||
let index = isMarker ? compositor.marker : compositor.cursor
|
||||
var isBound = (index == compositor.walkedNodes.contextRange(ofGivenCursor: index).lowerBound)
|
||||
|
@ -86,7 +86,7 @@ public class KeyHandler {
|
|||
/// 實際上要拿給 Megrez 使用的的游標位址,以方便在組字器最開頭或者最末尾的時候始終能抓取候選字節點陣列。
|
||||
///
|
||||
/// 威注音對游標前置與游標後置模式採取的候選字節點陣列抓取方法是分離的,且不使用 Node Crossing。
|
||||
var actualCandidateCursor: Int {
|
||||
var cursorForCandidate: Int {
|
||||
compositor.cursor
|
||||
- ((compositor.cursor == compositor.width || !prefs.useRearCursorMode) && compositor.cursor > 0 ? 1 : 0)
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public class KeyHandler {
|
|||
/// - Parameter key: 給定的聯想詞的開頭字。
|
||||
/// - Returns: 抓取到的聯想詞陣列。
|
||||
/// 不會是 nil,但那些負責接收結果的函式會對空白陣列結果做出正確的處理。
|
||||
func buildAssociatePhraseArray(withPair pair: Megrez.Compositor.KeyValuePaired) -> [(String, String)] {
|
||||
func generateArrayOfAssociates(withPair pair: Megrez.Compositor.KeyValuePaired) -> [(String, String)] {
|
||||
var arrResult: [(String, String)] = []
|
||||
if currentLM.hasAssociatedPhrasesFor(pair: pair) {
|
||||
arrResult = currentLM.associatedPhrasesFor(pair: pair).map { ("", $0) }
|
||||
|
@ -140,18 +140,18 @@ public class KeyHandler {
|
|||
/// - Parameter theCandidate: 要拿來覆寫的詞音配對。
|
||||
func consolidateCursorContext(with theCandidate: Megrez.Compositor.KeyValuePaired) {
|
||||
var grid = compositor
|
||||
var frontBoundaryEX = actualCandidateCursor + 1
|
||||
var rearBoundaryEX = actualCandidateCursor
|
||||
var frontBoundaryEX = cursorForCandidate + 1
|
||||
var rearBoundaryEX = cursorForCandidate
|
||||
var debugIntelToPrint = ""
|
||||
if grid.overrideCandidate(theCandidate, at: actualCandidateCursor) {
|
||||
if grid.overrideCandidate(theCandidate, at: cursorForCandidate) {
|
||||
grid.walk()
|
||||
let range = grid.walkedNodes.contextRange(ofGivenCursor: actualCandidateCursor)
|
||||
let range = grid.walkedNodes.contextRange(ofGivenCursor: cursorForCandidate)
|
||||
rearBoundaryEX = range.lowerBound
|
||||
frontBoundaryEX = range.upperBound
|
||||
debugIntelToPrint.append("EX: \(rearBoundaryEX)..<\(frontBoundaryEX), ")
|
||||
}
|
||||
|
||||
let range = compositor.walkedNodes.contextRange(ofGivenCursor: actualCandidateCursor)
|
||||
let range = compositor.walkedNodes.contextRange(ofGivenCursor: cursorForCandidate)
|
||||
var rearBoundary = min(range.lowerBound, rearBoundaryEX)
|
||||
var frontBoundary = max(range.upperBound, frontBoundaryEX)
|
||||
|
||||
|
@ -207,14 +207,14 @@ public class KeyHandler {
|
|||
/// - value: 給定之候選字(詞音配對)。
|
||||
/// - respectCursorPushing: 若該選項為 true,則會在選字之後始終將游標推送至選字後的節錨的前方。
|
||||
/// - consolidate: 在固化節點之前,先鞏固上下文。該選項可能會破壞在內文組字區內就地輪替候選字詞時的體驗。
|
||||
func fixNode(candidate: (String, String), respectCursorPushing: Bool = true, preConsolidate: Bool = false) {
|
||||
func consolidateNode(candidate: (String, String), respectCursorPushing: Bool = true, preConsolidate: Bool = false) {
|
||||
let theCandidate: Megrez.Compositor.KeyValuePaired = .init(key: candidate.0, value: candidate.1)
|
||||
|
||||
/// 必須先鞏固當前組字器游標上下文、以消滅意料之外的影響,但在內文組字區內就地輪替候選字詞時除外。
|
||||
if preConsolidate { consolidateCursorContext(with: theCandidate) }
|
||||
|
||||
// 回到正常流程。
|
||||
if !compositor.overrideCandidate(theCandidate, at: actualCandidateCursor) { return }
|
||||
if !compositor.overrideCandidate(theCandidate, at: cursorForCandidate) { return }
|
||||
let previousWalk = compositor.walkedNodes
|
||||
// 開始爬軌。
|
||||
walk()
|
||||
|
@ -222,7 +222,7 @@ public class KeyHandler {
|
|||
|
||||
// 在可行的情況下更新使用者半衰記憶模組。
|
||||
var accumulatedCursor = 0
|
||||
let currentNode = currentWalk.findNode(at: actualCandidateCursor, target: &accumulatedCursor)
|
||||
let currentNode = currentWalk.findNode(at: cursorForCandidate, target: &accumulatedCursor)
|
||||
guard let currentNode = currentNode else { return }
|
||||
|
||||
if currentNode.currentUnigram.score > -12, prefs.fetchSuggestionsFromUserOverrideModel {
|
||||
|
@ -234,7 +234,7 @@ public class KeyHandler {
|
|||
// 令半衰記憶模組觀測給定的三元圖。
|
||||
// 這個過程會讓半衰引擎根據當前上下文生成三元圖索引鍵。
|
||||
currentUOM.performObservation(
|
||||
walkedBefore: previousWalk, walkedAfter: currentWalk, cursor: actualCandidateCursor,
|
||||
walkedBefore: previousWalk, walkedAfter: currentWalk, cursor: cursorForCandidate,
|
||||
timestamp: Date().timeIntervalSince1970, saveCallback: { self.currentUOM.saveData() }
|
||||
)
|
||||
// 如果沒有出現崩框的話,那就將這個開關復位。
|
||||
|
@ -249,15 +249,15 @@ public class KeyHandler {
|
|||
}
|
||||
|
||||
/// 獲取候選字詞(包含讀音)陣列資料內容。
|
||||
func getCandidatesArray(fixOrder: Bool = true) -> [(String, String)] {
|
||||
func generateArrayOfCandidates(fixOrder: Bool = true) -> [(String, String)] {
|
||||
/// 警告:不要對游標前置風格使用 nodesCrossing,否則會導致游標行為與 macOS 內建注音輸入法不一致。
|
||||
/// 微軟新注音輸入法的游標後置風格也是不允許 nodeCrossing 的。
|
||||
var arrCandidates: [Megrez.Compositor.KeyValuePaired] = {
|
||||
switch prefs.useRearCursorMode {
|
||||
case false:
|
||||
return compositor.fetchCandidates(at: actualCandidateCursor, filter: .endAt)
|
||||
return compositor.fetchCandidates(at: cursorForCandidate, filter: .endAt)
|
||||
case true:
|
||||
return compositor.fetchCandidates(at: actualCandidateCursor, filter: .beginAt)
|
||||
return compositor.fetchCandidates(at: cursorForCandidate, filter: .beginAt)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -271,7 +271,7 @@ public class KeyHandler {
|
|||
return arrCandidates.map { ($0.key, $0.value) }
|
||||
}
|
||||
|
||||
let arrSuggestedUnigrams: [(String, Megrez.Unigram)] = fetchSuggestionsFromUOM(apply: false)
|
||||
let arrSuggestedUnigrams: [(String, Megrez.Unigram)] = retrieveUOMSuggestions(apply: false)
|
||||
let arrSuggestedCandidates: [Megrez.Compositor.KeyValuePaired] = arrSuggestedUnigrams.map {
|
||||
Megrez.Compositor.KeyValuePaired(key: $0.0, value: $0.1.value)
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ public class KeyHandler {
|
|||
}
|
||||
|
||||
/// 向半衰引擎詢問可能的選字建議、且套用給組字器內的當前游標位置。
|
||||
@discardableResult func fetchSuggestionsFromUOM(apply: Bool) -> [(String, Megrez.Unigram)] {
|
||||
@discardableResult func retrieveUOMSuggestions(apply: Bool) -> [(String, Megrez.Unigram)] {
|
||||
var arrResult = [(String, Megrez.Unigram)]()
|
||||
/// 如果逐字選字模式有啟用的話,直接放棄執行這個函式。
|
||||
if prefs.useSCPCTypingMode { return arrResult }
|
||||
|
@ -290,7 +290,7 @@ public class KeyHandler {
|
|||
if !prefs.fetchSuggestionsFromUserOverrideModel { return arrResult }
|
||||
/// 獲取來自半衰記憶模組的建議結果
|
||||
let suggestion = currentUOM.fetchSuggestion(
|
||||
currentWalk: compositor.walkedNodes, cursor: actualCandidateCursor, timestamp: Date().timeIntervalSince1970
|
||||
currentWalk: compositor.walkedNodes, cursor: cursorForCandidate, timestamp: Date().timeIntervalSince1970
|
||||
)
|
||||
arrResult.append(contentsOf: suggestion.candidates)
|
||||
if apply {
|
||||
|
@ -303,9 +303,9 @@ public class KeyHandler {
|
|||
)
|
||||
vCLog(
|
||||
"UOM: Suggestion retrieved, overriding the node score of the selected candidate: \(suggestedPair.toNGramKey)")
|
||||
if !compositor.overrideCandidate(suggestedPair, at: actualCandidateCursor, overrideType: overrideBehavior) {
|
||||
if !compositor.overrideCandidate(suggestedPair, at: cursorForCandidate, overrideType: overrideBehavior) {
|
||||
compositor.overrideCandidateLiteral(
|
||||
newestSuggestedCandidate.1.value, at: actualCandidateCursor, overrideType: overrideBehavior
|
||||
newestSuggestedCandidate.1.value, at: cursorForCandidate, overrideType: overrideBehavior
|
||||
)
|
||||
}
|
||||
walk()
|
||||
|
@ -415,7 +415,7 @@ public class KeyHandler {
|
|||
/// - Remark: 該選項僅對不支援 IMKTextInput 協定的應用有用,就不交給 PrefMgr 了。
|
||||
private let compositorWidthLimit = 20
|
||||
|
||||
extension KeyHandler {
|
||||
extension InputHandler {
|
||||
/// 在爬取組字結果之前,先將即將從組字區溢出的內容遞交出去。
|
||||
///
|
||||
/// 在理想狀況之下,組字區多長都無所謂。但是,螢幕浮動組字窗的尺寸是有限的。
|
|
@ -13,7 +13,7 @@ import Shared
|
|||
|
||||
// MARK: - § 對選字狀態進行調度 (Handle Candidate State).
|
||||
|
||||
extension KeyHandler {
|
||||
extension InputHandler {
|
||||
/// 當且僅當選字窗出現時,對於經過初次篩選處理的輸入訊號的處理均藉由此函式來進行。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
|
@ -49,7 +49,7 @@ extension KeyHandler {
|
|||
// 所以這裡需要對 compositor.isEmpty 做判定。
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
} else {
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
}
|
||||
if state.type == .ofSymbolTable, let nodePrevious = state.node.previous, !nodePrevious.members.isEmpty {
|
||||
stateCallback(IMEState.ofSymbolTable(node: nodePrevious))
|
||||
|
@ -64,7 +64,7 @@ extension KeyHandler {
|
|||
stateCallback(IMEState.ofAbortion())
|
||||
return true
|
||||
}
|
||||
delegate?.candidateSelectionCalledByKeyHandler(at: ctlCandidate.selectedCandidateIndex)
|
||||
delegate?.candidateSelectionCalledByInputHandler(at: ctlCandidate.selectedCandidateIndex)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -245,7 +245,7 @@ extension KeyHandler {
|
|||
if index != NSNotFound {
|
||||
let candidateIndex = ctlCandidate.candidateIndexAtKeyLabelIndex(index)
|
||||
if candidateIndex != -114_514 {
|
||||
delegate?.candidateSelectionCalledByKeyHandler(at: candidateIndex)
|
||||
delegate?.candidateSelectionCalledByInputHandler(at: candidateIndex)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -285,10 +285,10 @@ extension KeyHandler {
|
|||
if shouldAutoSelectCandidate {
|
||||
let candidateIndex = ctlCandidate.candidateIndexAtKeyLabelIndex(0)
|
||||
if candidateIndex != -114_514 {
|
||||
delegate?.candidateSelectionCalledByKeyHandler(at: candidateIndex)
|
||||
delegate?.candidateSelectionCalledByInputHandler(at: candidateIndex)
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
return handle(
|
||||
input: input, state: IMEState.ofEmpty(), stateCallback: stateCallback, errorCallback: errorCallback
|
||||
return handleInput(
|
||||
event: input, state: IMEState.ofEmpty(), stateCallback: stateCallback, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
return true
|
|
@ -6,13 +6,13 @@
|
|||
// marks, or product names of Contributor, except as required to fulfill notice
|
||||
// requirements defined in MIT License.
|
||||
|
||||
/// 該檔案用來處理 KeyHandler.HandleInput() 當中的與組字有關的行為。
|
||||
/// 該檔案用來處理 InputHandler.HandleInput() 當中的與組字有關的行為。
|
||||
|
||||
import Shared
|
||||
import Tekkon
|
||||
|
||||
extension KeyHandler {
|
||||
/// 用來處理 KeyHandler.HandleInput() 當中的與組字有關的行為。
|
||||
extension InputHandler {
|
||||
/// 用來處理 InputHandler.HandleInput() 當中的與組字有關的行為。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。
|
||||
|
@ -52,7 +52,7 @@ extension KeyHandler {
|
|||
compositor.dropKey(direction: .rear)
|
||||
walk() // 這裡必須 Walk 一次、來更新目前被 walk 的內容。
|
||||
composer = theComposer
|
||||
// 這裡不需要回呼 buildInputtingState,因為當前輸入的聲調鍵一定是合規的、會在之後回呼 buildInputtingState。
|
||||
// 這裡不需要回呼 generateStateOfInputting(),因為當前輸入的聲調鍵一定是合規的、會在之後回呼 generateStateOfInputting()。
|
||||
} else {
|
||||
errorCallback("4B0DD2D4:語彙庫內無「\(temporaryReadingKey)」的匹配記錄,放棄覆寫游標身後的內容。")
|
||||
return true
|
||||
|
@ -65,7 +65,7 @@ extension KeyHandler {
|
|||
// 沒有調號的話,只需要 setInlineDisplayWithCursor() 且終止處理(return true)即可。
|
||||
// 有調號的話,則不需要這樣,而是轉而繼續在此之後的處理。
|
||||
if !composer.hasToneMarker() {
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -90,14 +90,14 @@ extension KeyHandler {
|
|||
|
||||
if prefs.keepReadingUponCompositionError {
|
||||
composer.intonation.clear() // 砍掉聲調。
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
composer.clear()
|
||||
// 根據「組字器是否為空」來判定回呼哪一種狀態。
|
||||
switch compositor.isEmpty {
|
||||
case false: stateCallback(buildInputtingState)
|
||||
case false: stateCallback(generateStateOfInputting())
|
||||
case true:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
}
|
||||
|
@ -114,19 +114,19 @@ extension KeyHandler {
|
|||
let textToCommit = commitOverflownComposition
|
||||
|
||||
// 看看半衰記憶模組是否會對目前的狀態給出自動選字建議。
|
||||
fetchSuggestionsFromUOM(apply: true)
|
||||
retrieveUOMSuggestions(apply: true)
|
||||
|
||||
// 之後就是更新組字區了。先清空注拼槽的內容。
|
||||
composer.clear()
|
||||
|
||||
// 再以回呼組字狀態的方式來執行 setInlineDisplayWithCursor()。
|
||||
var inputting = buildInputtingState
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
|
||||
/// 逐字選字模式的處理。
|
||||
if prefs.useSCPCTypingMode {
|
||||
let candidateState: IMEStateProtocol = buildCandidate(state: inputting)
|
||||
let candidateState: IMEStateProtocol = generateStateOfCandidates(state: inputting)
|
||||
switch candidateState.candidates.count {
|
||||
case 2...: stateCallback(candidateState)
|
||||
case 1:
|
||||
|
@ -139,7 +139,7 @@ extension KeyHandler {
|
|||
stateCallback(IMEState.ofEmpty())
|
||||
} else {
|
||||
let associatedPhrases =
|
||||
buildAssociatePhraseState(
|
||||
generateStateOfAssociates(
|
||||
withPair: .init(key: reading, value: text)
|
||||
)
|
||||
stateCallback(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases)
|
||||
|
@ -154,7 +154,7 @@ extension KeyHandler {
|
|||
/// 是說此時注拼槽並非為空、卻還沒組音。這種情況下只可能是「注拼槽內只有聲調」。
|
||||
if keyConsumedByReading {
|
||||
// 以回呼組字狀態的方式來執行 setInlineDisplayWithCursor()。
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
return nil
|
|
@ -14,7 +14,7 @@ import Shared
|
|||
|
||||
// MARK: - § 根據狀態調度按鍵輸入 (Handle Input with States)
|
||||
|
||||
extension KeyHandler {
|
||||
extension InputHandler {
|
||||
/// 對於輸入訊號的第一關處理均藉由此函式來進行。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
|
@ -22,8 +22,8 @@ extension KeyHandler {
|
|||
/// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
func handle(
|
||||
input: InputSignalProtocol,
|
||||
func handleInput(
|
||||
event input: InputSignalProtocol,
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
|
@ -41,7 +41,7 @@ extension KeyHandler {
|
|||
if state.type == .ofEmpty || state.type == .ofDeactivated {
|
||||
return false
|
||||
}
|
||||
errorCallback("550BCF7B: KeyHandler just refused an invalid input.")
|
||||
errorCallback("550BCF7B: InputHandler just refused an invalid input.")
|
||||
stateCallback(state)
|
||||
return true
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ extension KeyHandler {
|
|||
walk()
|
||||
// 一邊吃一邊屙(僅對位列黑名單的 App 用這招限制組字區長度)。
|
||||
let textToCommit = commitOverflownComposition
|
||||
var inputting = buildInputtingState
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ extension KeyHandler {
|
|||
)
|
||||
}
|
||||
}
|
||||
let candidateState: IMEStateProtocol = buildCandidate(state: state)
|
||||
let candidateState: IMEStateProtocol = generateStateOfCandidates(state: state)
|
||||
if candidateState.candidates.isEmpty {
|
||||
errorCallback("3572F238")
|
||||
} else {
|
||||
|
@ -291,10 +291,10 @@ extension KeyHandler {
|
|||
walk()
|
||||
// 一邊吃一邊屙(僅對位列黑名單的 App 用這招限制組字區長度)。
|
||||
let textToCommit = commitOverflownComposition
|
||||
var inputting = buildInputtingState
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
let candidateState = buildCandidate(state: inputting)
|
||||
let candidateState = generateStateOfCandidates(state: inputting)
|
||||
if candidateState.candidates.isEmpty {
|
||||
errorCallback("B5127D8A")
|
||||
} else {
|
|
@ -14,11 +14,11 @@ import Tekkon
|
|||
|
||||
// MARK: - § 根據按鍵行為來調控模式的函式 (Functions Interact With States).
|
||||
|
||||
extension KeyHandler {
|
||||
extension InputHandler {
|
||||
// MARK: - 構築狀態(State Building)
|
||||
|
||||
/// 生成「正在輸入」狀態。相關的內容會被拿給狀態機械用來處理在電腦螢幕上顯示的內容。
|
||||
public var buildInputtingState: IMEStateProtocol {
|
||||
public func generateStateOfInputting() -> IMEStateProtocol {
|
||||
/// 「更新內文組字區 (Update the composing buffer)」是指要求客體軟體將組字緩衝區的內容
|
||||
/// 換成由此處重新生成的組字字串(NSAttributeString,否則會不顯示)。
|
||||
var displayTextSegments: [String] = compositor.walkedNodes.values
|
||||
|
@ -86,11 +86,11 @@ extension KeyHandler {
|
|||
/// - Parameters:
|
||||
/// - currentState: 當前狀態。
|
||||
/// - Returns: 回呼一個新的選詞狀態,來就給定的候選字詞陣列資料內容顯示選字窗。
|
||||
func buildCandidate(
|
||||
func generateStateOfCandidates(
|
||||
state currentState: IMEStateProtocol
|
||||
) -> IMEStateProtocol {
|
||||
IMEState.ofCandidates(
|
||||
candidates: getCandidatesArray(fixOrder: prefs.useFixecCandidateOrderOnSelection),
|
||||
candidates: generateArrayOfCandidates(fixOrder: prefs.useFixecCandidateOrderOnSelection),
|
||||
displayTextSegments: compositor.walkedNodes.values,
|
||||
cursor: currentState.cursor
|
||||
)
|
||||
|
@ -100,9 +100,9 @@ extension KeyHandler {
|
|||
|
||||
/// 拿著給定的聯想詞陣列資料內容,切換至聯想詞狀態。
|
||||
///
|
||||
/// 這次重寫時,針對「buildAssociatePhraseStateWithKey」這個(用以生成帶有
|
||||
/// 這次重寫時,針對「generateStateOfAssociates」這個(用以生成帶有
|
||||
/// 聯想詞候選清單的結果的狀態回呼的)函式進行了小幅度的重構處理,使其始終
|
||||
/// 可以從 Core 部分的「buildAssociatePhraseArray」函式獲取到一個內容類型
|
||||
/// 可以從 Core 部分的「generateArrayOfAssociates」函式獲取到一個內容類型
|
||||
/// 為「String」的標準 Swift 陣列。這樣一來,該聯想詞狀態回呼函式將始終能
|
||||
/// 夠傳回正確的結果形態、永遠也無法傳回 nil。於是,所有在用到該函式時以
|
||||
/// 回傳結果類型判斷作為合法性判斷依據的函式,全都將依據改為檢查傳回的陣列
|
||||
|
@ -110,11 +110,11 @@ extension KeyHandler {
|
|||
/// - Parameters:
|
||||
/// - key: 給定的索引鍵(也就是給定的聯想詞的開頭字)。
|
||||
/// - Returns: 回呼一個新的聯想詞狀態,來就給定的聯想詞陣列資料內容顯示選字窗。
|
||||
func buildAssociatePhraseState(
|
||||
func generateStateOfAssociates(
|
||||
withPair pair: Megrez.Compositor.KeyValuePaired
|
||||
) -> IMEStateProtocol {
|
||||
IMEState.ofAssociates(
|
||||
candidates: buildAssociatePhraseArray(withPair: pair))
|
||||
candidates: generateArrayOfAssociates(withPair: pair))
|
||||
}
|
||||
|
||||
// MARK: - 用以處理就地新增自訂語彙時的行為
|
||||
|
@ -133,7 +133,7 @@ extension KeyHandler {
|
|||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
if input.isEsc {
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ extension KeyHandler {
|
|||
|
||||
// Enter
|
||||
if input.isEnter {
|
||||
if let ctlIME = delegate {
|
||||
if let sessionCtl = delegate {
|
||||
// 先判斷是否是在摁了降權組合鍵的時候目標不在庫。
|
||||
if input.isShiftHold, input.isCommandHold, !state.isFilterable {
|
||||
errorCallback("2EAC1F7A")
|
||||
|
@ -155,28 +155,28 @@ extension KeyHandler {
|
|||
errorCallback("9AAFAC00")
|
||||
return true
|
||||
}
|
||||
if !ctlIME.performUserPhraseOperation(with: state, addToFilter: false) {
|
||||
if !sessionCtl.performUserPhraseOperation(with: state, addToFilter: false) {
|
||||
errorCallback("5B69CC8D")
|
||||
return true
|
||||
}
|
||||
}
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
// BackSpace & Delete
|
||||
if input.isBackSpace || input.isDelete {
|
||||
if let keyHandlerDelegate = delegate {
|
||||
if let inputHandlerDelegate = delegate {
|
||||
if !state.isFilterable {
|
||||
errorCallback("1F88B191")
|
||||
return true
|
||||
}
|
||||
if !keyHandlerDelegate.performUserPhraseOperation(with: state, addToFilter: true) {
|
||||
if !inputHandlerDelegate.performUserPhraseOperation(with: state, addToFilter: true) {
|
||||
errorCallback("68D3C6C8")
|
||||
return true
|
||||
}
|
||||
}
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -255,14 +255,14 @@ extension KeyHandler {
|
|||
walk()
|
||||
// 一邊吃一邊屙(僅對位列黑名單的 App 用這招限制組字區長度)。
|
||||
let textToCommit = commitOverflownComposition
|
||||
var inputting = buildInputtingState
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
|
||||
// 從這一行之後開始,就是針對逐字選字模式的單獨處理。
|
||||
guard prefs.useSCPCTypingMode, composer.isEmpty else { return true }
|
||||
|
||||
let candidateState = buildCandidate(state: inputting)
|
||||
let candidateState = generateStateOfCandidates(state: inputting)
|
||||
switch candidateState.candidates.count {
|
||||
case 2...: stateCallback(candidateState)
|
||||
case 1:
|
||||
|
@ -386,7 +386,7 @@ extension KeyHandler {
|
|||
compositor.dropKey(direction: .rear)
|
||||
walk() // 這裡必須 Walk 一次、來更新目前被 walk 的內容。
|
||||
prevReading.1.charComponents.forEach { composer.receiveKey(fromPhonabet: $0) }
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
case 1:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
|
@ -415,7 +415,7 @@ extension KeyHandler {
|
|||
}
|
||||
|
||||
switch composer.isEmpty && compositor.isEmpty {
|
||||
case false: stateCallback(buildInputtingState)
|
||||
case false: stateCallback(generateStateOfInputting())
|
||||
case true:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
}
|
||||
|
@ -457,7 +457,7 @@ extension KeyHandler {
|
|||
composer.clear()
|
||||
}
|
||||
|
||||
let inputting = buildInputtingState
|
||||
let inputting = generateStateOfInputting()
|
||||
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||
switch inputting.displayedText.isEmpty {
|
||||
case false: stateCallback(inputting)
|
||||
|
@ -511,7 +511,7 @@ extension KeyHandler {
|
|||
|
||||
if compositor.cursor != 0 {
|
||||
compositor.cursor = 0
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("66D97F90")
|
||||
stateCallback(state)
|
||||
|
@ -543,7 +543,7 @@ extension KeyHandler {
|
|||
|
||||
if compositor.cursor != compositor.length {
|
||||
compositor.cursor = compositor.length
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("9B69908E")
|
||||
stateCallback(state)
|
||||
|
@ -574,7 +574,7 @@ extension KeyHandler {
|
|||
/// 如果注拼槽不是空的話,則清空之。
|
||||
composer.clear()
|
||||
switch compositor.isEmpty {
|
||||
case false: stateCallback(buildInputtingState)
|
||||
case false: stateCallback(generateStateOfInputting())
|
||||
case true:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
}
|
||||
|
@ -634,14 +634,14 @@ extension KeyHandler {
|
|||
stateCallback(state)
|
||||
return true
|
||||
}
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
} else {
|
||||
if compositor.cursor < compositor.length {
|
||||
compositor.cursor += 1
|
||||
if isCursorCuttingChar() {
|
||||
compositor.jumpCursorBySpan(to: .front)
|
||||
}
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("A96AAD58")
|
||||
stateCallback(state)
|
||||
|
@ -703,14 +703,14 @@ extension KeyHandler {
|
|||
stateCallback(state)
|
||||
return true
|
||||
}
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
} else {
|
||||
if compositor.cursor > 0 {
|
||||
compositor.cursor -= 1
|
||||
if isCursorCuttingChar() {
|
||||
compositor.jumpCursorBySpan(to: .rear)
|
||||
}
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("7045E6F3")
|
||||
stateCallback(state)
|
||||
|
@ -750,7 +750,7 @@ extension KeyHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
let candidates = getCandidatesArray(fixOrder: true)
|
||||
let candidates = generateArrayOfCandidates(fixOrder: true)
|
||||
guard !candidates.isEmpty else {
|
||||
errorCallback("3378A6DF")
|
||||
return true
|
||||
|
@ -758,7 +758,7 @@ extension KeyHandler {
|
|||
|
||||
var length = 0
|
||||
var currentNode: Megrez.Compositor.Node?
|
||||
let cursorIndex = actualCandidateCursor
|
||||
let cursorIndex = cursorForCandidate
|
||||
for node in compositor.walkedNodes {
|
||||
length += node.spanLength
|
||||
if length > cursorIndex {
|
||||
|
@ -812,9 +812,9 @@ extension KeyHandler {
|
|||
currentIndex = 0
|
||||
}
|
||||
|
||||
fixNode(candidate: candidates[currentIndex], respectCursorPushing: false, preConsolidate: false)
|
||||
consolidateNode(candidate: candidates[currentIndex], respectCursorPushing: false, preConsolidate: false)
|
||||
|
||||
stateCallback(buildInputtingState)
|
||||
stateCallback(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ import Shared
|
|||
/// 使用者辭典資料預設範例檔案名稱。
|
||||
private let kTemplateNameUserPhrases = "template-userphrases"
|
||||
private let kTemplateNameUserReplacements = "template-replacements"
|
||||
private let kTemplateNameUserExclusions = "template-exclusions"
|
||||
private let kTemplateNameUserFilterList = "template-exclusions"
|
||||
private let kTemplateNameUserSymbolPhrases = "template-usersymbolphrases"
|
||||
private let kTemplateNameUserAssociatesCHS = "template-associatedPhrases-chs"
|
||||
private let kTemplateNameUserAssociatesCHT = "template-associatedPhrases-cht"
|
||||
|
@ -362,7 +362,7 @@ public enum LMMgr {
|
|||
// MARK: - 檢查具體的使用者語彙檔案是否存在
|
||||
|
||||
public static func ensureFileExists(
|
||||
_ fileURL: URL, populateWithTemplate templateBasename: String = "1145141919810",
|
||||
_ fileURL: URL, deployTemplate templateBasename: String = "1145141919810",
|
||||
extension ext: String = "txt"
|
||||
) -> Bool {
|
||||
let filePath = fileURL.path
|
||||
|
@ -393,15 +393,15 @@ public enum LMMgr {
|
|||
/// CandidateNode 資料與 UserOverrideModel 半衰模組資料檔案不需要強行確保存在。
|
||||
/// 前者的話,需要該檔案存在的人自己會建立。
|
||||
/// 後者的話,你在敲字時自己就會建立。
|
||||
if !ensureFileExists(userPhrasesDataURL(mode), populateWithTemplate: kTemplateNameUserPhrases)
|
||||
if !ensureFileExists(userPhrasesDataURL(mode), deployTemplate: kTemplateNameUserPhrases)
|
||||
|| !ensureFileExists(
|
||||
userAssociatesDataURL(mode),
|
||||
populateWithTemplate: mode == .imeModeCHS ? kTemplateNameUserAssociatesCHS : kTemplateNameUserAssociatesCHT
|
||||
deployTemplate: mode == .imeModeCHS ? kTemplateNameUserAssociatesCHS : kTemplateNameUserAssociatesCHT
|
||||
)
|
||||
|| !ensureFileExists(userSCPCSequencesURL(mode))
|
||||
|| !ensureFileExists(userFilteredDataURL(mode), populateWithTemplate: kTemplateNameUserExclusions)
|
||||
|| !ensureFileExists(userReplacementsDataURL(mode), populateWithTemplate: kTemplateNameUserReplacements)
|
||||
|| !ensureFileExists(userSymbolDataURL(mode), populateWithTemplate: kTemplateNameUserSymbolPhrases)
|
||||
|| !ensureFileExists(userFilteredDataURL(mode), deployTemplate: kTemplateNameUserFilterList)
|
||||
|| !ensureFileExists(userReplacementsDataURL(mode), deployTemplate: kTemplateNameUserReplacements)
|
||||
|| !ensureFileExists(userSymbolDataURL(mode), deployTemplate: kTemplateNameUserSymbolPhrases)
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -55,13 +55,13 @@ public class SessionCtl: IMKInputController {
|
|||
/// 當前這個 SessionCtl 副本是否處於英數輸入模式。
|
||||
public var isASCIIMode = false {
|
||||
didSet {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
setKeyLayout()
|
||||
}
|
||||
}
|
||||
|
||||
/// 按鍵調度模組的副本。
|
||||
var keyHandler = KeyHandler(lm: LMMgr.currentLM(), uom: LMMgr.currentUOM(), pref: PrefMgr.shared)
|
||||
var inputHandler = InputHandler(lm: LMMgr.currentLM(), uom: LMMgr.currentUOM(), pref: PrefMgr.shared)
|
||||
/// 用以記錄當前輸入法狀態的變數。
|
||||
public var state: IMEStateProtocol = IMEState.ofEmpty() {
|
||||
didSet {
|
||||
|
@ -79,7 +79,7 @@ public class SessionCtl: IMKInputController {
|
|||
useLShift: PrefMgr.shared.togglingAlphanumericalModeWithLShift)
|
||||
|
||||
/// `handle(event:)` 會利用這個參數判定某次 Shift 按鍵是否用來切換中英文輸入。
|
||||
public var rencentKeyHandledByKeyHandlerEtc = false
|
||||
public var rencentKeyHandledByInputHandlerEtc = false
|
||||
|
||||
/// 記錄當前輸入環境是縱排輸入還是橫排輸入。
|
||||
public static var isVerticalTyping: Bool = false
|
||||
|
@ -104,13 +104,13 @@ public class SessionCtl: IMKInputController {
|
|||
didSet {
|
||||
if oldValue != inputMode, inputMode != .imeModeNULL {
|
||||
UserDefaults.standard.synchronize()
|
||||
keyHandler.clear() // 這句不要砍,因為後面 handle State.Empty() 不一定執行。
|
||||
inputHandler.clear() // 這句不要砍,因為後面 handle State.Empty() 不一定執行。
|
||||
// ----------------------------
|
||||
/// 重設所有語言模組。這裡不需要做按需重設,因為對運算量沒有影響。
|
||||
keyHandler.currentLM = LMMgr.currentLM() // 會自動更新組字引擎內的模組。
|
||||
keyHandler.currentUOM = LMMgr.currentUOM()
|
||||
inputHandler.currentLM = LMMgr.currentLM() // 會自動更新組字引擎內的模組。
|
||||
inputHandler.currentUOM = LMMgr.currentUOM()
|
||||
/// 清空注拼槽+同步最新的注拼槽排列設定。
|
||||
keyHandler.ensureKeyboardParser()
|
||||
inputHandler.ensureKeyboardParser()
|
||||
/// 將輸入法偏好設定同步至語言模組內。
|
||||
syncBaseLMPrefs()
|
||||
// ----------------------------
|
||||
|
@ -131,7 +131,7 @@ public class SessionCtl: IMKInputController {
|
|||
/// - inputClient: 用以接受輸入的客體應用物件
|
||||
override public init!(server: IMKServer!, delegate: Any!, client inputClient: Any!) {
|
||||
super.init(server: server, delegate: delegate, client: inputClient)
|
||||
keyHandler.delegate = self
|
||||
inputHandler.delegate = self
|
||||
syncBaseLMPrefs()
|
||||
// 下述部分很有必要,否則輸入法會在手動重啟之後無法立刻生效。
|
||||
activateServer(inputClient)
|
||||
|
@ -163,11 +163,11 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
/// 重設按鍵調度模組,會將當前尚未遞交的內容遞交出去。
|
||||
public func resetKeyHandler() {
|
||||
public func resetInputHandler() {
|
||||
// 過濾掉尚未完成拼寫的注音。
|
||||
if state.type == .ofInputting, PrefMgr.shared.trimUnfinishedReadingsOnCommit {
|
||||
keyHandler.composer.clear()
|
||||
handle(state: keyHandler.buildInputtingState)
|
||||
inputHandler.composer.clear()
|
||||
handle(state: inputHandler.generateStateOfInputting())
|
||||
}
|
||||
let isSecureMode = PrefMgr.shared.clientsIMKTextInputIncapable.contains(clientBundleIdentifier)
|
||||
if state.hasComposition, !isSecureMode {
|
||||
|
@ -190,10 +190,10 @@ extension SessionCtl {
|
|||
|
||||
// 因為偶爾會收到與 activateServer 有關的以「強制拆 nil」為理由的報錯,
|
||||
// 所以這裡添加這句、來試圖應對這種情況。
|
||||
if keyHandler.delegate == nil { keyHandler.delegate = self }
|
||||
if inputHandler.delegate == nil { inputHandler.delegate = self }
|
||||
// 這裡不需要 setValue(),因為 IMK 會在 activateServer() 之後自動執行 setValue()。
|
||||
keyHandler.clear() // 這句不要砍,因為後面 handle State.Empty() 不一定執行。
|
||||
keyHandler.ensureKeyboardParser()
|
||||
inputHandler.clear() // 這句不要砍,因為後面 handle State.Empty() 不一定執行。
|
||||
inputHandler.ensureKeyboardParser()
|
||||
|
||||
Self.theShiftKeyDetector.alsoToggleWithLShift = PrefMgr.shared.togglingAlphanumericalModeWithLShift
|
||||
|
||||
|
@ -213,7 +213,7 @@ extension SessionCtl {
|
|||
/// - Parameter sender: 呼叫了該函式的客體(無須使用)。
|
||||
public override func deactivateServer(_ sender: Any!) {
|
||||
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||
resetKeyHandler() // 這條會自動搞定 Empty 狀態。
|
||||
resetInputHandler() // 這條會自動搞定 Empty 狀態。
|
||||
handle(state: IMEState.ofDeactivated())
|
||||
Self.allInstances.remove(self)
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ extension SessionCtl {
|
|||
/// - Parameter sender: 呼叫了該函式的客體(無須使用)。
|
||||
public override func commitComposition(_ sender: Any!) {
|
||||
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
// super.commitComposition(sender) // 這句不要引入,否則每次切出輸入法時都會死當。
|
||||
}
|
||||
|
||||
|
@ -289,7 +289,7 @@ extension SessionCtl {
|
|||
/// 不過好像因為 IMK 的 Bug 而並不會被執行。
|
||||
public override func inputControllerWillClose() {
|
||||
// 下述兩行用來防止尚未完成拼寫的注音內容貝蒂交出去。
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
super.inputControllerWillClose()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
import Shared
|
||||
|
||||
// MARK: - KeyHandler Delegate
|
||||
// MARK: - InputHandler Delegate
|
||||
|
||||
extension SessionCtl: KeyHandlerDelegate {
|
||||
extension SessionCtl: InputHandlerDelegate {
|
||||
public var clientBundleIdentifier: String {
|
||||
guard let client = client() else { return "" }
|
||||
return client.bundleIdentifier() ?? ""
|
||||
|
@ -18,7 +18,7 @@ extension SessionCtl: KeyHandlerDelegate {
|
|||
|
||||
public func candidateController() -> CtlCandidateProtocol { ctlCandidateCurrent }
|
||||
|
||||
public func candidateSelectionCalledByKeyHandler(at index: Int) {
|
||||
public func candidateSelectionCalledByInputHandler(at index: Int) {
|
||||
candidatePairSelected(at: index)
|
||||
}
|
||||
|
||||
|
@ -86,18 +86,18 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
|
||||
if [.ofCandidates, .ofSymbolTable].contains(state.type) {
|
||||
let selectedValue = state.candidates[index]
|
||||
keyHandler.fixNode(
|
||||
inputHandler.consolidateNode(
|
||||
candidate: selectedValue, respectCursorPushing: true,
|
||||
preConsolidate: PrefMgr.shared.consolidateContextOnCandidateSelection
|
||||
)
|
||||
|
||||
let inputting = keyHandler.buildInputtingState
|
||||
let inputting = inputHandler.generateStateOfInputting()
|
||||
|
||||
if PrefMgr.shared.useSCPCTypingMode {
|
||||
handle(state: IMEState.ofCommitting(textToCommit: inputting.displayedText))
|
||||
// 此時是逐字選字模式,所以「selectedValue.1」是單個字、不用追加處理。
|
||||
if PrefMgr.shared.associatedPhrasesEnabled {
|
||||
let associates = keyHandler.buildAssociatePhraseState(
|
||||
let associates = inputHandler.generateStateOfAssociates(
|
||||
withPair: .init(key: selectedValue.0, value: selectedValue.1)
|
||||
)
|
||||
handle(state: associates.candidates.isEmpty ? IMEState.ofEmpty() : associates)
|
||||
|
@ -120,7 +120,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
return
|
||||
}
|
||||
if PrefMgr.shared.associatedPhrasesEnabled {
|
||||
let associates = keyHandler.buildAssociatePhraseState(
|
||||
let associates = inputHandler.generateStateOfAssociates(
|
||||
withPair: .init(key: selectedValue.0, value: String(valueKept))
|
||||
)
|
||||
if !associates.candidates.isEmpty {
|
||||
|
|
|
@ -38,7 +38,7 @@ extension SessionCtl {
|
|||
// 就這傳入的 NSEvent 都還有可能是 nil,Apple InputMethodKit 團隊到底在搞三小。
|
||||
// 只針對特定類型的 client() 進行處理。
|
||||
guard let event = event, sender is IMKTextInput else {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ extension SessionCtl {
|
|||
/// 警告:這裡的 event 必須是原始 event 且不能被 var,否則會影響 Shift 中英模式判定。
|
||||
if #available(macOS 10.15, *) {
|
||||
if Self.theShiftKeyDetector.check(event), !PrefMgr.shared.disableShiftTogglingAlphanumericalMode {
|
||||
if !shouldUseShiftToggleHandle || (!rencentKeyHandledByKeyHandlerEtc && shouldUseShiftToggleHandle) {
|
||||
if !shouldUseShiftToggleHandle || (!rencentKeyHandledByInputHandlerEtc && shouldUseShiftToggleHandle) {
|
||||
let status = NSLocalizedString("NotificationSwitchASCII", comment: "")
|
||||
Notifier.notify(
|
||||
message: isASCIIMode.toggled()
|
||||
|
@ -82,7 +82,7 @@ extension SessionCtl {
|
|||
)
|
||||
}
|
||||
if shouldUseShiftToggleHandle {
|
||||
rencentKeyHandledByKeyHandlerEtc = false
|
||||
rencentKeyHandledByInputHandlerEtc = false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ extension SessionCtl {
|
|||
// 不再讓威注音處理由 Shift 切換到的英文模式的按鍵輸入。
|
||||
if isASCIIMode, !isCapsLocked { return false }
|
||||
|
||||
/// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。
|
||||
/// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 InputHandler 內修復。
|
||||
/// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。
|
||||
/// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false,
|
||||
/// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。
|
||||
|
@ -116,7 +116,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
// 在啟用注音排列而非拼音輸入的情況下,強制將當前鍵盤佈局翻譯為美規鍵盤。
|
||||
if keyHandler.composer.parser.rawValue < 100 {
|
||||
if inputHandler.composer.parser.rawValue < 100 {
|
||||
eventToDeal = eventToDeal.inAppleABCStaticForm
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ extension SessionCtl {
|
|||
|
||||
// IMK 選字窗處理,當且僅當啟用了 IMK 選字窗的時候才會生效。
|
||||
if let result = imkCandidatesEventPreHandler(event: eventToDeal) {
|
||||
if shouldUseShiftToggleHandle { rencentKeyHandledByKeyHandlerEtc = result }
|
||||
if shouldUseShiftToggleHandle { rencentKeyHandledByInputHandlerEtc = result }
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ extension SessionCtl {
|
|||
/// 這樣可以與 IMK 選字窗共用按鍵處理資源,維護起來也比較方便。
|
||||
let result = commonEventHandler(eventToDeal)
|
||||
if shouldUseShiftToggleHandle {
|
||||
rencentKeyHandledByKeyHandlerEtc = result
|
||||
rencentKeyHandledByInputHandlerEtc = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -155,13 +155,13 @@ extension SessionCtl {
|
|||
/// - Returns: 回「`true`」以將該案件已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。
|
||||
private func commonEventHandler(_ event: NSEvent) -> Bool {
|
||||
// 無法列印的訊號輸入,一概不作處理。
|
||||
// 這個過程不能放在 KeyHandler 內,否則不會起作用。
|
||||
// 這個過程不能放在 InputHandler 內,否則不會起作用。
|
||||
if !event.charCode.isPrintable { return false }
|
||||
|
||||
/// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。
|
||||
/// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。
|
||||
/// 這裡不用 keyHandler.handleCandidate() 是因為需要針對聯想詞輸入狀態做額外處理。
|
||||
let result = keyHandler.handle(input: event, state: state) { newState in
|
||||
/// 這裡不用 inputHandler.handleCandidate() 是因為需要針對聯想詞輸入狀態做額外處理。
|
||||
let result = inputHandler.handleInput(event: event, state: state) { newState in
|
||||
self.handle(state: newState)
|
||||
} errorCallback: { errorString in
|
||||
vCLog(errorString)
|
||||
|
@ -178,11 +178,11 @@ extension SessionCtl {
|
|||
// IMK 選字窗處理,當且僅當啟用了 IMK 選字窗的時候才會生效。
|
||||
// 這樣可以讓 interpretKeyEvents() 函式自行判斷:
|
||||
// - 是就地交給 imkCandidates.interpretKeyEvents() 處理?
|
||||
// - 還是藉由 delegate 扔回 SessionCtl 給 KeyHandler 處理?
|
||||
// - 還是藉由 delegate 扔回 SessionCtl 給 InputHandler 處理?
|
||||
if let imkCandidates = ctlCandidateCurrent as? CtlCandidateIMK, imkCandidates.visible {
|
||||
let event: NSEvent = CtlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal
|
||||
|
||||
// Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。
|
||||
// Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 inputHandler 從而崩潰。
|
||||
// 所以這裡直接將 Shift Flags 清空。
|
||||
if event.isShiftHold, event.isEnter {
|
||||
guard let newEvent = event.reinitiate(modifierFlags: []) else {
|
||||
|
|
|
@ -34,7 +34,7 @@ extension SessionCtl {
|
|||
}
|
||||
clearInlineDisplay()
|
||||
// 最後一道保險
|
||||
keyHandler.clear()
|
||||
inputHandler.clear()
|
||||
// 特殊處理:deactivateServer() 可能會遲於另一個客體會話的 activateServer() 執行。
|
||||
// 雖然所有在這個函式內影響到的變數都改為動態變數了(不會出現跨副本波及的情況),
|
||||
// 但 IMKCandidates 是有內部共用副本的、會被波及。
|
||||
|
@ -62,14 +62,14 @@ extension SessionCtl {
|
|||
tooltipInstance.hide()
|
||||
clearInlineDisplay()
|
||||
// 最後一道保險
|
||||
keyHandler.clear()
|
||||
inputHandler.clear()
|
||||
case .ofCommitting:
|
||||
ctlCandidateCurrent.visible = false
|
||||
tooltipInstance.hide()
|
||||
commit(text: newState.textToCommit)
|
||||
clearInlineDisplay()
|
||||
// 最後一道保險
|
||||
keyHandler.clear()
|
||||
inputHandler.clear()
|
||||
case .ofInputting:
|
||||
ctlCandidateCurrent.visible = false
|
||||
tooltipInstance.hide()
|
||||
|
|
|
@ -57,7 +57,7 @@ extension SessionCtl {
|
|||
public override func candidateSelectionChanged(_: NSAttributedString!) {
|
||||
// 警告:不要考慮用實作這個函式的方式來更新內文組字區的顯示。
|
||||
// 因為這樣會導致 IMKServer.commitCompositionWithReply() 呼叫你本來不想呼叫的 commitComposition(),
|
||||
// 然後 keyHandler 會被重設,屆時輸入法會在狀態處理等方面崩潰掉。
|
||||
// 然後 inputHandler 會被重設,屆時輸入法會在狀態處理等方面崩潰掉。
|
||||
|
||||
// 這個函式的實作其實很容易誘發各種崩潰,所以最好不要輕易實作。
|
||||
|
||||
|
|
|
@ -221,7 +221,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleSCPCTypingMode(_: Any? = nil) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("Per-Char Select Mode", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.useSCPCTypingMode.toggled()
|
||||
|
@ -231,7 +231,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleChineseConverter(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("Force KangXi Writing", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.chineseConversionEnabled.toggled()
|
||||
|
@ -241,7 +241,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleShiftJISShinjitaiOutput(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("JIS Shinjitai Output", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.shiftJISShinjitaiOutputEnabled.toggled()
|
||||
|
@ -251,7 +251,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleCurrencyNumerals(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("Currency Numeral Output", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.currencyNumeralsEnabled.toggled()
|
||||
|
@ -261,7 +261,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleHalfWidthPunctuation(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("Half-Width Punctuation Mode", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.halfWidthPunctuationEnabled.toggled()
|
||||
|
@ -271,7 +271,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleCNS11643Enabled(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("CNS11643 Mode", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.cns11643Enabled.toggled()
|
||||
|
@ -281,7 +281,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleSymbolEnabled(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("Symbol & Emoji Input", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.symbolInputEnabled.toggled()
|
||||
|
@ -291,7 +291,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func toggleAssociatedPhrasesEnabled(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("Per-Char Associated Phrases", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.associatedPhrasesEnabled.toggled()
|
||||
|
@ -301,7 +301,7 @@ extension SessionCtl {
|
|||
}
|
||||
|
||||
@objc public func togglePhraseReplacement(_: Any?) {
|
||||
resetKeyHandler()
|
||||
resetInputHandler()
|
||||
Notifier.notify(
|
||||
message: NSLocalizedString("Use Phrase Replacement", comment: "") + "\n"
|
||||
+ (PrefMgr.shared.phraseReplacementEnabled.toggled()
|
||||
|
|
|
@ -79,7 +79,7 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol {
|
|||
public required init(_ layout: NSUserInterfaceLayoutOrientation = .horizontal) {
|
||||
super.init(server: theServer, panelType: kIMKScrollingGridCandidatePanel)
|
||||
specifyLayout(layout)
|
||||
// 設為 true 表示先交給 ctlIME 處理
|
||||
// 設為 true 表示先交給 SessionCtl 處理
|
||||
setAttributes([IMKCandidatesSendServerKeyEventFirst: true])
|
||||
visible = false
|
||||
// guard let currentTISInputSource = currentTISInputSource else { return } // 下面兩句都沒用,所以註釋掉。
|
||||
|
@ -101,7 +101,7 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol {
|
|||
}
|
||||
|
||||
/// 幹話:這裡很多函式內容亂寫也都無所謂了,因為都被 IMKCandidates 代管執行。
|
||||
/// 對於所有 IMK 選字窗的選字判斷動作,不是在 keyHandler 中,而是在 `ctlIME_Core` 中。
|
||||
/// 對於所有 IMK 選字窗的選字判斷動作,不是在 inputHandler 中,而是在 `SessionCtl_HandleEvent` 中。
|
||||
|
||||
// 該函式會影響 IMK 選字窗。
|
||||
@discardableResult public func showNextPage() -> Bool {
|
||||
|
|
|
@ -15,18 +15,18 @@
|
|||
5B21176C287539BB000443A9 /* SessionCtl_HandleStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176B287539BB000443A9 /* SessionCtl_HandleStates.swift */; };
|
||||
5B21176E28753B35000443A9 /* SessionCtl_HandleDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176D28753B35000443A9 /* SessionCtl_HandleDisplay.swift */; };
|
||||
5B21177028753B9D000443A9 /* SessionCtl_Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176F28753B9D000443A9 /* SessionCtl_Delegates.swift */; };
|
||||
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */; };
|
||||
5B3133BF280B229700A4A505 /* InputHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* InputHandler_States.swift */; };
|
||||
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
|
||||
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; };
|
||||
5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */ = {isa = PBXBuildFile; productRef = 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */; };
|
||||
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
|
||||
5B6C141228A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift */; };
|
||||
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
|
||||
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; };
|
||||
5B782EC4280C243C007276DE /* InputHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */; };
|
||||
5B78EE0D28A562B4009456C1 /* suiPrefPaneDevZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B78EE0C28A562B4009456C1 /* suiPrefPaneDevZone.swift */; };
|
||||
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
|
||||
5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */; };
|
||||
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */; };
|
||||
5B7F225D2808501000DDD3CB /* InputHandler_HandleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* InputHandler_HandleInput.swift */; };
|
||||
5B84579E2871AD2200C93B01 /* convdict.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5B84579C2871AD2200C93B01 /* convdict.plist */; };
|
||||
5B8457A12871ADBE00C93B01 /* ChineseConverterBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */; };
|
||||
5B963C9D28D5BFB800DCEE88 /* CocoaExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963C9C28D5BFB800DCEE88 /* CocoaExtension */; };
|
||||
|
@ -60,7 +60,7 @@
|
|||
5BC5E02128DDEFE00094E427 /* TooltipUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5BC5E02028DDEFE00094E427 /* TooltipUI */; };
|
||||
5BC5E02428DE07860094E427 /* PopupCompositionBuffer in Frameworks */ = {isa = PBXBuildFile; productRef = 5BC5E02328DE07860094E427 /* PopupCompositionBuffer */; };
|
||||
5BCCAFF828DB19A300AB1B27 /* PrefMgr_Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCCAFF728DB19A300AB1B27 /* PrefMgr_Extension.swift */; };
|
||||
5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD0113C2818543900609769 /* KeyHandler_Core.swift */; };
|
||||
5BD0113D2818543900609769 /* InputHandler_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD0113C2818543900609769 /* InputHandler_Core.swift */; };
|
||||
5BD05BCA27B2A43D004C4F1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; };
|
||||
5BD05C5D27B2BBA9004C4F1D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05C5B27B2BBA9004C4F1D /* Main.storyboard */; };
|
||||
5BD05C6627B2BBEF004C4F1D /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD05C6127B2BBEF004C4F1D /* Document.swift */; };
|
||||
|
@ -77,7 +77,7 @@
|
|||
5BDB7A4528D4824A001AC277 /* ShiftKeyUpChecker in Frameworks */ = {isa = PBXBuildFile; productRef = 5BDB7A4428D4824A001AC277 /* ShiftKeyUpChecker */; };
|
||||
5BDB7A4728D4824A001AC277 /* Tekkon in Frameworks */ = {isa = PBXBuildFile; productRef = 5BDB7A4628D4824A001AC277 /* Tekkon */; };
|
||||
5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */; };
|
||||
5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */; };
|
||||
5BE377A0288FED8D0037365B /* InputHandler_HandleComposition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE3779F288FED8D0037365B /* InputHandler_HandleComposition.swift */; };
|
||||
5BE78BD927B3775B005EA1BE /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */; };
|
||||
5BE78BDD27B3776D005EA1BE /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE78BDA27B37764005EA1BE /* frmAboutWindow.xib */; };
|
||||
5BEDB721283B4C250078EB25 /* data-cns.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71D283B4AEA0078EB25 /* data-cns.plist */; };
|
||||
|
@ -202,7 +202,7 @@
|
|||
5B2F2BB3286216A500B8557B /* vChewingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = vChewingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = "<group>"; };
|
||||
5B312684287800DE001AA720 /* FAQ.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = FAQ.md; sourceTree = "<group>"; };
|
||||
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B3133BE280B229700A4A505 /* InputHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; };
|
||||
5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = ShapsBenkau_SwiftUIBackports; path = Packages/ShapsBenkau_SwiftUIBackports; sourceTree = "<group>"; };
|
||||
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
|
@ -210,12 +210,12 @@
|
|||
5B6C141128A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCtl_HandleEvent.swift; sourceTree = "<group>"; };
|
||||
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
|
||||
5B73FB5F27B2BE1300E9BF49 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_HandleCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler_HandleCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B78EE0C28A562B4009456C1 /* suiPrefPaneDevZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = suiPrefPaneDevZone.swift; sourceTree = "<group>"; };
|
||||
5B7BC4AF27AFFBE800F66C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmPrefWindow.xib; sourceTree = "<group>"; };
|
||||
5B7BC4B227AFFC0B00F66C24 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmPrefWindow.strings; sourceTree = "<group>"; };
|
||||
5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = fixinstall.sh; sourceTree = "<group>"; };
|
||||
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_HandleInput.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B7F225C2808501000DDD3CB /* InputHandler_HandleInput.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler_HandleInput.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B84579C2871AD2200C93B01 /* convdict.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = convdict.plist; sourceTree = "<group>"; };
|
||||
5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChineseConverterBridge.swift; sourceTree = "<group>"; };
|
||||
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CocoaExtension; path = Packages/vChewing_CocoaExtension; sourceTree = "<group>"; };
|
||||
|
@ -249,7 +249,7 @@
|
|||
5BC5E01F28DDEFD80094E427 /* vChewing_TooltipUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_TooltipUI; path = Packages/vChewing_TooltipUI; sourceTree = "<group>"; };
|
||||
5BC5E02228DE07250094E427 /* vChewing_PopupCompositionBuffer */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PopupCompositionBuffer; path = Packages/vChewing_PopupCompositionBuffer; sourceTree = "<group>"; };
|
||||
5BCCAFF728DB19A300AB1B27 /* PrefMgr_Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefMgr_Extension.swift; sourceTree = "<group>"; };
|
||||
5BD0113C2818543900609769 /* KeyHandler_Core.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = KeyHandler_Core.swift; sourceTree = "<group>"; usesTabs = 0; };
|
||||
5BD0113C2818543900609769 /* InputHandler_Core.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; lineEnding = 0; path = InputHandler_Core.swift; sourceTree = "<group>"; usesTabs = 0; };
|
||||
5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewingPhraseEditor.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5BD05BC627B2A42A004C4F1D /* vChewingPhraseEditor.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vChewingPhraseEditor.entitlements; sourceTree = "<group>"; };
|
||||
5BD05C5C27B2BBA9004C4F1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
|
@ -275,7 +275,7 @@
|
|||
5BDCBB4A27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
5BDCBB4B27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmAboutWindow.strings"; sourceTree = "<group>"; };
|
||||
5BDCBB4D27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_HandleComposition.swift; sourceTree = "<group>"; };
|
||||
5BE3779F288FED8D0037365B /* InputHandler_HandleComposition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputHandler_HandleComposition.swift; sourceTree = "<group>"; };
|
||||
5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5BE78BDB27B37764005EA1BE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; };
|
||||
5BE78BDF27B37968005EA1BE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = "<group>"; };
|
||||
|
@ -666,11 +666,11 @@
|
|||
5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */,
|
||||
5BF56F9728C39A2700DD6839 /* IMEState.swift */,
|
||||
5BF56F9928C39D1800DD6839 /* IMEStateData.swift */,
|
||||
5BD0113C2818543900609769 /* KeyHandler_Core.swift */,
|
||||
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
|
||||
5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */,
|
||||
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */,
|
||||
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
|
||||
5BD0113C2818543900609769 /* InputHandler_Core.swift */,
|
||||
5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */,
|
||||
5BE3779F288FED8D0037365B /* InputHandler_HandleComposition.swift */,
|
||||
5B7F225C2808501000DDD3CB /* InputHandler_HandleInput.swift */,
|
||||
5B3133BE280B229700A4A505 /* InputHandler_States.swift */,
|
||||
5BAEFACF28012565001F42C9 /* LMMgr.swift */,
|
||||
D47B92BF27972AC800458394 /* main.swift */,
|
||||
5B963CA728D5DB1400DCEE88 /* PrefMgr_Core.swift */,
|
||||
|
@ -1053,7 +1053,7 @@
|
|||
files = (
|
||||
5B963CA828D5DB1400DCEE88 /* PrefMgr_Core.swift in Sources */,
|
||||
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */,
|
||||
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */,
|
||||
5B7F225D2808501000DDD3CB /* InputHandler_HandleInput.swift in Sources */,
|
||||
5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */,
|
||||
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */,
|
||||
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */,
|
||||
|
@ -1063,15 +1063,15 @@
|
|||
5B21177028753B9D000443A9 /* SessionCtl_Delegates.swift in Sources */,
|
||||
5B21176E28753B35000443A9 /* SessionCtl_HandleDisplay.swift in Sources */,
|
||||
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */,
|
||||
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */,
|
||||
5B3133BF280B229700A4A505 /* InputHandler_States.swift in Sources */,
|
||||
5B78EE0D28A562B4009456C1 /* suiPrefPaneDevZone.swift in Sources */,
|
||||
5B6C141228A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift in Sources */,
|
||||
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */,
|
||||
5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */,
|
||||
5BD0113D2818543900609769 /* InputHandler_Core.swift in Sources */,
|
||||
5BF56F9A28C39D1800DD6839 /* IMEStateData.swift in Sources */,
|
||||
5B21176C287539BB000443A9 /* SessionCtl_HandleStates.swift in Sources */,
|
||||
5BAEFAD028012565001F42C9 /* LMMgr.swift in Sources */,
|
||||
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
|
||||
5B782EC4280C243C007276DE /* InputHandler_HandleCandidate.swift in Sources */,
|
||||
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
|
||||
5BCCAFF828DB19A300AB1B27 /* PrefMgr_Extension.swift in Sources */,
|
||||
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
|
||||
|
@ -1079,7 +1079,7 @@
|
|||
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
|
||||
5BFDF011289635C100417BBC /* IMKCandidatesImpl.swift in Sources */,
|
||||
5BB802DA27FABA8300CF1C19 /* SessionCtl_Menu.swift in Sources */,
|
||||
5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */,
|
||||
5BE377A0288FED8D0037365B /* InputHandler_HandleComposition.swift in Sources */,
|
||||
5B00FA0C28DEC17200F6D436 /* SessionCtl_IMKCandidatesData.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
Loading…
Reference in New Issue