Repo // Deprecating Zonble's InputState, using IMEState instead.

- This allows adding user phrases with high-unicode chars like emojis.
- AUTHORS // Remove attribution of Zonble's InputState.
This commit is contained in:
ShikiSuen 2022-09-04 09:10:14 +08:00
parent a5fa288f97
commit 5f89fdf9b2
17 changed files with 530 additions and 1158 deletions

View File

@ -15,7 +15,7 @@ $ 3rd-Party Modules Used:
$ Contributors and volunteers of the upstream repo, having no responsibility in discussing anything in the current repo:
- Zonble Yang:
- McBopomofo for macOS 2.x architect, especially state-based IME behavior management.
- McBopomofo for macOS 2.x architect.
- Voltaire candidate window MK2 (massively modified as MK3 in vChewing by Shiki Suen).
- Notifier window and Tooltip UI.
- NSStringUtils and FSEventStreamHelper.

View File

@ -9,39 +9,83 @@
import Foundation
// enum
public enum StateType {
case ofDeactivated
case ofEmpty
case ofAbortion // Empty
case ofCommitting
case ofAssociates
case ofNotEmpty
case ofInputting
case ofMarking
case ofCandidates
case ofSymbolTable
public enum StateType: String {
case ofDeactivated = "Deactivated"
case ofEmpty = "Empty"
case ofAbortion = "Abortion" // Empty
case ofCommitting = "Committing"
case ofAssociates = "Associates"
case ofNotEmpty = "NotEmpty"
case ofInputting = "Inputting"
case ofMarking = "Marking"
case ofCandidates = "Candidates"
case ofSymbolTable = "SymbolTable"
}
// InputState
public protocol InputStateProtocol {
// IMEState
public protocol IMEStateProtocol {
var type: StateType { get }
var data: StateData { get }
var hasBuffer: Bool { get }
var candidates: [(String, String)] { get }
var hasComposition: Bool { get }
var isCandidateContainer: Bool { get }
var displayedText: String { get }
var textToCommit: String { get set }
var tooltip: String { get set }
var attributedString: NSAttributedString { get }
var convertedToInputting: IMEState { get }
var isFilterable: Bool { get }
var node: SymbolNode { get set }
}
public struct IMEState {
/// ctlInputMethod
///
/// Finite State Machine/
/// 使
/// 使
///
///
/// IMEState
/// 使
///
///
///
/// IMEState
/// IMEState.Marking IMEState.Inputting
///
/// (Constructor)
///
///
///
/// - .Deactivated: 使使
/// - .AssociatedPhrases:
/// 西 .NotEmpty
/// - .Empty: 使
///
/// - .Abortion: Empty
/// .Empty()
/// - .Committing:
/// - .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
public struct IMEState: IMEStateProtocol {
public var type: StateType = .ofEmpty
public var data: StateData = .init()
public var node: SymbolNode = .init("")
init(_ data: StateData = .init(), type: StateType = .ofEmpty) {
self.data = data
self.type = type
}
init(_ data: StateData = .init(), type: StateType = .ofEmpty, node: SymbolNode) {
self.data = data
self.type = type
self.node = node
self.data.candidates = { node.children?.map(\.title) ?? [String]() }().map { ("", $0) }
}
}
// MARK: -
@ -63,59 +107,56 @@ extension IMEState {
return result
}
public static func NotEmpty(nodeValues: [String], reading: String = "", cursor: Int) -> IMEState {
public static func NotEmpty(displayTextSegments: [String], cursor: Int) -> IMEState {
var result = IMEState(type: .ofNotEmpty)
// nodeValuesArray reading
result.data.nodeValuesArray = nodeValues
if !reading.isEmpty {
result.data.reading = reading // nodeValuesArray
}
// nodeValuesArray 使
result.data.displayedText = result.data.nodeValuesArray.joined()
// displayTextSegments
result.data.displayTextSegments = displayTextSegments
result.data.cursor = cursor
return result
}
public static func Inputting(nodeValues: [String], reading: String = "", cursor: Int) -> IMEState {
var result = IMEState.NotEmpty(nodeValues: nodeValues, reading: reading, cursor: cursor)
public static func Inputting(displayTextSegments: [String], cursor: Int) -> IMEState {
var result = IMEState.NotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofInputting
return result
}
public static func Marking(nodeValues: [String], nodeReadings: [String], cursor: Int, marker: Int) -> IMEState {
var result = IMEState.NotEmpty(nodeValues: nodeValues, cursor: cursor)
public static func Marking(
displayTextSegments: [String], markedReadings: [String], cursor: Int, marker: Int
)
-> IMEState
{
var result = IMEState.NotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofMarking
result.data.nodeReadingsArray = nodeReadings
result.data.marker = marker
result.data.markedReadings = markedReadings
StateData.Marking.updateParameters(&result.data)
return result
}
public static func Candidates(candidates: [(String, String)], nodeValues: [String], cursor: Int) -> IMEState {
var result = IMEState.NotEmpty(nodeValues: nodeValues, cursor: cursor)
public static func Candidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int) -> IMEState
{
var result = IMEState.NotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofCandidates
result.data.candidates = candidates
return result
}
public static func SymbolTable(node: SymbolNode, previous: SymbolNode? = nil) -> IMEState {
let candidates = { node.children?.map(\.title) ?? [String]() }().map { ("", $0) }
var result = IMEState.Candidates(candidates: candidates, nodeValues: [], cursor: 0)
public static func SymbolTable(node: SymbolNode) -> IMEState {
var result = IMEState(type: .ofNotEmpty, node: node)
result.type = .ofSymbolTable
result.data.node = node
if let previous = previous {
result.data.node.previous = previous
}
return result
}
}
// MARK: -
extension IMEState: InputStateProtocol {
extension IMEState {
public var isFilterable: Bool { data.isFilterable }
public var candidates: [(String, String)] { data.candidates }
public var convertedToInputting: IMEState {
if type == .ofInputting { return self }
var result = IMEState.Inputting(nodeValues: data.nodeValuesArray, reading: data.reading, cursor: data.cursor)
var result = IMEState.Inputting(displayTextSegments: data.displayTextSegments, cursor: data.cursor)
result.tooltip = data.tooltipBackupForInputting
return result
}
@ -146,25 +187,7 @@ extension IMEState: InputStateProtocol {
}
}
public var node: SymbolNode {
get {
data.node
}
set {
data.node = newValue
}
}
public var tooltipBackupForInputting: String {
get {
data.tooltipBackupForInputting
}
set {
data.tooltipBackupForInputting = newValue
}
}
public var hasBuffer: Bool {
public var hasComposition: Bool {
switch type {
case .ofNotEmpty, .ofInputting, .ofMarking, .ofCandidates: return true
default: return false

View File

@ -38,6 +38,9 @@ public struct StateData {
// MARK: Cursor & Marker & Range for UTF16 (Read-Only)
/// IMK UTF8 emoji
/// Swift.utf16NSString.length()
///
var u16Cursor: Int {
displayedText.charComponents[0..<cursor].joined().utf16.count
}
@ -53,32 +56,14 @@ public struct StateData {
// MARK: Other data for non-empty states.
var markedTargetExists: Bool = false
var nodeReadingsArray = [String]()
var nodeValuesArray = [String]()
var reading: String = "" {
var displayTextSegments = [String]() {
didSet {
if !reading.isEmpty {
var newNodeValuesArray = [String]()
var temporaryNode = ""
var charCounter = 0
for node in nodeValuesArray {
for char in node {
if charCounter == cursor - reading.count {
newNodeValuesArray.append(temporaryNode)
temporaryNode = ""
newNodeValuesArray.append(reading)
}
temporaryNode += String(char)
charCounter += 1
}
newNodeValuesArray.append(temporaryNode)
temporaryNode = ""
}
nodeValuesArray = newNodeValuesArray.isEmpty ? [reading] : newNodeValuesArray
}
displayedText = displayTextSegments.joined()
}
}
var reading: String = ""
var markedReadings = [String]()
var candidates = [(String, String)]()
var textToCommit: String = ""
var tooltip: String = ""
@ -94,13 +79,12 @@ public struct StateData {
markedTargetExists ? mgrPrefs.allowedMarkRange.contains(markedRange.count) : false
}
var readingCountMismatched: Bool { displayedText.count != nodeReadingsArray.count }
var attributedStringNormal: NSAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: displayedText)
var newBegin = 0
for (i, neta) in nodeValuesArray.enumerated() {
for (i, neta) in displayTextSegments.enumerated() {
attributedString.setAttributes(
[
/// .thick
@ -147,17 +131,14 @@ public struct StateData {
)
return attributedString
}
var node: SymbolNode = .init("")
}
// MARK: - InputState
// MARK: - IMEState
extension StateData {
var chkIfUserPhraseExists: Bool {
let text = displayedText.charComponents[markedRange].joined()
let selectedReadings = nodeReadingsArray[markedRange]
let joined = selectedReadings.joined(separator: "-")
let joined = markedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
@ -165,8 +146,7 @@ extension StateData {
var userPhrase: String {
let text = displayedText.charComponents[markedRange].joined()
let selectedReadings = nodeReadingsArray[markedRange]
let joined = selectedReadings.joined(separator: "-")
let joined = markedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
return "\(text) \(joined)\(nerfedScore)"
}
@ -174,8 +154,7 @@ extension StateData {
var userPhraseConverted: String {
let text =
ChineseConverter.crossConvert(displayedText.charComponents[markedRange].joined()) ?? ""
let selectedReadings = nodeReadingsArray[markedRange]
let joined = selectedReadings.joined(separator: "-")
let joined = markedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
return "\(text) \(joined)\(nerfedScore)\t\(convertedMark)"
@ -184,7 +163,7 @@ extension StateData {
enum Marking {
private static func generateReadingThread(_ data: StateData) -> String {
var arrOutput = [String]()
for neta in data.nodeReadingsArray[data.markedRange] {
for neta in data.markedReadings {
var neta = neta
if neta.isEmpty { continue }
if neta.contains("_") {
@ -207,12 +186,6 @@ extension StateData {
/// - Parameter data:
public static func updateParameters(_ data: inout StateData) {
var tooltipGenerated: String {
if data.displayedText.count != data.nodeReadingsArray.count {
ctlInputMethod.tooltipController.setColor(state: .redAlert)
return NSLocalizedString(
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: ""
)
}
if mgrPrefs.phraseReplacementEnabled {
ctlInputMethod.tooltipController.setColor(state: .warning)
return NSLocalizedString(
@ -240,8 +213,7 @@ extension StateData {
)
}
let selectedReadings = data.nodeReadingsArray[data.markedRange]
let joined = selectedReadings.joined(separator: "-")
let joined = data.markedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)

View File

@ -1,488 +0,0 @@
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Foundation
// InputState 使 Struct Struct
/// ctlInputMethod
///
/// Finite State Machine/
/// 使
/// 使
///
///
/// InputState
/// 使
///
///
///
/// InputState
/// InputState.Marking InputState.Inputting
///
///
///
///
/// - .Deactivated: 使使
/// - .AssociatedPhrases:
/// 西 .NotEmpty
/// - .Empty: 使
///
/// - .Abortion: Empty
/// .Empty()
/// - .Committing:
/// - .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
public enum InputState {
/// .Deactivated: 使使
class Deactivated: InputStateProtocol {
var node: SymbolNode = .init("")
var attributedString: NSAttributedString = .init()
var data: StateData = .init()
var textToCommit: String = ""
var tooltip: String = ""
let displayedText: String = ""
let hasBuffer: Bool = false
let isCandidateContainer: Bool = false
public var type: StateType { .ofDeactivated }
}
// MARK: -
/// .Empty: 使
///
class Empty: InputStateProtocol {
var node: SymbolNode = .init("")
var attributedString: NSAttributedString = .init()
var data: StateData = .init()
var textToCommit: String = ""
var tooltip: String = ""
let hasBuffer: Bool = false
let isCandidateContainer: Bool = false
public var type: StateType { .ofEmpty }
let displayedText: String = ""
}
// MARK: -
/// .Abortion: Empty
///
/// .Empty()
class Abortion: Empty {
override public var type: StateType { .ofAbortion }
}
// MARK: -
/// .Committing:
class Committing: InputStateProtocol {
var node: SymbolNode = .init("")
var attributedString: NSAttributedString = .init()
var data: StateData = .init()
var tooltip: String = ""
var textToCommit: String = ""
let displayedText: String = ""
let hasBuffer: Bool = false
let isCandidateContainer: Bool = false
public var type: StateType { .ofCommitting }
init(textToCommit: String) {
self.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &self.textToCommit)
}
}
// MARK: -
/// .AssociatedPhrases:
/// 西 .NotEmpty
class Associates: InputStateProtocol {
var node: SymbolNode = .init("")
var attributedString: NSAttributedString = .init()
var data: StateData = .init()
var textToCommit: String = ""
var tooltip: String = ""
let displayedText: String = ""
let hasBuffer: Bool = false
let isCandidateContainer: Bool = true
public var type: StateType { .ofAssociates }
var candidates: [(String, String)] { data.candidates }
init(candidates: [(String, String)]) {
data.candidates = candidates
attributedString = {
let attributedString = NSMutableAttributedString(
string: " ",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
return attributedString
}()
}
}
// MARK: -
/// .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
class NotEmpty: InputStateProtocol {
var node: SymbolNode = .init("")
var attributedString: NSAttributedString = .init()
var data: StateData = .init()
var tooltip: String = ""
var textToCommit: String = ""
let hasBuffer: Bool = true
var isCandidateContainer: Bool { false }
public var type: StateType { .ofNotEmpty }
private(set) var displayedText: String
private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } }
private(set) var reading: String = ""
private(set) var nodeValuesArray = [String]()
public var displayedTextConverted: String {
let converted = IME.kanjiConversionIfRequired(displayedText)
if converted.utf16.count != displayedText.utf16.count
|| converted.count != displayedText.count
{
return displayedText
}
return converted
}
public var committingBufferConverted: String { displayedTextConverted }
init(displayedText: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
self.displayedText = displayedText
self.reading = reading
// reading
if !reading.isEmpty {
var newNodeValuesArray = [String]()
var temporaryNode = ""
var charCounter = 0
for node in nodeValuesArray {
for char in node {
if charCounter == cursorIndex - reading.utf16.count {
newNodeValuesArray.append(temporaryNode)
temporaryNode = ""
newNodeValuesArray.append(reading)
}
temporaryNode += String(char)
charCounter += 1
}
newNodeValuesArray.append(temporaryNode)
temporaryNode = ""
}
self.nodeValuesArray = newNodeValuesArray
} else {
self.nodeValuesArray = nodeValuesArray
}
defer {
self.cursorIndex = cursorIndex
self.attributedString = {
///
/// JIS
let attributedString = NSMutableAttributedString(string: displayedTextConverted)
var newBegin = 0
for (i, neta) in nodeValuesArray.enumerated() {
attributedString.setAttributes(
[
/// .thick
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: i,
], range: NSRange(location: newBegin, length: neta.utf16.count)
)
newBegin += neta.utf16.count
}
return attributedString
}()
}
}
}
// MARK: -
/// .Inputting: 使Compositor
class Inputting: NotEmpty {
override public var type: StateType { .ofInputting }
override public var committingBufferConverted: String {
let committingBuffer = nodeValuesArray.joined()
let converted = IME.kanjiConversionIfRequired(committingBuffer)
if converted.utf16.count != displayedText.utf16.count
|| converted.count != displayedText.count
{
return displayedText
}
return converted
}
override init(displayedText: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
super.init(
displayedText: displayedText, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
}
}
// MARK: -
/// .Marking: 使
///
class Marking: NotEmpty {
override public var type: StateType { .ofMarking }
private var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength
private(set) var markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } }
private(set) var markedRange: Range<Int>
private var literalMarkedRange: Range<Int> {
let lowerBoundLiteral = displayedText.charIndexLiteral(from: markedRange.lowerBound)
let upperBoundLiteral = displayedText.charIndexLiteral(from: markedRange.upperBound)
return lowerBoundLiteral..<upperBoundLiteral
}
var literalReadingThread: String {
var arrOutput = [String]()
for neta in readings[literalMarkedRange] {
var neta = neta
if neta.isEmpty { continue }
if neta.contains("_") {
arrOutput.append("??")
continue
}
if mgrPrefs.showHanyuPinyinInCompositionBuffer { // ->->調
neta = Tekkon.restoreToneOneInZhuyinKey(target: neta)
neta = Tekkon.cnvPhonaToHanyuPinyin(target: neta)
neta = Tekkon.cnvHanyuPinyinToTextbookStyle(target: neta)
} else {
neta = Tekkon.cnvZhuyinChainToTextbookReading(target: neta)
}
arrOutput.append(neta)
}
return arrOutput.joined(separator: " ")
}
private var markedTargetExists = false
var tooltipForMarking: String {
if displayedText.count != readings.count {
ctlInputMethod.tooltipController.setColor(state: .redAlert)
return NSLocalizedString(
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: ""
)
}
if mgrPrefs.phraseReplacementEnabled {
ctlInputMethod.tooltipController.setColor(state: .warning)
return NSLocalizedString(
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry.", comment: ""
)
}
if markedRange.isEmpty {
return ""
}
let text = displayedText.utf16SubString(with: markedRange)
if literalMarkedRange.count < allowedMarkRange.lowerBound {
ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency)
return String(
format: NSLocalizedString(
"\"%@\" length must ≥ 2 for a user phrase.", comment: ""
) + "\n// " + literalReadingThread, text
)
} else if literalMarkedRange.count > allowedMarkRange.upperBound {
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
return String(
format: NSLocalizedString(
"\"%@\" length should ≤ %d for a user phrase.", comment: ""
) + "\n// " + literalReadingThread, text, allowedMarkRange.upperBound
)
}
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
if exist {
markedTargetExists = exist
ctlInputMethod.tooltipController.setColor(state: .prompt)
return String(
format: NSLocalizedString(
"\"%@\" already exists: ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude.",
comment: ""
) + "\n// " + literalReadingThread, text
)
}
ctlInputMethod.tooltipController.resetColor()
return String(
format: NSLocalizedString("\"%@\" selected. ENTER to add user phrase.", comment: "") + "\n// "
+ literalReadingThread,
text
)
}
var tooltipBackupForInputting: String = ""
private(set) var readings: [String]
init(
displayedText: String, cursorIndex: Int, markerIndex: Int, readings: [String], nodeValuesArray: [String] = []
) {
let begin = min(cursorIndex, markerIndex)
let end = max(cursorIndex, markerIndex)
markedRange = begin..<end
self.readings = readings
super.init(
displayedText: displayedText, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray
)
defer {
self.markerIndex = markerIndex
tooltip = tooltipForMarking
attributedString = {
///
/// JIS
let attributedString = NSMutableAttributedString(string: displayedTextConverted)
let end = markedRange.upperBound
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
], range: NSRange(location: 0, length: markedRange.lowerBound)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 1,
],
range: NSRange(
location: markedRange.lowerBound,
length: markedRange.upperBound - markedRange.lowerBound
)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 2,
],
range: NSRange(
location: end,
length: displayedText.utf16.count - end
)
)
return attributedString
}()
}
}
var convertedToInputting: Inputting {
let state = Inputting(
displayedText: displayedText, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
state.tooltip = tooltipBackupForInputting
return state
}
var isFilterable: Bool { markedTargetExists ? allowedMarkRange.contains(literalMarkedRange.count) : false }
var bufferReadingCountMisMatch: Bool { displayedText.count != readings.count }
var chkIfUserPhraseExists: Bool {
let text = displayedText.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
}
var userPhrase: String {
let text = displayedText.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
return "\(text) \(joined)\(nerfedScore)"
}
var userPhraseConverted: String {
let text =
ChineseConverter.crossConvert(displayedText.utf16SubString(with: markedRange)) ?? ""
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
return "\(text) \(joined)\(nerfedScore)\t\(convertedMark)"
}
}
// MARK: -
/// .ChoosingCandidate: 使
class ChoosingCandidate: NotEmpty {
override var isCandidateContainer: Bool { true }
override public var type: StateType { .ofCandidates }
var candidates: [(String, String)]
// ctlInputMethod.candidateSelectionChanged()
public var chosenCandidateString: String = "" {
didSet {
// / JIS
if chosenCandidateString.contains("\u{17}") {
chosenCandidateString = String(chosenCandidateString.split(separator: "\u{17}")[0])
}
if !chosenCandidateString.contains("\u{1A}") { return }
chosenCandidateString = String(chosenCandidateString.split(separator: "\u{1A}").reversed()[0])
}
}
init(
displayedText: String, cursorIndex: Int, candidates: [(String, String)],
nodeValuesArray: [String] = []
) {
self.candidates = candidates
super.init(displayedText: displayedText, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray)
}
}
// MARK: -
/// .SymbolTable:
class SymbolTable: ChoosingCandidate {
override public var type: StateType { .ofSymbolTable }
init(node: SymbolNode, previous: SymbolNode? = nil) {
super.init(displayedText: "", cursorIndex: 0, candidates: [])
self.node = node
if let previous = previous {
self.node.previous = previous
}
let candidates = node.children?.map(\.title) ?? [String]()
self.candidates = candidates.map { ("", $0) }
// InputState.SymbolTable
// MS Word
//
//
// Crediting Qwertyyb: https://github.com/qwertyyb/Fire/issues/55#issuecomment-1133497700
attributedString = {
let attributedString = NSMutableAttributedString(
string: " ",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
return attributedString
}()
}
}
}

View File

@ -25,7 +25,7 @@ protocol KeyHandlerDelegate {
_: KeyHandler, didSelectCandidateAt index: Int,
ctlCandidate controller: ctlCandidateProtocol
)
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol, addToFilter: Bool)
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: IMEStateProtocol, addToFilter: Bool)
-> Bool
}
@ -35,8 +35,6 @@ protocol KeyHandlerDelegate {
public class KeyHandler {
///
let kEpsilon: Double = 0.000001
///
var isCursorCuttingChar = false
///
var isTypingContentEmpty: Bool { composer.isEmpty && compositor.isEmpty }
@ -88,6 +86,21 @@ public class KeyHandler {
// MARK: - Functions dealing with Megrez.
///
/// - Returns:
func currentMarkedRange() -> Range<Int> {
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)
if index == compositor.width { isBound = true }
let rawResult = compositor.walkedNodes.findNode(at: index)?.isReadingMismatched ?? false
return !isBound && rawResult
}
/// Megrez 使便
///
/// 使 Node Crossing
@ -107,7 +120,8 @@ public class KeyHandler {
// GraphViz
if mgrPrefs.isDebugModeEnabled {
let result = compositor.dumpDOT
let appSupportPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].path.appending("vChewing-visualization.dot")
let appSupportPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].path.appending(
"vChewing-visualization.dot")
do {
try result.write(toFile: appSupportPath, atomically: true, encoding: .utf8)
} catch {

View File

@ -23,9 +23,9 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: IMK
func handleCandidate(
state: InputStateProtocol,
state: IMEStateProtocol,
input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard var ctlCandidateCurrent = delegate?.ctlCandidate() else {
@ -41,7 +41,7 @@ extension KeyHandler {
|| ((input.isCursorBackward || input.isCursorForward) && input.isShiftHold)
if cancelCandidateKey {
if state is InputState.Associates
if state.type == .ofAssociates
|| mgrPrefs.useSCPCTypingMode
|| compositor.isEmpty
{
@ -49,13 +49,12 @@ extension KeyHandler {
//
// 使 BackSpace
// compositor.isEmpty
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
} else {
stateCallback(buildInputtingState)
}
if let state = state as? InputState.SymbolTable, let nodePrevious = state.node.previous {
stateCallback(InputState.SymbolTable(node: nodePrevious))
if state.type == .ofSymbolTable, let nodePrevious = state.node.previous, let _ = nodePrevious.children {
stateCallback(IMEState.SymbolTable(node: nodePrevious))
}
return true
}
@ -63,9 +62,8 @@ extension KeyHandler {
// MARK: Enter
if input.isEnter {
if state is InputState.Associates, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
if state.type == .ofAssociates, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
stateCallback(IMEState.Abortion())
return true
}
delegate?.keyHandler(
@ -243,23 +241,15 @@ extension KeyHandler {
// MARK: End Key
var candidates: [(String, String)]!
if let state = state as? InputState.ChoosingCandidate {
candidates = state.candidates
} else if let state = state as? InputState.Associates {
candidates = state.candidates
}
if candidates.isEmpty {
if state.candidates.isEmpty {
return false
} else { // count > 0!isEmpty滿
if input.isEnd || input.emacsKey == EmacsKey.end {
if ctlCandidateCurrent.selectedCandidateIndex == candidates.count - 1 {
if ctlCandidateCurrent.selectedCandidateIndex == state.candidates.count - 1 {
IME.prtDebugIntel("9B69AAAD")
errorCallback()
} else {
ctlCandidateCurrent.selectedCandidateIndex = candidates.count - 1
ctlCandidateCurrent.selectedCandidateIndex = state.candidates.count - 1
}
return true
}
@ -267,13 +257,13 @@ extension KeyHandler {
// MARK: (Associated Phrases)
if state is InputState.Associates {
if state.type == .ofAssociates {
if !input.isShiftHold { return false }
}
var index: Int = NSNotFound
let match: String =
(state is InputState.Associates) ? input.inputTextIgnoringModifiers ?? "" : input.text
(state.type == .ofAssociates) ? input.inputTextIgnoringModifiers ?? "" : input.text
for j in 0..<ctlCandidateCurrent.keyLabels.count {
let label: CandidateKeyLabel = ctlCandidateCurrent.keyLabels[j]
@ -293,7 +283,7 @@ extension KeyHandler {
}
}
if state is InputState.Associates { return false }
if state.type == .ofAssociates { return false }
// MARK: (SCPC Mode Processing)
@ -333,10 +323,9 @@ extension KeyHandler {
didSelectCandidateAt: candidateIndex,
ctlCandidate: ctlCandidateCurrent
)
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
return handle(
input: input, state: InputState.Empty(), stateCallback: stateCallback, errorCallback: errorCallback
input: input, state: IMEState.Empty(), stateCallback: stateCallback, errorCallback: errorCallback
)
}
return true

View File

@ -19,7 +19,7 @@ extension KeyHandler {
/// - Returns: IMK
func handleComposition(
input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool? {
// MARK: (Handle BPMF Keys)
@ -100,8 +100,7 @@ extension KeyHandler {
switch compositor.isEmpty {
case false: stateCallback(buildInputtingState)
case true:
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
}
return true // IMK
}
@ -124,31 +123,26 @@ extension KeyHandler {
///
if mgrPrefs.useSCPCTypingMode {
let choosingCandidates: InputState.ChoosingCandidate = buildCandidate(
let candidateState: IMEState = buildCandidate(
state: inputting,
isTypingVertical: input.isTypingVertical
)
if choosingCandidates.candidates.count == 1, let firstCandidate = choosingCandidates.candidates.first {
if candidateState.candidates.count == 1, let firstCandidate = candidateState.candidates.first {
let reading: String = firstCandidate.0
let text: String = firstCandidate.1
stateCallback(InputState.Committing(textToCommit: text))
stateCallback(IMEState.Committing(textToCommit: text))
if !mgrPrefs.associatedPhrasesEnabled {
stateCallback(InputState.Empty())
stateCallback(IMEState.Empty())
} else {
if let associatedPhrases =
let associatedPhrases =
buildAssociatePhraseState(
withPair: .init(key: reading, value: text),
isTypingVertical: input.isTypingVertical
), !associatedPhrases.candidates.isEmpty
{
stateCallback(associatedPhrases)
} else {
stateCallback(InputState.Empty())
}
withPair: .init(key: reading, value: text)
)
stateCallback(associatedPhrases.candidates.isEmpty ? IMEState.Empty() : associatedPhrases)
}
} else {
stateCallback(choosingCandidates)
stateCallback(candidateState)
}
}
// ctlInputMethod IMK

View File

@ -25,8 +25,8 @@ extension KeyHandler {
/// - Returns: IMK
func handle(
input: InputSignalProtocol,
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
// inputTest
@ -39,7 +39,7 @@ extension KeyHandler {
if input.isInvalid {
// .Empty(IgnoringPreviousState) .Deactivated
// .Abortion.Empty
if state is InputState.Empty || state is InputState.Deactivated {
if state.type == .ofEmpty || state.type == .ofDeactivated {
return false
}
IME.prtDebugIntel("550BCF7B: KeyHandler just refused an invalid input.")
@ -51,7 +51,7 @@ extension KeyHandler {
//
let isFunctionKey: Bool =
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey)
if !(state is InputState.NotEmpty) && !(state is InputState.Associates) && isFunctionKey {
if state.type != .ofAssociates, !state.hasComposition, !state.isCandidateContainer, isFunctionKey {
return false
}
@ -68,7 +68,7 @@ extension KeyHandler {
// BackSpace
} else if input.isCapsLockOn || input.isASCIIModeInput {
//
stateCallback(InputState.Empty())
stateCallback(IMEState.Empty())
// Shift
if input.isUpperCaseASCIILetterKey {
@ -82,8 +82,8 @@ extension KeyHandler {
}
//
stateCallback(InputState.Committing(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty())
stateCallback(IMEState.Committing(textToCommit: inputText.lowercased()))
stateCallback(IMEState.Empty())
return true
}
@ -94,19 +94,19 @@ extension KeyHandler {
// 使 Cocoa flags
//
if input.isNumericPadKey {
if !(state is InputState.ChoosingCandidate || state is InputState.Associates
|| state is InputState.SymbolTable)
if !(state.type == .ofCandidates || state.type == .ofAssociates
|| state.type == .ofSymbolTable)
{
stateCallback(InputState.Empty())
stateCallback(InputState.Committing(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty())
stateCallback(IMEState.Empty())
stateCallback(IMEState.Committing(textToCommit: inputText.lowercased()))
stateCallback(IMEState.Empty())
return true
}
}
// MARK: (Handle Candidates)
if state is InputState.ChoosingCandidate {
if [.ofCandidates, .ofSymbolTable].contains(state.type) {
return handleCandidate(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
)
@ -114,26 +114,26 @@ extension KeyHandler {
// MARK: (Handle Associated Phrases)
if state is InputState.Associates {
if state.type == .ofAssociates {
if handleCandidate(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
) {
return true
} else {
stateCallback(InputState.Empty())
stateCallback(IMEState.Empty())
}
}
// MARK: 便使() (Handle Marking)
if let marking = state as? InputState.Marking {
if state.type == .ofMarking {
if handleMarkingState(
marking, input: input, stateCallback: stateCallback,
state, input: input, stateCallback: stateCallback,
errorCallback: errorCallback
) {
return true
}
state = marking.convertedToInputting
state = state.convertedToInputting
stateCallback(state)
}
@ -147,7 +147,7 @@ extension KeyHandler {
// MARK: (Calling candidate window using Up / Down or PageUp / PageDn.)
if let currentState = state as? InputState.NotEmpty, composer.isEmpty, !input.isOptionHold,
if state.hasComposition, composer.isEmpty, !input.isOptionHold,
input.isCursorClockLeft || input.isCursorClockRight || input.isSpace
|| input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
{
@ -155,12 +155,12 @@ extension KeyHandler {
/// Space
if !mgrPrefs.chooseCandidateUsingSpace {
if compositor.cursor >= compositor.length {
let displayedText = currentState.displayedText
let displayedText = state.displayedText
if !displayedText.isEmpty {
stateCallback(InputState.Committing(textToCommit: displayedText))
stateCallback(IMEState.Committing(textToCommit: displayedText))
}
stateCallback(InputState.Committing(textToCommit: " "))
stateCallback(InputState.Empty())
stateCallback(IMEState.Committing(textToCommit: " "))
stateCallback(IMEState.Empty())
} else if currentLM.hasUnigramsFor(key: " ") {
compositor.insertKey(" ")
walk()
@ -175,7 +175,7 @@ extension KeyHandler {
)
}
}
stateCallback(buildCandidate(state: currentState, isTypingVertical: input.isTypingVertical))
stateCallback(buildCandidate(state: state))
return true
}
@ -236,7 +236,7 @@ extension KeyHandler {
// MARK: Clock-Left & Clock-Right
if input.isCursorClockLeft || input.isCursorClockRight {
if input.isOptionHold, state is InputState.Inputting {
if input.isOptionHold, state.type == .ofInputting {
if input.isCursorClockRight {
return handleInlineCandidateRotation(
state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback
@ -285,7 +285,7 @@ extension KeyHandler {
walk()
let inputting = buildInputtingState
stateCallback(inputting)
stateCallback(buildCandidate(state: inputting, isTypingVertical: input.isTypingVertical))
stateCallback(buildCandidate(state: inputting))
} else { //
IME.prtDebugIntel("17446655")
errorCallback()
@ -297,14 +297,14 @@ extension KeyHandler {
// Enter 使 commit buffer
// bool _ =
_ = handleEnter(state: state, stateCallback: stateCallback)
stateCallback(InputState.SymbolTable(node: SymbolNode.root, isTypingVertical: input.isTypingVertical))
stateCallback(IMEState.SymbolTable(node: SymbolNode.root))
return true
}
}
// MARK: / (FW / HW Arabic Numbers Input)
if state is InputState.Empty {
if state.type == .ofEmpty {
if input.isMainAreaNumKey, input.isShiftHold, input.isOptionHold, !input.isControlHold, !input.isCommandHold {
// NOTE: macOS 10.11 El Capitan CFStringTransform StringTransform:
// https://developer.apple.com/documentation/foundation/stringtransform
@ -312,9 +312,9 @@ extension KeyHandler {
let string = NSMutableString(string: stringRAW)
CFStringTransform(string, nil, kCFStringTransformFullwidthHalfwidth, true)
stateCallback(
InputState.Committing(textToCommit: mgrPrefs.halfWidthPunctuationEnabled ? stringRAW : string as String)
IMEState.Committing(textToCommit: mgrPrefs.halfWidthPunctuationEnabled ? stringRAW : string as String)
)
stateCallback(InputState.Empty())
stateCallback(IMEState.Empty())
return true
}
}
@ -357,10 +357,10 @@ extension KeyHandler {
// MARK: / (Full-Width / Half-Width Space)
/// 使
if state is InputState.Empty {
if state.type == .ofEmpty {
if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold {
stateCallback(InputState.Committing(textToCommit: input.isShiftHold ? " " : " "))
stateCallback(InputState.Empty())
stateCallback(IMEState.Committing(textToCommit: input.isShiftHold ? " " : " "))
stateCallback(IMEState.Empty())
return true
}
}
@ -371,14 +371,14 @@ extension KeyHandler {
if input.isShiftHold { // isOptionHold
switch mgrPrefs.upperCaseLetterKeyBehavior {
case 1:
stateCallback(InputState.Empty())
stateCallback(InputState.Committing(textToCommit: inputText.lowercased()))
stateCallback(InputState.Empty())
stateCallback(IMEState.Empty())
stateCallback(IMEState.Committing(textToCommit: inputText.lowercased()))
stateCallback(IMEState.Empty())
return true
case 2:
stateCallback(InputState.Empty())
stateCallback(InputState.Committing(textToCommit: inputText.uppercased()))
stateCallback(InputState.Empty())
stateCallback(IMEState.Empty())
stateCallback(IMEState.Committing(textToCommit: inputText.uppercased()))
stateCallback(IMEState.Empty())
return true
default: // case 0
let letter = "_letter_\(inputText)"
@ -401,7 +401,7 @@ extension KeyHandler {
///
/// F1-F12
/// 便
if (state is InputState.NotEmpty) || !composer.isEmpty {
if state.hasComposition || !composer.isEmpty {
IME.prtDebugIntel(
"Blocked data: charCode: \(input.charCode), keyCode: \(input.keyCode)")
IME.prtDebugIntel("A9BFF20E")

View File

@ -17,101 +17,73 @@ import Foundation
extension KeyHandler {
// MARK: - State Building
///
var buildInputtingState: InputState.Inputting {
///
var buildInputtingState: IMEState {
/// (Update the composing buffer)
/// NSAttributeString
var tooltipParameterRef: [String] = ["", ""]
let nodeValuesArray: [String] = compositor.walkedNodes.values.map {
var displayTextSegments: [String] = compositor.walkedNodes.values.map {
guard let delegate = delegate, delegate.isVerticalTyping else { return $0 }
guard mgrPrefs.hardenVerticalPunctuations else { return $0 }
var neta = $0
ChineseConverter.hardenVerticalPunctuations(target: &neta, convert: delegate.isVerticalTyping)
return neta
}
var cursor = convertCursorForDisplay(compositor.cursor)
let reading = composer.getInlineCompositionForDisplay(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer)
if !reading.isEmpty {
var newDisplayTextSegments = [String]()
var temporaryNode = ""
var charCounter = 0
for node in displayTextSegments {
for char in node {
if charCounter == cursor {
newDisplayTextSegments.append(temporaryNode)
temporaryNode = ""
newDisplayTextSegments.append(reading)
}
temporaryNode += String(char)
charCounter += 1
}
newDisplayTextSegments.append(temporaryNode)
temporaryNode = ""
}
if newDisplayTextSegments == displayTextSegments { newDisplayTextSegments.append(reading) }
displayTextSegments = newDisplayTextSegments
cursor += reading.count
}
/// 使
return IMEState.Inputting(displayTextSegments: displayTextSegments, cursor: cursor)
}
///
func convertCursorForDisplay(_ rawCursor: Int) -> Int {
var composedStringCursorIndex = 0
var readingCursorIndex = 0
/// IMK UTF8 emoji
/// Swift.utf16NSString.length()
///
for theNode in compositor.walkedNodes {
let strNodeValue = theNode.value
let arrSplit: [String] = Array(strNodeValue).charComponents
let codepointCount = arrSplit.count
///
/// NodeAnchorspanningLength
///
let spanningLength: Int = theNode.spanLength
if readingCursorIndex + spanningLength <= compositor.cursor {
composedStringCursorIndex += strNodeValue.utf16.count
let spanningLength: Int = theNode.keyArray.count
if readingCursorIndex + spanningLength <= rawCursor {
composedStringCursorIndex += strNodeValue.count
readingCursorIndex += spanningLength
continue
}
if codepointCount == spanningLength {
for i in 0..<codepointCount {
guard readingCursorIndex < compositor.cursor else { continue }
composedStringCursorIndex += arrSplit[i].utf16.count
if !theNode.isReadingMismatched {
for _ in 0..<strNodeValue.count {
guard readingCursorIndex < rawCursor else { continue }
composedStringCursorIndex += 1
readingCursorIndex += 1
}
continue
}
guard readingCursorIndex < compositor.cursor else { continue }
composedStringCursorIndex += strNodeValue.utf16.count
guard readingCursorIndex < rawCursor else { continue }
composedStringCursorIndex += strNodeValue.count
readingCursorIndex += spanningLength
readingCursorIndex = min(readingCursorIndex, compositor.cursor)
///
///
///
///
///
switch compositor.cursor {
case compositor.keys.count...:
// compositor.cursor readings.count Megrez
tooltipParameterRef[0] = compositor.keys[compositor.cursor - 1]
case 0:
tooltipParameterRef[1] = compositor.keys[compositor.cursor]
default:
tooltipParameterRef[0] = compositor.keys[compositor.cursor - 1]
tooltipParameterRef[1] = compositor.keys[compositor.cursor]
readingCursorIndex = min(readingCursorIndex, rawCursor)
}
}
isCursorCuttingChar = !tooltipParameterRef[0].isEmpty || !tooltipParameterRef[1].isEmpty
///
/// 便 composer
var arrHead = [String.UTF16View.Element]()
var arrTail = [String.UTF16View.Element]()
for (i, n) in nodeValuesArray.joined().utf16.enumerated() {
if i < composedStringCursorIndex {
arrHead.append(n)
} else {
arrTail.append(n)
}
}
/// stringview
///
let head = String(utf16CodeUnits: arrHead, count: arrHead.count)
let reading = composer.getInlineCompositionForDisplay(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer)
let tail = String(utf16CodeUnits: arrTail, count: arrTail.count)
let composedText = head + reading + tail
let cursorIndex = composedStringCursorIndex + reading.utf16.count
//
var cleanedComposition = ""
for theChar in composedText {
guard let charCode = theChar.utf16.first else { continue }
if !(theChar.isASCII && !(charCode.isPrintable)) {
cleanedComposition += String(theChar)
}
}
/// 使
return InputState.Inputting(
displayedText: cleanedComposition, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
return composedStringCursorIndex
}
// MARK: -
@ -122,14 +94,13 @@ extension KeyHandler {
/// - isTypingVertical:
/// - Returns:
func buildCandidate(
state currentState: InputState.NotEmpty,
state currentState: IMEStateProtocol,
isTypingVertical _: Bool = false
) -> InputState.ChoosingCandidate {
InputState.ChoosingCandidate(
displayedText: currentState.displayedText,
cursorIndex: currentState.cursorIndex,
) -> IMEState {
IMEState.Candidates(
candidates: getCandidatesArray(fixOrder: mgrPrefs.useFixecCandidateOrderOnSelection),
nodeValuesArray: compositor.walkedNodes.values
displayTextSegments: compositor.walkedNodes.values,
cursor: currentState.data.cursor
)
}
@ -149,9 +120,9 @@ extension KeyHandler {
/// - Returns:
func buildAssociatePhraseState(
withPair pair: Megrez.Compositor.KeyValuePaired
) -> InputState.Associates! {
) -> IMEState {
//  Xcode
InputState.Associates(
IMEState.Associates(
candidates: buildAssociatePhraseArray(withPair: pair))
}
@ -165,9 +136,9 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleMarkingState(
_ state: InputState.Marking,
_ state: IMEStateProtocol,
input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if input.isEsc {
@ -220,18 +191,19 @@ extension KeyHandler {
// Shift + Left
if input.isCursorBackward || input.emacsKey == EmacsKey.backward, input.isShiftHold {
var index = state.markerIndex
if index > 0 {
index = state.displayedText.utf16PreviousPosition(for: index)
let marking = InputState.Marking(
displayedText: state.displayedText,
cursorIndex: state.cursorIndex,
markerIndex: index,
readings: state.readings,
nodeValuesArray: compositor.walkedNodes.values
if compositor.marker > 0 {
compositor.marker -= 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .rear, isMarker: true)
}
var marking = IMEState.Marking(
displayTextSegments: state.data.displayTextSegments,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
marking.data.tooltipBackupForInputting = state.data.tooltipBackupForInputting
stateCallback(marking.data.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else {
IME.prtDebugIntel("1149908D")
errorCallback()
@ -242,18 +214,19 @@ extension KeyHandler {
// Shift + Right
if input.isCursorForward || input.emacsKey == EmacsKey.forward, input.isShiftHold {
var index = state.markerIndex
if index < (state.displayedText.utf16.count) {
index = state.displayedText.utf16NextPosition(for: index)
let marking = InputState.Marking(
displayedText: state.displayedText,
cursorIndex: state.cursorIndex,
markerIndex: index,
readings: state.readings,
nodeValuesArray: compositor.walkedNodes.values
if compositor.marker < compositor.width {
compositor.marker += 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .front, isMarker: true)
}
var marking = IMEState.Marking(
displayTextSegments: state.data.displayTextSegments,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
marking.data.tooltipBackupForInputting = state.data.tooltipBackupForInputting
stateCallback(marking.data.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else {
IME.prtDebugIntel("9B51408D")
errorCallback()
@ -276,9 +249,9 @@ extension KeyHandler {
/// - Returns: ctlInputMethod IMK
func handlePunctuation(
_ customPunctuation: String,
state: InputStateProtocol,
state: IMEStateProtocol,
usingVerticalTyping isTypingVertical: Bool,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !currentLM.hasUnigramsFor(key: customPunctuation) {
@ -308,8 +281,8 @@ extension KeyHandler {
if candidateState.candidates.count == 1 {
clear() // candidateState
if let candidateToCommit: (String, String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty {
stateCallback(InputState.Committing(textToCommit: candidateToCommit.1))
stateCallback(InputState.Empty())
stateCallback(IMEState.Committing(textToCommit: candidateToCommit.1))
stateCallback(IMEState.Empty())
} else {
stateCallback(candidateState)
}
@ -327,13 +300,13 @@ extension KeyHandler {
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleEnter(
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
stateCallback(InputState.Committing(textToCommit: currentState.displayedText))
stateCallback(InputState.Empty())
stateCallback(IMEState.Committing(textToCommit: state.displayedText))
stateCallback(IMEState.Empty())
return true
}
@ -345,10 +318,10 @@ extension KeyHandler {
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleCtrlCommandEnter(
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
var displayedText = compositor.keys.joined(separator: "-")
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
@ -360,8 +333,8 @@ extension KeyHandler {
displayedText = displayedText.replacingOccurrences(of: "-", with: " ")
}
stateCallback(InputState.Committing(textToCommit: displayedText))
stateCallback(InputState.Empty())
stateCallback(IMEState.Committing(textToCommit: displayedText))
stateCallback(IMEState.Empty())
return true
}
@ -373,10 +346,10 @@ extension KeyHandler {
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleCtrlOptionCommandEnter(
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
var composed = ""
@ -396,8 +369,8 @@ extension KeyHandler {
composed += key.contains("_") ? value : "<ruby>\(value)<rp>(</rp><rt>\(key)</rt><rp>)</rp></ruby>"
}
stateCallback(InputState.Committing(textToCommit: composed))
stateCallback(InputState.Empty())
stateCallback(IMEState.Committing(textToCommit: composed))
stateCallback(IMEState.Empty())
return true
}
@ -411,12 +384,12 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleBackSpace(
state: InputStateProtocol,
state: IMEStateProtocol,
input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
// macOS Shift+BackSpace
switch mgrPrefs.specifyShiftBackSpaceKeyBehavior {
@ -430,15 +403,13 @@ extension KeyHandler {
stateCallback(buildInputtingState)
return true
case 1:
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
return true
default: break
}
if input.isShiftHold, input.isOptionHold {
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
return true
}
@ -461,8 +432,7 @@ extension KeyHandler {
switch composer.isEmpty && compositor.isEmpty {
case false: stateCallback(buildInputtingState)
case true:
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
}
return true
}
@ -477,16 +447,15 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleDelete(
state: InputStateProtocol,
state: IMEStateProtocol,
input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
if input.isShiftHold {
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
return true
}
@ -509,8 +478,7 @@ extension KeyHandler {
switch inputting.displayedText.isEmpty {
case false: stateCallback(inputting)
case true:
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
}
return true
}
@ -524,11 +492,11 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleClockKey(
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
if !composer.isEmpty {
IME.prtDebugIntel("9B6F908D")
errorCallback()
@ -546,11 +514,11 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleHome(
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
if !composer.isEmpty {
IME.prtDebugIntel("ABC44080")
@ -580,11 +548,11 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleEnd(
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
if !composer.isEmpty {
IME.prtDebugIntel("9B69908D")
@ -613,16 +581,15 @@ extension KeyHandler {
/// - stateCallback:
/// - Returns: ctlInputMethod IMK
func handleEsc(
state: InputStateProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void
state: IMEStateProtocol,
stateCallback: @escaping (IMEStateProtocol) -> Void
) -> Bool {
guard state is InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
if mgrPrefs.escToCleanInputBuffer {
///
/// macOS Windows 使
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
} else {
if composer.isEmpty { return true }
///
@ -630,8 +597,7 @@ extension KeyHandler {
switch compositor.isEmpty {
case false: stateCallback(buildInputtingState)
case true:
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
stateCallback(IMEState.Abortion())
}
}
return true
@ -647,12 +613,12 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleForward(
state: InputStateProtocol,
state: IMEStateProtocol,
input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
if !composer.isEmpty {
IME.prtDebugIntel("B3BA5257")
@ -663,16 +629,18 @@ extension KeyHandler {
if input.isShiftHold {
// Shift + Right
if currentState.cursorIndex < currentState.displayedText.utf16.count {
let nextPosition = currentState.displayedText.utf16NextPosition(
for: currentState.cursorIndex)
let marking: InputState.Marking! = InputState.Marking(
displayedText: currentState.displayedText,
cursorIndex: currentState.cursorIndex,
markerIndex: nextPosition,
readings: compositor.keys
if compositor.cursor < compositor.width {
compositor.marker = compositor.cursor + 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .front, isMarker: true)
}
var marking = IMEState.Marking(
displayTextSegments: compositor.walkedNodes.values,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = currentState.tooltip
marking.data.tooltipBackupForInputting = state.tooltip
stateCallback(marking)
} else {
IME.prtDebugIntel("BB7F6DB9")
@ -680,7 +648,6 @@ extension KeyHandler {
stateCallback(state)
}
} else if input.isOptionHold {
isCursorCuttingChar = false
if input.isControlHold {
return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
}
@ -695,12 +662,10 @@ extension KeyHandler {
} else {
if compositor.cursor < compositor.length {
compositor.cursor += 1
var inputtingState = buildInputtingState
if isCursorCuttingChar == true {
if isCursorCuttingChar() {
compositor.jumpCursorBySpan(to: .front)
inputtingState = buildInputtingState
}
stateCallback(inputtingState)
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("A96AAD58")
errorCallback()
@ -721,12 +686,12 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleBackward(
state: InputStateProtocol,
state: IMEStateProtocol,
input: InputSignalProtocol,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false }
guard state.type == .ofInputting else { return false }
if !composer.isEmpty {
IME.prtDebugIntel("6ED95318")
@ -737,16 +702,18 @@ extension KeyHandler {
if input.isShiftHold {
// Shift + left
if currentState.cursorIndex > 0 {
let previousPosition = currentState.displayedText.utf16PreviousPosition(
for: currentState.cursorIndex)
let marking: InputState.Marking! = InputState.Marking(
displayedText: currentState.displayedText,
cursorIndex: currentState.cursorIndex,
markerIndex: previousPosition,
readings: compositor.keys
if compositor.cursor > 0 {
compositor.marker = compositor.cursor - 1
if isCursorCuttingChar(isMarker: true) {
compositor.jumpCursorBySpan(to: .rear, isMarker: true)
}
var marking = IMEState.Marking(
displayTextSegments: compositor.walkedNodes.values,
markedReadings: Array(compositor.keys[currentMarkedRange()]),
cursor: convertCursorForDisplay(compositor.cursor),
marker: convertCursorForDisplay(compositor.marker)
)
marking.tooltipBackupForInputting = currentState.tooltip
marking.data.tooltipBackupForInputting = state.tooltip
stateCallback(marking)
} else {
IME.prtDebugIntel("D326DEA3")
@ -754,7 +721,6 @@ extension KeyHandler {
stateCallback(state)
}
} else if input.isOptionHold {
isCursorCuttingChar = false
if input.isControlHold {
return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
}
@ -769,12 +735,10 @@ extension KeyHandler {
} else {
if compositor.cursor > 0 {
compositor.cursor -= 1
var inputtingState = buildInputtingState
if isCursorCuttingChar == true {
if isCursorCuttingChar() {
compositor.jumpCursorBySpan(to: .rear)
inputtingState = buildInputtingState
}
stateCallback(inputtingState)
stateCallback(buildInputtingState)
} else {
IME.prtDebugIntel("7045E6F3")
errorCallback()
@ -795,14 +759,14 @@ extension KeyHandler {
/// - errorCallback:
/// - Returns: ctlInputMethod IMK
func handleInlineCandidateRotation(
state: InputStateProtocol,
state: IMEStateProtocol,
reverseModifier: Bool,
stateCallback: @escaping (InputStateProtocol) -> Void,
stateCallback: @escaping (IMEStateProtocol) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if composer.isEmpty, compositor.isEmpty || compositor.walkedNodes.isEmpty { return false }
guard state is InputState.Inputting else {
guard state is InputState.Empty else {
guard state.type == .ofInputting else {
guard state.type == .ofEmpty else {
IME.prtDebugIntel("6044F081")
errorCallback()
return true

View File

@ -31,9 +31,9 @@ extension ctlInputMethod {
if !shouldUseHandle || (!rencentKeyHandledByKeyHandler && shouldUseHandle) {
NotifierController.notify(
message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n"
+ toggleASCIIMode()
+ (toggleASCIIMode()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
if shouldUseHandle {

View File

@ -32,16 +32,20 @@ class ctlInputMethod: IMKInputController {
// MARK: -
/// 調
var keyHandler: KeyHandler = .init()
///
var state: InputStateProtocol = InputState.Empty()
/// ctlInputMethod
var isASCIIMode: Bool = false
/// ctlInputMethod
static var isASCIIModeSituation: Bool = false
/// ctlInputMethod
static var isVerticalTypingSituation: Bool = false
/// ctlInputMethod
var isASCIIMode: Bool = false
/// 調
var keyHandler: KeyHandler = .init()
///
var state: IMEStateProtocol = IMEState.Empty() {
didSet {
IME.prtDebugIntel("Current State: \(state.type.rawValue)")
}
}
/// ctlInputMethod
func toggleASCIIMode() -> Bool {
@ -65,15 +69,15 @@ class ctlInputMethod: IMKInputController {
/// 調
func resetKeyHandler() {
//
if state is InputState.Inputting, mgrPrefs.trimUnfinishedReadingsOnCommit {
if state.type == .ofInputting, mgrPrefs.trimUnfinishedReadingsOnCommit {
keyHandler.composer.clear()
handle(state: keyHandler.buildInputtingState)
}
if let state = state as? InputState.NotEmpty {
if state.hasComposition {
/// 調
handle(state: InputState.Committing(textToCommit: state.displayedTextConverted))
handle(state: IMEState.Committing(textToCommit: state.displayedText))
}
handle(state: InputState.Empty())
handle(state: IMEState.Empty())
}
// MARK: - IMKInputController
@ -115,9 +119,9 @@ class ctlInputMethod: IMKInputController {
} else {
NotifierController.notify(
message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n"
+ isASCIIMode
+ (isASCIIMode
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
}
@ -127,7 +131,7 @@ class ctlInputMethod: IMKInputController {
if let client = client(), client.bundleIdentifier() != Bundle.main.bundleIdentifier {
// 使
setKeyLayout()
handle(state: InputState.Empty())
handle(state: IMEState.Empty())
} //
(NSApp.delegate as? AppDelegate)?.checkForUpdate()
}
@ -137,7 +141,7 @@ class ctlInputMethod: IMKInputController {
override func deactivateServer(_ sender: Any!) {
_ = sender //
resetKeyHandler() // Empty
handle(state: InputState.Deactivated())
handle(state: IMEState.Deactivated())
}
///
@ -168,7 +172,7 @@ class ctlInputMethod: IMKInputController {
if let client = client(), client.bundleIdentifier() != Bundle.main.bundleIdentifier {
// 使
setKeyLayout()
handle(state: InputState.Empty())
handle(state: IMEState.Empty())
} //
}
@ -286,8 +290,8 @@ class ctlInputMethod: IMKInputController {
/// - Returns: nil
override func composedString(_ sender: Any!) -> Any! {
_ = sender //
guard let state = state as? InputState.NotEmpty else { return "" }
return state.committingBufferConverted
guard state.hasComposition else { return "" }
return state.displayedText
}
///
@ -307,7 +311,7 @@ class ctlInputMethod: IMKInputController {
_ = sender //
var arrResult = [String]()
// 便 InputState
// 便 IMEState
func handleCandidatesPrepared(_ candidates: [(String, String)], prefix: String = "") {
for theCandidate in candidates {
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
@ -323,12 +327,12 @@ class ctlInputMethod: IMKInputController {
}
}
if let state = state as? InputState.Associates {
if state.type == .ofAssociates {
handleCandidatesPrepared(state.candidates, prefix: "")
} else if let state = state as? InputState.SymbolTable {
} else if state.type == .ofSymbolTable {
// / JIS 使
arrResult = state.candidates.map(\.1)
} else if let state = state as? InputState.ChoosingCandidate {
} else if state.type == .ofCandidates {
guard !state.candidates.isEmpty else { return .init() }
if state.candidates[0].0.contains("_punctuation") {
arrResult = state.candidates.map(\.1) //
@ -361,17 +365,16 @@ class ctlInputMethod: IMKInputController {
/// - Parameter candidateString:
override open func candidateSelected(_ candidateString: NSAttributedString!) {
let candidateString: NSAttributedString = candidateString ?? .init(string: "")
if state is InputState.Associates {
if state.type == .ofAssociates {
if !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
handle(state: InputState.Abortion())
handle(state: InputState.Empty())
handle(state: IMEState.Abortion())
return
}
}
var indexDeducted = 0
// 便 InputState
// 便 IMEState
func handleCandidatesSelected(_ candidates: [(String, String)], prefix: String = "") {
for (i, neta) in candidates.enumerated() {
let theConverted = IME.kanjiConversionIfRequired(neta.1)
@ -401,11 +404,11 @@ class ctlInputMethod: IMKInputController {
}
}
if let state = state as? InputState.Associates {
if state.type == .ofAssociates {
handleCandidatesSelected(state.candidates, prefix: "")
} else if let state = state as? InputState.SymbolTable {
} else if state.type == .ofSymbolTable {
handleSymbolCandidatesSelected(state.candidates)
} else if let state = state as? InputState.ChoosingCandidate {
} else if state.type == .ofCandidates {
guard !state.candidates.isEmpty else { return }
if state.candidates[0].0.contains("_punctuation") {
handleSymbolCandidatesSelected(state.candidates) //

View File

@ -37,21 +37,20 @@ extension ctlInputMethod: KeyHandlerDelegate {
ctlCandidate(controller, didSelectCandidateAtIndex: index)
}
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol, addToFilter: Bool)
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: IMEStateProtocol, addToFilter: Bool)
-> Bool
{
guard let state = state as? InputState.Marking else { return false }
if state.bufferReadingCountMisMatch { return false }
guard state.type == .ofMarking else { return false }
let refInputModeReversed: InputMode =
(keyHandler.inputMode == InputMode.imeModeCHT)
? InputMode.imeModeCHS : InputMode.imeModeCHT
if !mgrLangModel.writeUserPhrase(
state.userPhrase, inputMode: keyHandler.inputMode,
areWeDuplicating: state.chkIfUserPhraseExists,
state.data.userPhrase, inputMode: keyHandler.inputMode,
areWeDuplicating: state.data.chkIfUserPhraseExists,
areWeDeleting: addToFilter
)
|| !mgrLangModel.writeUserPhrase(
state.userPhraseConverted, inputMode: refInputModeReversed,
state.data.userPhraseConverted, inputMode: refInputModeReversed,
areWeDuplicating: false,
areWeDeleting: addToFilter
)
@ -65,7 +64,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
// MARK: - Candidate Controller Delegate
extension ctlInputMethod: ctlCandidateDelegate {
var isAssociatedPhrasesState: Bool { state is InputState.Associates }
var isAssociatedPhrasesState: Bool { state.type == .ofAssociates }
/// handle() IMK
/// handle()
@ -78,9 +77,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int {
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates.count
} else if let state = state as? InputState.Associates {
if state.isCandidateContainer {
return state.candidates.count
}
return 0
@ -91,9 +88,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
/// - Returns:
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] {
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates
} else if let state = state as? InputState.Associates {
if state.isCandidateContainer {
return state.candidates
}
return .init()
@ -103,9 +98,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
-> (String, String)
{
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates[index]
} else if let state = state as? InputState.Associates {
if state.isCandidateContainer {
return state.candidates[index]
}
return ("", "")
@ -114,20 +107,20 @@ extension ctlInputMethod: ctlCandidateDelegate {
func ctlCandidate(_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int) {
_ = controller //
if let state = state as? InputState.SymbolTable,
if state.type == .ofSymbolTable,
let node = state.node.children?[index]
{
if let children = node.children, !children.isEmpty {
handle(state: InputState.Empty()) //
handle(state: InputState.SymbolTable(node: node, previous: state.node))
handle(state: IMEState.Empty()) //
handle(state: IMEState.SymbolTable(node: node))
} else {
handle(state: InputState.Committing(textToCommit: node.title))
handle(state: InputState.Empty())
handle(state: IMEState.Committing(textToCommit: node.title))
handle(state: IMEState.Empty())
}
return
}
if let state = state as? InputState.ChoosingCandidate {
if [.ofCandidates, .ofSymbolTable].contains(state.type) {
let selectedValue = state.candidates[index]
keyHandler.fixNode(
candidate: selectedValue, respectCursorPushing: true,
@ -137,16 +130,15 @@ extension ctlInputMethod: ctlCandidateDelegate {
let inputting = keyHandler.buildInputtingState
if mgrPrefs.useSCPCTypingMode {
handle(state: InputState.Committing(textToCommit: inputting.displayedTextConverted))
handle(state: IMEState.Committing(textToCommit: inputting.displayedText))
// selectedValue.1
if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState(
if mgrPrefs.associatedPhrasesEnabled {
let associates = keyHandler.buildAssociatePhraseState(
withPair: .init(key: selectedValue.0, value: selectedValue.1)
), !associatePhrases.candidates.isEmpty
{
handle(state: associatePhrases)
)
handle(state: associates.candidates.isEmpty ? IMEState.Empty() : associates)
} else {
handle(state: InputState.Empty())
handle(state: IMEState.Empty())
}
} else {
handle(state: inputting)
@ -154,24 +146,25 @@ extension ctlInputMethod: ctlCandidateDelegate {
return
}
if let state = state as? InputState.Associates {
if state.type == .ofAssociates {
let selectedValue = state.candidates[index]
handle(state: InputState.Committing(textToCommit: selectedValue.1))
handle(state: IMEState.Committing(textToCommit: selectedValue.1))
// selectedValue.1
//
guard let valueKept = selectedValue.1.last else {
handle(state: InputState.Empty())
handle(state: IMEState.Empty())
return
}
if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState(
if mgrPrefs.associatedPhrasesEnabled {
let associates = keyHandler.buildAssociatePhraseState(
withPair: .init(key: selectedValue.0, value: String(valueKept))
), !associatePhrases.candidates.isEmpty
{
handle(state: associatePhrases)
)
if !associates.candidates.isEmpty {
handle(state: associates)
return
}
handle(state: InputState.Empty())
}
handle(state: IMEState.Empty())
}
}
}

View File

@ -13,10 +13,10 @@ import Cocoa
// MARK: - Tooltip Display and Candidate Display Methods
extension ctlInputMethod {
func show(tooltip: String, displayedText: String, cursorIndex: Int) {
func show(tooltip: String, displayedText: String, u16Cursor: Int) {
guard let client = client() else { return }
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
var cursor = cursorIndex
var cursor = u16Cursor
if cursor == displayedText.count, cursor != 0 {
cursor -= 1
}
@ -38,24 +38,14 @@ extension ctlInputMethod {
ctlInputMethod.tooltipController.show(tooltip: tooltip, at: finalOrigin)
}
func show(candidateWindowWith state: InputStateProtocol) {
func show(candidateWindowWith state: IMEStateProtocol) {
guard let client = client() else { return }
var isTypingVertical: Bool {
if state.type == .ofCandidates {
return ctlInputMethod.isVerticalTypingSituation
} else if state.type == ..ofAssociates {
return ctlInputMethod.isVerticalTypingSituation
}
return false
}
var isCandidateWindowVertical: Bool {
var candidates: [(String, String)] = .init()
if let state = state as? InputState.ChoosingCandidate {
candidates = state.candidates
} else if let state = state as? InputState.Associates {
if state.isCandidateContainer {
candidates = state.candidates
}
if isTypingVertical { return true }
if isVerticalTyping { return true }
// IMK
guard ctlInputMethod.ctlCandidateCurrent is ctlCandidateUniversal else { return false }
// 使
@ -106,7 +96,7 @@ extension ctlInputMethod {
let candidateKeys = mgrPrefs.candidateKeys
let keyLabels =
candidateKeys.count > 4 ? Array(candidateKeys) : Array(mgrPrefs.defaultCandidateKeys)
let keyLabelSuffix = state is InputState.Associates ? "^" : ""
let keyLabelSuffix = state.type == .ofAssociates ? "^" : ""
ctlInputMethod.ctlCandidateCurrent.keyLabels = keyLabels.map {
CandidateKeyLabel(key: String($0), displayedText: String($0) + keyLabelSuffix)
}
@ -126,8 +116,8 @@ extension ctlInputMethod {
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
var cursor = 0
if let state = state as? InputState.ChoosingCandidate {
cursor = state.cursorIndex
if [.ofCandidates, .ofSymbolTable].contains(state.type) {
cursor = state.data.cursor
if cursor == state.displayedText.count, cursor != 0 {
cursor -= 1
}
@ -140,7 +130,7 @@ extension ctlInputMethod {
cursor -= 1
}
if isTypingVertical {
if isVerticalTyping {
ctlInputMethod.ctlCandidateCurrent.set(
windowTopLeftPoint: NSPoint(
x: lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, y: lineHeightRect.origin.y - 4.0

View File

@ -18,29 +18,77 @@ extension ctlInputMethod {
///
///
/// - Parameter newState:
func handle(state newState: InputStateProtocol) {
let prevState = state
func handle(state newState: IMEStateProtocol) {
let previous = state
state = newState
switch newState {
case let newState as InputState.Deactivated:
handle(state: newState, previous: prevState)
case let newState as InputState.Empty:
handle(state: newState, previous: prevState)
case let newState as InputState.Abortion:
handle(state: newState, previous: prevState)
case let newState as InputState.Committing:
handle(state: newState, previous: prevState)
case let newState as InputState.Inputting:
handle(state: newState, previous: prevState)
case let newState as InputState.Marking:
handle(state: newState, previous: prevState)
case let newState as InputState.ChoosingCandidate:
handle(state: newState, previous: prevState)
case let newState as InputState.Associates:
handle(state: newState, previous: prevState)
case let newState as InputState.SymbolTable:
handle(state: newState, previous: prevState)
switch state.type {
case .ofDeactivated:
ctlInputMethod.ctlCandidateCurrent.delegate = nil
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
if previous.hasComposition {
commit(text: previous.displayedText)
}
clearInlineDisplay()
//
keyHandler.clear()
case .ofEmpty, .ofAbortion:
var previous = previous
if state.type == .ofAbortion {
state = IMEState.Empty()
previous = state
}
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
// .Abortion
if previous.hasComposition, state.type != .ofAbortion {
commit(text: previous.displayedText)
}
//
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
clearInlineDisplay()
//
keyHandler.clear()
case .ofCommitting:
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty { commit(text: textToCommit) }
clearInlineDisplay()
//
keyHandler.clear()
case .ofInputting:
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty { commit(text: textToCommit) }
setInlineDisplayWithCursor()
if !state.tooltip.isEmpty {
show(
tooltip: state.tooltip, displayedText: state.displayedText,
u16Cursor: state.data.u16Cursor
)
}
case .ofMarking:
ctlInputMethod.ctlCandidateCurrent.visible = false
setInlineDisplayWithCursor()
if state.tooltip.isEmpty {
ctlInputMethod.tooltipController.hide()
} else {
let cursorReference: Int = {
if state.data.marker >= state.data.cursor { return state.data.u16Cursor }
return state.data.u16Marker //
}()
show(
tooltip: state.tooltip, displayedText: state.displayedText,
u16Cursor: cursorReference
)
}
case .ofCandidates, .ofAssociates, .ofSymbolTable:
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
default: break
}
}
@ -48,7 +96,7 @@ extension ctlInputMethod {
/// .NotEmpty()
func setInlineDisplayWithCursor() {
guard let client = client() else { return }
if let state = state as? InputState.Associates {
if state.type == .ofAssociates {
client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
@ -56,49 +104,19 @@ extension ctlInputMethod {
return
}
guard let state = state as? InputState.NotEmpty else {
clearInlineDisplay()
return
}
var identifier: AnyObject {
switch IME.currentInputMode {
case InputMode.imeModeCHS:
if #available(macOS 12.0, *) {
return "zh-Hans" as AnyObject
}
case InputMode.imeModeCHT:
if #available(macOS 12.0, *) {
return (mgrPrefs.shiftJISShinjitaiOutputEnabled || mgrPrefs.chineseConversionEnabled)
? "ja" as AnyObject : "zh-Hant" as AnyObject
}
default:
break
}
return "" as AnyObject
}
// [Shiki's Note] This might needs to be bug-reported to Apple:
// The LanguageIdentifier attribute of an NSAttributeString designated to
// IMK Client().SetMarkedText won't let the actual font respect your languageIdentifier
// settings. Still, this might behaves as Apple's current expectation, I'm afraid.
if #available(macOS 12.0, *) {
state.attributedString.setAttributes(
[.languageIdentifier: identifier],
range: NSRange(
location: 0,
length: state.displayedText.utf16.count
)
)
}
if state.hasComposition || state.isCandidateContainer {
/// selectionRange
/// 0 replacementRangeNSNotFound
///
client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
state.attributedString, selectionRange: NSRange(location: state.data.u16Cursor, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
return
}
//
clearInlineDisplay()
}
/// .NotEmpty()
@ -123,109 +141,4 @@ extension ctlInputMethod {
buffer, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
}
private func handle(state: InputState.Deactivated, previous: InputStateProtocol) {
_ = state //
ctlInputMethod.ctlCandidateCurrent.delegate = nil
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
if let previous = previous as? InputState.NotEmpty {
commit(text: previous.committingBufferConverted)
}
clearInlineDisplay()
//
keyHandler.clear()
}
private func handle(state: InputState.Empty, previous: InputStateProtocol) {
_ = state //
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
// .Abortion
if let previous = previous as? InputState.NotEmpty,
!(state is InputState.Abortion)
{
commit(text: previous.committingBufferConverted)
}
//
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
clearInlineDisplay()
//
keyHandler.clear()
}
private func handle(
state: InputState.Abortion, previous: InputStateProtocol
) {
_ = state //
_ = previous //
// previous state 使西 commit
handle(state: InputState.Empty())
}
private func handle(state: InputState.Committing, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty {
commit(text: textToCommit)
}
clearInlineDisplay()
//
keyHandler.clear()
}
private func handle(state: InputState.Inputting, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
let textToCommit = state.textToCommit
if !textToCommit.isEmpty {
commit(text: textToCommit)
}
setInlineDisplayWithCursor()
if !state.tooltip.isEmpty {
show(
tooltip: state.tooltip, displayedText: state.displayedText,
cursorIndex: state.cursorIndex
)
}
}
private func handle(state: InputState.Marking, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.ctlCandidateCurrent.visible = false
setInlineDisplayWithCursor()
if state.tooltip.isEmpty {
ctlInputMethod.tooltipController.hide()
} else {
show(
tooltip: state.tooltip, displayedText: state.displayedText,
cursorIndex: state.markerIndex
)
}
}
private func handle(state: InputState.ChoosingCandidate, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
private func handle(state: InputState.SymbolTable, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
private func handle(state: InputState.Associates, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()
show(candidateWindowWith: state)
}
}

View File

@ -212,9 +212,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Per-Char Select Mode", comment: "") + "\n"
+ mgrPrefs.toggleSCPCTypingModeEnabled()
+ (mgrPrefs.toggleSCPCTypingModeEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -222,9 +222,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Force KangXi Writing", comment: "") + "\n"
+ mgrPrefs.toggleChineseConversionEnabled()
+ (mgrPrefs.toggleChineseConversionEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -232,9 +232,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("JIS Shinjitai Output", comment: "") + "\n"
+ mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
+ (mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -242,9 +242,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Currency Numeral Output", comment: "") + "\n"
+ mgrPrefs.toggleCurrencyNumeralsEnabled()
+ (mgrPrefs.toggleCurrencyNumeralsEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -252,9 +252,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Half-Width Punctuation Mode", comment: "") + "\n"
+ mgrPrefs.toggleHalfWidthPunctuationEnabled()
+ (mgrPrefs.toggleHalfWidthPunctuationEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -262,9 +262,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("CNS11643 Mode", comment: "") + "\n"
+ mgrPrefs.toggleCNS11643Enabled()
+ (mgrPrefs.toggleCNS11643Enabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -272,9 +272,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Symbol & Emoji Input", comment: "") + "\n"
+ mgrPrefs.toggleSymbolInputEnabled()
+ (mgrPrefs.toggleSymbolInputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -282,9 +282,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Per-Char Associated Phrases", comment: "") + "\n"
+ mgrPrefs.toggleAssociatedPhrasesEnabled()
+ (mgrPrefs.toggleAssociatedPhrasesEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}
@ -292,9 +292,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Use Phrase Replacement", comment: "") + "\n"
+ mgrPrefs.togglePhraseReplacementEnabled()
+ (mgrPrefs.togglePhraseReplacementEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
)
}

View File

@ -18,17 +18,26 @@ public class SymbolNode {
init(_ title: String, _ children: [SymbolNode]? = nil, previous: SymbolNode? = nil) {
self.title = title
self.children = children
self.children?.forEach {
$0.previous = self
}
self.previous = previous
}
init(_ title: String, symbols: String) {
self.title = title
children = Array(symbols).map { SymbolNode(String($0), nil) }
children?.forEach {
$0.previous = self
}
}
init(_ title: String, symbols: [String]) {
self.title = title
children = symbols.map { SymbolNode($0, nil) }
children?.forEach {
$0.previous = self
}
}
static func parseUserSymbolNodeData() {

View File

@ -136,7 +136,6 @@
6ACA41FD15FC1D9000935EF6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41F015FC1D9000935EF6 /* MainMenu.xib */; };
6ACA420215FC1E5200935EF6 /* vChewing.app in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */; };
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; };
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */; };
D47F7DD0278C0897002F9DD7 /* ctlNonModalAlertWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */; };
@ -370,7 +369,6 @@
6ACA41EF15FC1D9000935EF6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
6ACA41F215FC1D9000935EF6 /* Installer-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Installer-Info.plist"; path = "Installer/Installer-Info.plist"; sourceTree = SOURCE_ROOT; };
D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppDelegate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputState.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = main.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
D47F7DCF278C0897002F9DD7 /* ctlNonModalAlertWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlNonModalAlertWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
@ -504,7 +502,6 @@
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */,
5BF56F9728C39A2700DD6839 /* IMEState.swift */,
5BF56F9928C39D1800DD6839 /* IMEStateData.swift */,
D461B791279DAC010070E734 /* InputState.swift */,
5BD0113C2818543900609769 /* KeyHandler_Core.swift */,
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */,
@ -1211,7 +1208,6 @@
5BA9FD4127FEF3C8002DE248 /* PreferencesStyle.swift in Sources */,
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */,
5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */,
D461B792279DAC010070E734 /* InputState.swift in Sources */,
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */,
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */,
D47B92C027972AD100458394 /* main.swift in Sources */,