Repo // Refactoring InputFSM - preparation.

This commit is contained in:
ShikiSuen 2022-09-03 20:29:45 +08:00
parent 205e07e03a
commit 97062ab731
16 changed files with 743 additions and 378 deletions

View File

@ -0,0 +1,182 @@
// (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
// 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
}
// InputState
public protocol InputStateProtocol {
var type: StateType { get }
var data: StateData { get }
var hasBuffer: 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 node: SymbolNode { get set }
}
public struct IMEState {
public var type: StateType = .ofEmpty
public var data: StateData = .init()
init(_ data: StateData = .init(), type: StateType = .ofEmpty) {
self.data = data
self.type = type
}
}
// MARK: -
extension IMEState {
public static func Deactivated() -> IMEState { .init(type: .ofDeactivated) }
public static func Empty() -> IMEState { .init(type: .ofEmpty) }
public static func Abortion() -> IMEState { .init(type: .ofAbortion) }
public static func Committing(textToCommit: String) -> IMEState {
var result = IMEState(type: .ofCommitting)
result.data.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &result.data.textToCommit)
return result
}
public static func Associates(candidates: [(String, String)]) -> IMEState {
var result = IMEState(type: .ofAssociates)
result.data.candidates = candidates
return result
}
public static func NotEmpty(nodeValues: [String], reading: 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()
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)
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)
result.type = .ofMarking
result.data.nodeReadingsArray = nodeReadings
result.data.marker = marker
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)
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)
result.type = .ofSymbolTable
result.data.node = node
if let previous = previous {
result.data.node.previous = previous
}
return result
}
}
// MARK: -
extension IMEState: InputStateProtocol {
public var convertedToInputting: IMEState {
if type == .ofInputting { return self }
var result = IMEState.Inputting(nodeValues: data.nodeValuesArray, reading: data.reading, cursor: data.cursor)
result.tooltip = data.tooltipBackupForInputting
return result
}
public var textToCommit: String {
get {
data.textToCommit
}
set {
data.textToCommit = newValue
}
}
public var tooltip: String {
get {
data.tooltip
}
set {
data.tooltip = newValue
}
}
public var attributedString: NSAttributedString {
switch type {
case .ofMarking: return data.attributedStringMarking
case .ofAssociates, .ofSymbolTable: return data.attributedStringPlaceholder
default: return data.attributedStringNormal
}
}
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 {
switch type {
case .ofNotEmpty, .ofInputting, .ofMarking, .ofCandidates: return true
default: return false
}
}
public var isCandidateContainer: Bool {
switch type {
case .ofCandidates, .ofAssociates, .ofSymbolTable: return true
default: return false
}
}
public var displayedText: String { data.displayedText }
}

View File

@ -0,0 +1,268 @@
// (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
public struct StateData {
var displayedText: String = "" {
didSet {
let result = IME.kanjiConversionIfRequired(displayedText)
if result.utf16.count == displayedText.utf16.count, result.count == displayedText.count {
displayedText = result
}
}
}
// MARK: Cursor & Marker & Range for UTF8
var cursor: Int = 0 {
didSet {
cursor = min(max(cursor, 0), displayedText.count)
}
}
var marker: Int = 0 {
didSet {
marker = min(max(marker, 0), displayedText.count)
}
}
var markedRange: Range<Int> {
min(cursor, marker)..<max(cursor, marker)
}
// MARK: Cursor & Marker & Range for UTF16 (Read-Only)
var u16Cursor: Int {
displayedText.charComponents[0..<cursor].joined().utf16.count
}
var u16Marker: Int {
displayedText.charComponents[0..<marker].joined().utf16.count
}
var u16MarkedRange: Range<Int> {
min(u16Cursor, u16Marker)..<max(u16Cursor, u16Marker)
}
// MARK: Other data for non-empty states.
var markedTargetExists: Bool = false
var nodeReadingsArray = [String]()
var nodeValuesArray = [String]()
var reading: 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
}
}
}
var candidates = [(String, String)]()
var textToCommit: String = ""
var tooltip: String = ""
var tooltipBackupForInputting: String = ""
var attributedStringPlaceholder: NSAttributedString = .init(
string: " ",
attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
]
)
var isFilterable: Bool {
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() {
attributedString.setAttributes(
[
/// .thick
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: i,
], range: NSRange(location: newBegin, length: neta.utf16.count)
)
newBegin += neta.utf16.count
}
return attributedString
}
var attributedStringMarking: NSAttributedString {
///
/// JIS
let attributedString = NSMutableAttributedString(string: displayedText)
let end = u16MarkedRange.upperBound
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
], range: NSRange(location: 0, length: u16MarkedRange.lowerBound)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 1,
],
range: NSRange(
location: u16MarkedRange.lowerBound,
length: u16MarkedRange.upperBound - u16MarkedRange.lowerBound
)
)
attributedString.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 2,
],
range: NSRange(
location: end,
length: displayedText.utf16.count - end
)
)
return attributedString
}
var node: SymbolNode = .init("")
}
// MARK: - InputState
extension StateData {
var chkIfUserPhraseExists: Bool {
let text = displayedText.charComponents[markedRange].joined()
let selectedReadings = nodeReadingsArray[markedRange]
let joined = selectedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
}
var userPhrase: String {
let text = displayedText.charComponents[markedRange].joined()
let selectedReadings = nodeReadingsArray[markedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
return "\(text) \(joined)\(nerfedScore)"
}
var userPhraseConverted: String {
let text =
ChineseConverter.crossConvert(displayedText.charComponents[markedRange].joined()) ?? ""
let selectedReadings = nodeReadingsArray[markedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
return "\(text) \(joined)\(nerfedScore)\t\(convertedMark)"
}
enum Marking {
private static func generateReadingThread(_ data: StateData) -> String {
var arrOutput = [String]()
for neta in data.nodeReadingsArray[data.markedRange] {
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: " ")
}
///
/// - 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(
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry.", comment: ""
)
}
if data.markedRange.isEmpty {
return ""
}
let text = data.displayedText.charComponents[data.markedRange].joined()
if data.markedRange.count < mgrPrefs.allowedMarkRange.lowerBound {
ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency)
return String(
format: NSLocalizedString(
"\"%@\" length must ≥ 2 for a user phrase.", comment: ""
) + "\n// " + generateReadingThread(data), text
)
} else if data.markedRange.count > mgrPrefs.allowedMarkRange.upperBound {
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
return String(
format: NSLocalizedString(
"\"%@\" length should ≤ %d for a user phrase.", comment: ""
) + "\n// " + generateReadingThread(data), text, mgrPrefs.allowedMarkRange.upperBound
)
}
let selectedReadings = data.nodeReadingsArray[data.markedRange]
let joined = selectedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: IME.currentInputMode, key: joined
)
if exist {
data.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// " + generateReadingThread(data), text
)
}
ctlInputMethod.tooltipController.resetColor()
return String(
format: NSLocalizedString("\"%@\" selected. ENTER to add user phrase.", comment: "") + "\n// "
+ generateReadingThread(data),
text
)
}
data.tooltip = tooltipGenerated
}
}
}

View File

@ -12,27 +12,6 @@ import Foundation
// InputState 使 Struct Struct
// enum
public enum StateType {
case ofDeactivated
case ofAssociatedPhrases
case ofEmpty
case ofEmptyIgnoringPreviousState
case ofCommitting
case ofNotEmpty
case ofInputting
case ofMarking
case ofChoosingCandidate
case ofSymbolTable
}
// InputState
public protocol InputStateProtocol {
var type: StateType { get }
var hasBuffer: Bool { get }
var isCandidateContainer: Bool { get }
}
/// ctlInputMethod
///
/// Finite State Machine/
@ -56,7 +35,7 @@ public protocol InputStateProtocol {
/// 西 .NotEmpty
/// - .Empty: 使
///
/// - .EmptyIgnoringPreviousState: Empty
/// - .Abortion: Empty
/// .Empty()
/// - .Committing:
/// - .NotEmpty:
@ -68,6 +47,12 @@ public protocol InputStateProtocol {
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 }
@ -78,32 +63,41 @@ public enum InputState {
/// .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 composingBuffer: String = ""
let displayedText: String = ""
}
// MARK: -
/// .EmptyIgnoringPreviousState: Empty
/// .Abortion: Empty
///
/// .Empty()
class EmptyIgnoringPreviousState: Empty {
override public var type: StateType { .ofEmptyIgnoringPreviousState }
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 }
private(set) var textToCommit: String = ""
convenience init(textToCommit: String) {
self.init()
init(textToCommit: String) {
self.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &self.textToCommit)
}
@ -113,18 +107,20 @@ public enum InputState {
/// .AssociatedPhrases:
/// 西 .NotEmpty
class AssociatedPhrases: InputStateProtocol {
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 { .ofAssociatedPhrases }
private(set) var candidates: [(String, String)] = []
private(set) var isTypingVertical: Bool = false
init(candidates: [(String, String)], isTypingVertical: Bool) {
self.candidates = candidates
self.isTypingVertical = isTypingVertical
}
var attributedString: NSMutableAttributedString {
public var type: StateType { .ofAssociates }
var candidates: [(String, String)] { data.candidates }
init(candidates: [(String, String)]) {
data.candidates = candidates
attributedString = {
let attributedString = NSMutableAttributedString(
string: " ",
attributes: [
@ -133,6 +129,7 @@ public enum InputState {
]
)
return attributedString
}()
}
}
@ -145,27 +142,32 @@ public enum InputState {
/// - .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 composingBuffer: String
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 composingBufferConverted: String {
let converted = IME.kanjiConversionIfRequired(composingBuffer)
if converted.utf16.count != composingBuffer.utf16.count
|| converted.count != composingBuffer.count
public var displayedTextConverted: String {
let converted = IME.kanjiConversionIfRequired(displayedText)
if converted.utf16.count != displayedText.utf16.count
|| converted.count != displayedText.count
{
return composingBuffer
return displayedText
}
return converted
}
public var committingBufferConverted: String { composingBufferConverted }
public var committingBufferConverted: String { displayedTextConverted }
init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
self.composingBuffer = composingBuffer
init(displayedText: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
self.displayedText = displayedText
self.reading = reading
// reading
if !reading.isEmpty {
@ -189,13 +191,12 @@ public enum InputState {
} else {
self.nodeValuesArray = nodeValuesArray
}
defer { self.cursorIndex = cursorIndex }
}
var attributedString: NSMutableAttributedString {
defer {
self.cursorIndex = cursorIndex
self.attributedString = {
///
/// JIS
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
let attributedString = NSMutableAttributedString(string: displayedTextConverted)
var newBegin = 0
for (i, neta) in nodeValuesArray.enumerated() {
attributedString.setAttributes(
@ -208,6 +209,8 @@ public enum InputState {
newBegin += neta.utf16.count
}
return attributedString
}()
}
}
}
@ -216,23 +219,20 @@ public enum InputState {
/// .Inputting: 使Compositor
class Inputting: NotEmpty {
override public var type: StateType { .ofInputting }
var textToCommit: String = ""
var tooltip: String = ""
override public var committingBufferConverted: String {
let committingBuffer = nodeValuesArray.joined()
let converted = IME.kanjiConversionIfRequired(committingBuffer)
if converted.utf16.count != composingBuffer.utf16.count
|| converted.count != composingBuffer.count
if converted.utf16.count != displayedText.utf16.count
|| converted.count != displayedText.count
{
return composingBuffer
return displayedText
}
return converted
}
override init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
override init(displayedText: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
super.init(
composingBuffer: composingBuffer, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
displayedText: displayedText, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
}
}
@ -247,8 +247,8 @@ public enum InputState {
private(set) var markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } }
private(set) var markedRange: Range<Int>
private var literalMarkedRange: Range<Int> {
let lowerBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.lowerBound)
let upperBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.upperBound)
let lowerBoundLiteral = displayedText.charIndexLiteral(from: markedRange.lowerBound)
let upperBoundLiteral = displayedText.charIndexLiteral(from: markedRange.upperBound)
return lowerBoundLiteral..<upperBoundLiteral
}
@ -274,8 +274,9 @@ public enum InputState {
}
private var markedTargetExists = false
var tooltip: String {
if composingBuffer.count != readings.count {
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: ""
@ -291,7 +292,7 @@ public enum InputState {
return ""
}
let text = composingBuffer.utf16SubString(with: markedRange)
let text = displayedText.utf16SubString(with: markedRange)
if literalMarkedRange.count < allowedMarkRange.lowerBound {
ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency)
return String(
@ -331,26 +332,26 @@ public enum InputState {
)
}
var tooltipForInputting: String = ""
var tooltipBackupForInputting: String = ""
private(set) var readings: [String]
init(
composingBuffer: String, cursorIndex: Int, markerIndex: Int, readings: [String], nodeValuesArray: [String] = []
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(
composingBuffer: composingBuffer, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray
displayedText: displayedText, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray
)
defer { self.markerIndex = markerIndex }
}
override var attributedString: NSMutableAttributedString {
defer {
self.markerIndex = markerIndex
tooltip = tooltipForMarking
attributedString = {
///
/// JIS
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
let attributedString = NSMutableAttributedString(string: displayedTextConverted)
let end = markedRange.upperBound
attributedString.setAttributes(
@ -376,26 +377,28 @@ public enum InputState {
],
range: NSRange(
location: end,
length: composingBuffer.utf16.count - end
length: displayedText.utf16.count - end
)
)
return attributedString
}()
}
}
var convertedToInputting: Inputting {
let state = Inputting(
composingBuffer: composingBuffer, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
displayedText: displayedText, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
state.tooltip = tooltipForInputting
state.tooltip = tooltipBackupForInputting
return state
}
var validToFilter: Bool { markedTargetExists ? allowedMarkRange.contains(literalMarkedRange.count) : false }
var isFilterable: Bool { markedTargetExists ? allowedMarkRange.contains(literalMarkedRange.count) : false }
var bufferReadingCountMisMatch: Bool { composingBuffer.count != readings.count }
var bufferReadingCountMisMatch: Bool { displayedText.count != readings.count }
var chkIfUserPhraseExists: Bool {
let text = composingBuffer.utf16SubString(with: markedRange)
let text = displayedText.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist(
@ -404,7 +407,7 @@ public enum InputState {
}
var userPhrase: String {
let text = composingBuffer.utf16SubString(with: markedRange)
let text = displayedText.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
@ -413,7 +416,7 @@ public enum InputState {
var userPhraseConverted: String {
let text =
ChineseConverter.crossConvert(composingBuffer.utf16SubString(with: markedRange)) ?? ""
ChineseConverter.crossConvert(displayedText.utf16SubString(with: markedRange)) ?? ""
let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
@ -427,9 +430,8 @@ public enum InputState {
/// .ChoosingCandidate: 使
class ChoosingCandidate: NotEmpty {
override var isCandidateContainer: Bool { true }
override public var type: StateType { .ofChoosingCandidate }
private(set) var candidates: [(String, String)]
private(set) var isTypingVertical: Bool
override public var type: StateType { .ofCandidates }
var candidates: [(String, String)]
// ctlInputMethod.candidateSelectionChanged()
public var chosenCandidateString: String = "" {
didSet {
@ -443,76 +445,11 @@ public enum InputState {
}
init(
composingBuffer: String, cursorIndex: Int, candidates: [(String, String)], isTypingVertical: Bool,
displayedText: String, cursorIndex: Int, candidates: [(String, String)],
nodeValuesArray: [String] = []
) {
self.candidates = candidates
self.isTypingVertical = isTypingVertical
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray)
}
// 使 chosenCandidateString
// macOS
// ctlInputMethod.candidateSelectionChanged()
//
override var attributedString: NSMutableAttributedString {
guard !chosenCandidateString.isEmpty else { return super.attributedString }
let bufferTextRear = composingBuffer.utf16SubString(with: 0..<cursorIndex)
let bufferTextFront = composingBuffer.utf16SubString(with: cursorIndex..<(composingBuffer.utf16.count))
let cursorIndexU8 = bufferTextRear.count - 1
//
if (mgrPrefs.useRearCursorMode && bufferTextFront.count < chosenCandidateString.count)
|| (!mgrPrefs.useRearCursorMode && bufferTextRear.count < chosenCandidateString.count)
{
return super.attributedString
}
// u16Range NSAttributedString NSRange
let u16Range: Range<Int> = {
switch mgrPrefs.useRearCursorMode {
case false: return (max(0, cursorIndex - chosenCandidateString.utf16.count))..<cursorIndex
case true:
return
cursorIndex..<min(cursorIndex + chosenCandidateString.utf16.count, composingBuffer.utf16.count - 1)
}
}()
// u8Range
let u8Range: Range<Int> = {
switch mgrPrefs.useRearCursorMode {
case false: return (max(0, cursorIndexU8 - chosenCandidateString.count))..<cursorIndexU8
case true:
return cursorIndexU8..<min(cursorIndexU8 + chosenCandidateString.count, composingBuffer.count - 1)
}
}()
let strSegmentedRear = composingBuffer.charComponents[0..<u8Range.lowerBound].joined()
let strSegmentedFront = composingBuffer.charComponents[u8Range.upperBound...].joined()
let newBufferConverted: String = NotEmpty(
composingBuffer: strSegmentedRear + chosenCandidateString + strSegmentedFront, cursorIndex: 0
).composingBufferConverted
guard newBufferConverted.count == composingBuffer.count else { return super.attributedString }
///
/// JIS
let attributedStringResult = NSMutableAttributedString(string: newBufferConverted)
attributedStringResult.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0,
], range: NSRange(location: 0, length: u16Range.lowerBound)
)
attributedStringResult.setAttributes(
[
.underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 1,
], range: NSRange(location: u16Range.lowerBound, length: u16Range.count)
)
attributedStringResult.setAttributes(
[
.underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 2,
], range: NSRange(location: u16Range.upperBound, length: newBufferConverted.utf16.count)
)
return attributedStringResult
super.init(displayedText: displayedText, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray)
}
}
@ -521,26 +458,22 @@ public enum InputState {
/// .SymbolTable:
class SymbolTable: ChoosingCandidate {
override public var type: StateType { .ofSymbolTable }
var node: SymbolNode
init(node: SymbolNode, previous: SymbolNode? = nil, isTypingVertical: Bool) {
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]()
super.init(
composingBuffer: "", cursorIndex: 0, candidates: candidates.map { ("", $0) },
isTypingVertical: isTypingVertical
)
}
self.candidates = candidates.map { ("", $0) }
// InputState.SymbolTable
// MS Word
//
//
// Crediting Qwertyyb: https://github.com/qwertyyb/Fire/issues/55#issuecomment-1133497700
override var attributedString: NSMutableAttributedString {
attributedString = {
let attributedString = NSMutableAttributedString(
string: " ",
attributes: [
@ -549,6 +482,7 @@ public enum InputState {
]
)
return attributedString
}()
}
}
}

View File

@ -41,7 +41,7 @@ extension KeyHandler {
|| ((input.isCursorBackward || input.isCursorForward) && input.isShiftHold)
if cancelCandidateKey {
if state is InputState.AssociatedPhrases
if state is InputState.Associates
|| mgrPrefs.useSCPCTypingMode
|| compositor.isEmpty
{
@ -49,13 +49,13 @@ extension KeyHandler {
//
// 使 BackSpace
// compositor.isEmpty
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
} else {
stateCallback(buildInputtingState)
}
if let state = state as? InputState.SymbolTable, let nodePrevious = state.node.previous {
stateCallback(InputState.SymbolTable(node: nodePrevious, isTypingVertical: state.isTypingVertical))
stateCallback(InputState.SymbolTable(node: nodePrevious))
}
return true
}
@ -63,8 +63,8 @@ extension KeyHandler {
// MARK: Enter
if input.isEnter {
if state is InputState.AssociatedPhrases, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
stateCallback(InputState.EmptyIgnoringPreviousState())
if state is InputState.Associates, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
return true
}
@ -247,7 +247,7 @@ extension KeyHandler {
if let state = state as? InputState.ChoosingCandidate {
candidates = state.candidates
} else if let state = state as? InputState.AssociatedPhrases {
} else if let state = state as? InputState.Associates {
candidates = state.candidates
}
@ -267,13 +267,13 @@ extension KeyHandler {
// MARK: (Associated Phrases)
if state is InputState.AssociatedPhrases {
if state is InputState.Associates {
if !input.isShiftHold { return false }
}
var index: Int = NSNotFound
let match: String =
(state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : input.text
(state is InputState.Associates) ? input.inputTextIgnoringModifiers ?? "" : input.text
for j in 0..<ctlCandidateCurrent.keyLabels.count {
let label: CandidateKeyLabel = ctlCandidateCurrent.keyLabels[j]
@ -293,7 +293,7 @@ extension KeyHandler {
}
}
if state is InputState.AssociatedPhrases { return false }
if state is InputState.Associates { return false }
// MARK: (SCPC Mode Processing)
@ -333,7 +333,7 @@ extension KeyHandler {
didSelectCandidateAt: candidateIndex,
ctlCandidate: ctlCandidateCurrent
)
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
return handle(
input: input, state: InputState.Empty(), stateCallback: stateCallback, errorCallback: errorCallback

View File

@ -62,7 +62,7 @@ extension KeyHandler {
composer.receiveKey(fromString: input.text)
keyConsumedByReading = true
// 調 updateClientComposingBuffer() return true
// 調 updateClientdisplayedText() return true
// 調
if !composer.hasToneMarker() {
stateCallback(buildInputtingState)
@ -100,7 +100,7 @@ extension KeyHandler {
switch compositor.isEmpty {
case false: stateCallback(buildInputtingState)
case true:
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
}
return true // IMK
@ -118,7 +118,7 @@ extension KeyHandler {
//
composer.clear()
// updateClientComposingBuffer()
// updateClientdisplayedText()
let inputting = buildInputtingState
stateCallback(inputting)
@ -157,7 +157,7 @@ extension KeyHandler {
/// 調
if keyConsumedByReading {
// updateClientComposingBuffer()
// updateClientdisplayedText()
stateCallback(buildInputtingState)
return true
}

View File

@ -38,7 +38,7 @@ extension KeyHandler {
// Megrez
if input.isInvalid {
// .Empty(IgnoringPreviousState) .Deactivated
// .EmptyIgnoringPreviousState.Empty
// .Abortion.Empty
if state is InputState.Empty || state is InputState.Deactivated {
return false
}
@ -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.AssociatedPhrases) && isFunctionKey {
if !(state is InputState.NotEmpty) && !(state is InputState.Associates) && isFunctionKey {
return false
}
@ -94,7 +94,7 @@ extension KeyHandler {
// 使 Cocoa flags
//
if input.isNumericPadKey {
if !(state is InputState.ChoosingCandidate || state is InputState.AssociatedPhrases
if !(state is InputState.ChoosingCandidate || state is InputState.Associates
|| state is InputState.SymbolTable)
{
stateCallback(InputState.Empty())
@ -114,7 +114,7 @@ extension KeyHandler {
// MARK: (Handle Associated Phrases)
if state is InputState.AssociatedPhrases {
if state is InputState.Associates {
if handleCandidate(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
) {
@ -155,9 +155,9 @@ extension KeyHandler {
/// Space
if !mgrPrefs.chooseCandidateUsingSpace {
if compositor.cursor >= compositor.length {
let composingBuffer = currentState.composingBuffer
if !composingBuffer.isEmpty {
stateCallback(InputState.Committing(textToCommit: composingBuffer))
let displayedText = currentState.displayedText
if !displayedText.isEmpty {
stateCallback(InputState.Committing(textToCommit: displayedText))
}
stateCallback(InputState.Committing(textToCommit: " "))
stateCallback(InputState.Empty())

View File

@ -110,7 +110,7 @@ extension KeyHandler {
/// 使
return InputState.Inputting(
composingBuffer: cleanedComposition, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
displayedText: cleanedComposition, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
)
}
@ -123,13 +123,12 @@ extension KeyHandler {
/// - Returns:
func buildCandidate(
state currentState: InputState.NotEmpty,
isTypingVertical: Bool = false
isTypingVertical _: Bool = false
) -> InputState.ChoosingCandidate {
InputState.ChoosingCandidate(
composingBuffer: currentState.composingBuffer,
displayedText: currentState.displayedText,
cursorIndex: currentState.cursorIndex,
candidates: getCandidatesArray(fixOrder: mgrPrefs.useFixecCandidateOrderOnSelection),
isTypingVertical: isTypingVertical,
nodeValuesArray: compositor.walkedNodes.values
)
}
@ -147,16 +146,13 @@ extension KeyHandler {
///
/// - Parameters:
/// - key:
/// - isTypingVertical:
/// - Returns:
func buildAssociatePhraseState(
withPair pair: Megrez.Compositor.Candidate,
isTypingVertical: Bool
) -> InputState.AssociatedPhrases! {
withPair pair: Megrez.Compositor.Candidate
) -> InputState.Associates! {
//  Xcode
InputState.AssociatedPhrases(
candidates: buildAssociatePhraseArray(withPair: pair), isTypingVertical: isTypingVertical
)
InputState.Associates(
candidates: buildAssociatePhraseArray(withPair: pair))
}
// MARK: -
@ -190,7 +186,7 @@ extension KeyHandler {
if input.isEnter {
if let keyHandlerDelegate = delegate {
//
if input.isShiftHold, input.isCommandHold, !state.validToFilter {
if input.isShiftHold, input.isCommandHold, !state.isFilterable {
IME.prtDebugIntel("2EAC1F7A")
errorCallback()
return true
@ -207,7 +203,7 @@ extension KeyHandler {
// BackSpace & Delete
if input.isBackSpace || input.isDelete {
if let keyHandlerDelegate = delegate {
if !state.validToFilter {
if !state.isFilterable {
IME.prtDebugIntel("1F88B191")
errorCallback()
return true
@ -226,15 +222,15 @@ extension KeyHandler {
if input.isCursorBackward || input.emacsKey == EmacsKey.backward, input.isShiftHold {
var index = state.markerIndex
if index > 0 {
index = state.composingBuffer.utf16PreviousPosition(for: index)
index = state.displayedText.utf16PreviousPosition(for: index)
let marking = InputState.Marking(
composingBuffer: state.composingBuffer,
displayedText: state.displayedText,
cursorIndex: state.cursorIndex,
markerIndex: index,
readings: state.readings,
nodeValuesArray: compositor.walkedNodes.values
)
marking.tooltipForInputting = state.tooltipForInputting
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else {
IME.prtDebugIntel("1149908D")
@ -247,16 +243,16 @@ extension KeyHandler {
// Shift + Right
if input.isCursorForward || input.emacsKey == EmacsKey.forward, input.isShiftHold {
var index = state.markerIndex
if index < (state.composingBuffer.utf16.count) {
index = state.composingBuffer.utf16NextPosition(for: index)
if index < (state.displayedText.utf16.count) {
index = state.displayedText.utf16NextPosition(for: index)
let marking = InputState.Marking(
composingBuffer: state.composingBuffer,
displayedText: state.displayedText,
cursorIndex: state.cursorIndex,
markerIndex: index,
readings: state.readings,
nodeValuesArray: compositor.walkedNodes.values
)
marking.tooltipForInputting = state.tooltipForInputting
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
} else {
IME.prtDebugIntel("9B51408D")
@ -336,7 +332,7 @@ extension KeyHandler {
) -> Bool {
guard let currentState = state as? InputState.Inputting else { return false }
stateCallback(InputState.Committing(textToCommit: currentState.composingBuffer))
stateCallback(InputState.Committing(textToCommit: currentState.displayedText))
stateCallback(InputState.Empty())
return true
}
@ -354,17 +350,17 @@ extension KeyHandler {
) -> Bool {
guard state is InputState.Inputting else { return false }
var composingBuffer = compositor.keys.joined(separator: "-")
var displayedText = compositor.keys.joined(separator: "-")
if mgrPrefs.inlineDumpPinyinInLieuOfZhuyin {
composingBuffer = Tekkon.restoreToneOneInZhuyinKey(target: composingBuffer) //
composingBuffer = Tekkon.cnvPhonaToHanyuPinyin(target: composingBuffer) //
displayedText = Tekkon.restoreToneOneInZhuyinKey(target: displayedText) //
displayedText = Tekkon.cnvPhonaToHanyuPinyin(target: displayedText) //
}
if let delegate = delegate, !delegate.clientBundleIdentifier.contains("vChewingPhraseEditor") {
composingBuffer = composingBuffer.replacingOccurrences(of: "-", with: " ")
displayedText = displayedText.replacingOccurrences(of: "-", with: " ")
}
stateCallback(InputState.Committing(textToCommit: composingBuffer))
stateCallback(InputState.Committing(textToCommit: displayedText))
stateCallback(InputState.Empty())
return true
}
@ -434,14 +430,14 @@ extension KeyHandler {
stateCallback(buildInputtingState)
return true
case 1:
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
return true
default: break
}
if input.isShiftHold, input.isOptionHold {
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
return true
}
@ -465,7 +461,7 @@ extension KeyHandler {
switch composer.isEmpty && compositor.isEmpty {
case false: stateCallback(buildInputtingState)
case true:
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
}
return true
@ -489,7 +485,7 @@ extension KeyHandler {
guard state is InputState.Inputting else { return false }
if input.isShiftHold {
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
return true
}
@ -510,10 +506,10 @@ extension KeyHandler {
let inputting = buildInputtingState
// count > 0!isEmpty滿
switch inputting.composingBuffer.isEmpty {
switch inputting.displayedText.isEmpty {
case false: stateCallback(inputting)
case true:
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
}
return true
@ -625,7 +621,7 @@ extension KeyHandler {
if mgrPrefs.escToCleanInputBuffer {
///
/// macOS Windows 使
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
} else {
if composer.isEmpty { return true }
@ -634,7 +630,7 @@ extension KeyHandler {
switch compositor.isEmpty {
case false: stateCallback(buildInputtingState)
case true:
stateCallback(InputState.EmptyIgnoringPreviousState())
stateCallback(InputState.Abortion())
stateCallback(InputState.Empty())
}
}
@ -667,16 +663,16 @@ extension KeyHandler {
if input.isShiftHold {
// Shift + Right
if currentState.cursorIndex < currentState.composingBuffer.utf16.count {
let nextPosition = currentState.composingBuffer.utf16NextPosition(
if currentState.cursorIndex < currentState.displayedText.utf16.count {
let nextPosition = currentState.displayedText.utf16NextPosition(
for: currentState.cursorIndex)
let marking: InputState.Marking! = InputState.Marking(
composingBuffer: currentState.composingBuffer,
displayedText: currentState.displayedText,
cursorIndex: currentState.cursorIndex,
markerIndex: nextPosition,
readings: compositor.keys
)
marking.tooltipForInputting = currentState.tooltip
marking.tooltipBackupForInputting = currentState.tooltip
stateCallback(marking)
} else {
IME.prtDebugIntel("BB7F6DB9")
@ -742,15 +738,15 @@ extension KeyHandler {
if input.isShiftHold {
// Shift + left
if currentState.cursorIndex > 0 {
let previousPosition = currentState.composingBuffer.utf16PreviousPosition(
let previousPosition = currentState.displayedText.utf16PreviousPosition(
for: currentState.cursorIndex)
let marking: InputState.Marking! = InputState.Marking(
composingBuffer: currentState.composingBuffer,
displayedText: currentState.displayedText,
cursorIndex: currentState.cursorIndex,
markerIndex: previousPosition,
readings: compositor.keys
)
marking.tooltipForInputting = currentState.tooltip
marking.tooltipBackupForInputting = currentState.tooltip
stateCallback(marking)
} else {
IME.prtDebugIntel("D326DEA3")

View File

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

View File

@ -71,7 +71,7 @@ class ctlInputMethod: IMKInputController {
}
if let state = state as? InputState.NotEmpty {
/// 調
handle(state: InputState.Committing(textToCommit: state.composingBufferConverted))
handle(state: InputState.Committing(textToCommit: state.displayedTextConverted))
}
handle(state: InputState.Empty())
}
@ -115,11 +115,9 @@ class ctlInputMethod: IMKInputController {
} else {
NotifierController.notify(
message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n"
+ {
isASCIIMode
+ isASCIIMode
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
}()
)
}
}
@ -325,7 +323,7 @@ class ctlInputMethod: IMKInputController {
}
}
if let state = state as? InputState.AssociatedPhrases {
if let state = state as? InputState.Associates {
handleCandidatesPrepared(state.candidates, prefix: "")
} else if let state = state as? InputState.SymbolTable {
// / JIS 使
@ -363,9 +361,9 @@ class ctlInputMethod: IMKInputController {
/// - Parameter candidateString:
override open func candidateSelected(_ candidateString: NSAttributedString!) {
let candidateString: NSAttributedString = candidateString ?? .init(string: "")
if state is InputState.AssociatedPhrases {
if state is InputState.Associates {
if !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
handle(state: InputState.EmptyIgnoringPreviousState())
handle(state: InputState.Abortion())
handle(state: InputState.Empty())
return
}
@ -403,7 +401,7 @@ class ctlInputMethod: IMKInputController {
}
}
if let state = state as? InputState.AssociatedPhrases {
if let state = state as? InputState.Associates {
handleCandidatesSelected(state.candidates, prefix: "")
} else if let state = state as? InputState.SymbolTable {
handleSymbolCandidatesSelected(state.candidates)

View File

@ -65,7 +65,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
// MARK: - Candidate Controller Delegate
extension ctlInputMethod: ctlCandidateDelegate {
var isAssociatedPhrasesState: Bool { state is InputState.AssociatedPhrases }
var isAssociatedPhrasesState: Bool { state is InputState.Associates }
/// handle() IMK
/// handle()
@ -80,7 +80,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates.count
} else if let state = state as? InputState.AssociatedPhrases {
} else if let state = state as? InputState.Associates {
return state.candidates.count
}
return 0
@ -93,7 +93,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates
} else if let state = state as? InputState.AssociatedPhrases {
} else if let state = state as? InputState.Associates {
return state.candidates
}
return .init()
@ -105,7 +105,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
_ = controller //
if let state = state as? InputState.ChoosingCandidate {
return state.candidates[index]
} else if let state = state as? InputState.AssociatedPhrases {
} else if let state = state as? InputState.Associates {
return state.candidates[index]
}
return ("", "")
@ -119,9 +119,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
{
if let children = node.children, !children.isEmpty {
handle(state: InputState.Empty()) //
handle(
state: InputState.SymbolTable(node: node, previous: state.node, isTypingVertical: state.isTypingVertical)
)
handle(state: InputState.SymbolTable(node: node, previous: state.node))
} else {
handle(state: InputState.Committing(textToCommit: node.title))
handle(state: InputState.Empty())
@ -139,12 +137,11 @@ extension ctlInputMethod: ctlCandidateDelegate {
let inputting = keyHandler.buildInputtingState
if mgrPrefs.useSCPCTypingMode {
handle(state: InputState.Committing(textToCommit: inputting.composingBufferConverted))
handle(state: InputState.Committing(textToCommit: inputting.displayedTextConverted))
// selectedValue.1
if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState(
withPair: .init(key: selectedValue.0, value: selectedValue.1),
isTypingVertical: state.isTypingVertical
withPair: .init(key: selectedValue.0, value: selectedValue.1)
), !associatePhrases.candidates.isEmpty
{
handle(state: associatePhrases)
@ -157,7 +154,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
return
}
if let state = state as? InputState.AssociatedPhrases {
if let state = state as? InputState.Associates {
let selectedValue = state.candidates[index]
handle(state: InputState.Committing(textToCommit: selectedValue.1))
// selectedValue.1
@ -168,8 +165,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
}
if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState(
withPair: .init(key: selectedValue.0, value: String(valueKept)),
isTypingVertical: state.isTypingVertical
withPair: .init(key: selectedValue.0, value: String(valueKept))
), !associatePhrases.candidates.isEmpty
{
handle(state: associatePhrases)

View File

@ -13,11 +13,11 @@ import Cocoa
// MARK: - Tooltip Display and Candidate Display Methods
extension ctlInputMethod {
func show(tooltip: String, composingBuffer: String, cursorIndex: Int) {
func show(tooltip: String, displayedText: String, cursorIndex: 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
if cursor == composingBuffer.count, cursor != 0 {
if cursor == displayedText.count, cursor != 0 {
cursor -= 1
}
while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
@ -41,10 +41,10 @@ extension ctlInputMethod {
func show(candidateWindowWith state: InputStateProtocol) {
guard let client = client() else { return }
var isTypingVertical: Bool {
if let state = state as? InputState.ChoosingCandidate {
return state.isTypingVertical
} else if let state = state as? InputState.AssociatedPhrases {
return state.isTypingVertical
if state.type == .ofCandidates {
return ctlInputMethod.isVerticalTypingSituation
} else if state.type == ..ofAssociates {
return ctlInputMethod.isVerticalTypingSituation
}
return false
}
@ -52,7 +52,7 @@ extension ctlInputMethod {
var candidates: [(String, String)] = .init()
if let state = state as? InputState.ChoosingCandidate {
candidates = state.candidates
} else if let state = state as? InputState.AssociatedPhrases {
} else if let state = state as? InputState.Associates {
candidates = state.candidates
}
if isTypingVertical { return true }
@ -106,7 +106,7 @@ extension ctlInputMethod {
let candidateKeys = mgrPrefs.candidateKeys
let keyLabels =
candidateKeys.count > 4 ? Array(candidateKeys) : Array(mgrPrefs.defaultCandidateKeys)
let keyLabelSuffix = state is InputState.AssociatedPhrases ? "^" : ""
let keyLabelSuffix = state is InputState.Associates ? "^" : ""
ctlInputMethod.ctlCandidateCurrent.keyLabels = keyLabels.map {
CandidateKeyLabel(key: String($0), displayedText: String($0) + keyLabelSuffix)
}
@ -128,7 +128,7 @@ extension ctlInputMethod {
if let state = state as? InputState.ChoosingCandidate {
cursor = state.cursorIndex
if cursor == state.composingBuffer.count, cursor != 0 {
if cursor == state.displayedText.count, cursor != 0 {
cursor -= 1
}
}

View File

@ -27,7 +27,7 @@ extension ctlInputMethod {
handle(state: newState, previous: prevState)
case let newState as InputState.Empty:
handle(state: newState, previous: prevState)
case let newState as InputState.EmptyIgnoringPreviousState:
case let newState as InputState.Abortion:
handle(state: newState, previous: prevState)
case let newState as InputState.Committing:
handle(state: newState, previous: prevState)
@ -37,7 +37,7 @@ extension ctlInputMethod {
handle(state: newState, previous: prevState)
case let newState as InputState.ChoosingCandidate:
handle(state: newState, previous: prevState)
case let newState as InputState.AssociatedPhrases:
case let newState as InputState.Associates:
handle(state: newState, previous: prevState)
case let newState as InputState.SymbolTable:
handle(state: newState, previous: prevState)
@ -48,7 +48,7 @@ extension ctlInputMethod {
/// .NotEmpty()
func setInlineDisplayWithCursor() {
guard let client = client() else { return }
if let state = state as? InputState.AssociatedPhrases {
if let state = state as? InputState.Associates {
client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
@ -87,7 +87,7 @@ extension ctlInputMethod {
[.languageIdentifier: identifier],
range: NSRange(
location: 0,
length: state.composingBuffer.utf16.count
length: state.displayedText.utf16.count
)
)
}
@ -141,9 +141,9 @@ extension ctlInputMethod {
_ = state //
ctlInputMethod.ctlCandidateCurrent.visible = false
ctlInputMethod.tooltipController.hide()
// .EmptyIgnoringPreviousState
// .Abortion
if let previous = previous as? InputState.NotEmpty,
!(state is InputState.EmptyIgnoringPreviousState)
!(state is InputState.Abortion)
{
commit(text: previous.committingBufferConverted)
}
@ -156,7 +156,7 @@ extension ctlInputMethod {
}
private func handle(
state: InputState.EmptyIgnoringPreviousState, previous: InputStateProtocol
state: InputState.Abortion, previous: InputStateProtocol
) {
_ = state //
_ = previous //
@ -188,7 +188,7 @@ extension ctlInputMethod {
setInlineDisplayWithCursor()
if !state.tooltip.isEmpty {
show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer,
tooltip: state.tooltip, displayedText: state.displayedText,
cursorIndex: state.cursorIndex
)
}
@ -202,7 +202,7 @@ extension ctlInputMethod {
ctlInputMethod.tooltipController.hide()
} else {
show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer,
tooltip: state.tooltip, displayedText: state.displayedText,
cursorIndex: state.markerIndex
)
}
@ -222,7 +222,7 @@ extension ctlInputMethod {
show(candidateWindowWith: state)
}
private func handle(state: InputState.AssociatedPhrases, previous: InputStateProtocol) {
private func handle(state: InputState.Associates, previous: InputStateProtocol) {
_ = previous //
ctlInputMethod.tooltipController.hide()
setInlineDisplayWithCursor()

View File

@ -212,11 +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: "")
}()
)
}
@ -224,11 +222,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Force KangXi Writing", comment: "") + "\n"
+ {
mgrPrefs.toggleChineseConversionEnabled()
+ mgrPrefs.toggleChineseConversionEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
}()
)
}
@ -236,11 +232,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("JIS Shinjitai Output", comment: "") + "\n"
+ {
mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
+ mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
}()
)
}
@ -248,11 +242,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Currency Numeral Output", comment: "") + "\n"
+ {
mgrPrefs.toggleCurrencyNumeralsEnabled()
+ mgrPrefs.toggleCurrencyNumeralsEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
}()
)
}
@ -260,11 +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: "")
}()
)
}
@ -272,11 +262,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("CNS11643 Mode", comment: "") + "\n"
+ {
mgrPrefs.toggleCNS11643Enabled()
+ mgrPrefs.toggleCNS11643Enabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
}()
)
}
@ -284,11 +272,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Symbol & Emoji Input", comment: "") + "\n"
+ {
mgrPrefs.toggleSymbolInputEnabled()
+ mgrPrefs.toggleSymbolInputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
}()
)
}
@ -296,11 +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: "")
}()
)
}
@ -308,11 +292,9 @@ extension ctlInputMethod {
resetKeyHandler()
NotifierController.notify(
message: NSLocalizedString("Use Phrase Replacement", comment: "") + "\n"
+ {
mgrPrefs.togglePhraseReplacementEnabled()
+ mgrPrefs.togglePhraseReplacementEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
}()
)
}

View File

@ -438,6 +438,7 @@ public enum mgrPrefs {
@UserDefault(key: UserDef.kTrimUnfinishedReadingsOnCommit.rawValue, defaultValue: true)
static var trimUnfinishedReadingsOnCommit: Bool
// MARK: - Settings (Tier 2)
@UserDefault(key: UserDef.kUseIMKCandidateWindow.rawValue, defaultValue: false)
@ -458,6 +459,8 @@ public enum mgrPrefs {
@UserDefault(key: UserDef.kMaxCandidateLength.rawValue, defaultValue: 10)
static var maxCandidateLength: Int
static var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength
@UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep.rawValue, defaultValue: true)
static var shouldNotFartInLieuOfBeep: Bool

View File

@ -10,7 +10,7 @@
import Foundation
class SymbolNode {
public class SymbolNode {
var title: String
var children: [SymbolNode]?
var previous: SymbolNode?

View File

@ -117,6 +117,8 @@
5BEDB724283B4C250078EB25 /* data-symbols.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71E283B4AEA0078EB25 /* data-symbols.plist */; };
5BEDB725283B4C250078EB25 /* data-chs.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71C283B4AEA0078EB25 /* data-chs.plist */; };
5BF0B84C28C070B000795FC6 /* NSEventExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */; };
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF56F9728C39A2700DD6839 /* IMEState.swift */; };
5BF56F9A28C39D1800DD6839 /* IMEStateData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF56F9928C39D1800DD6839 /* IMEStateData.swift */; };
5BF9DA2728840E6200DBD48E /* template-usersymbolphrases.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2228840E6200DBD48E /* template-usersymbolphrases.txt */; };
5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2328840E6200DBD48E /* template-exclusions.txt */; };
5BF9DA2928840E6200DBD48E /* template-associatedPhrases-chs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2428840E6200DBD48E /* template-associatedPhrases-chs.txt */; };
@ -346,6 +348,8 @@
5BEDB720283B4AEA0078EB25 /* data-cht.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; name = "data-cht.plist"; path = "Data/data-cht.plist"; sourceTree = "<group>"; };
5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEventExtension.swift; sourceTree = "<group>"; };
5BF255CD28B2694E003ECB60 /* vChewing-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "vChewing-Bridging-Header.h"; sourceTree = "<group>"; };
5BF56F9728C39A2700DD6839 /* IMEState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMEState.swift; sourceTree = "<group>"; };
5BF56F9928C39D1800DD6839 /* IMEStateData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMEStateData.swift; sourceTree = "<group>"; };
5BF9DA2228840E6200DBD48E /* template-usersymbolphrases.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-usersymbolphrases.txt"; sourceTree = "<group>"; usesTabs = 0; };
5BF9DA2328840E6200DBD48E /* template-exclusions.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-exclusions.txt"; sourceTree = "<group>"; usesTabs = 0; };
5BF9DA2428840E6200DBD48E /* template-associatedPhrases-chs.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; name = "template-associatedPhrases-chs.txt"; path = "../Data/components/chs/template-associatedPhrases-chs.txt"; sourceTree = "<group>"; usesTabs = 0; };
@ -498,6 +502,8 @@
5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */,
5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */,
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */,
5BF56F9728C39A2700DD6839 /* IMEState.swift */,
5BF56F9928C39D1800DD6839 /* IMEStateData.swift */,
D461B791279DAC010070E734 /* InputState.swift */,
5BD0113C2818543900609769 /* KeyHandler_Core.swift */,
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
@ -1206,6 +1212,7 @@
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 */,
D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */,
@ -1232,6 +1239,7 @@
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */,
5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */,
5B2170E4289FACAD00BE7304 /* 2_Walker.swift in Sources */,
5BF56F9A28C39D1800DD6839 /* IMEStateData.swift in Sources */,
5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */,
5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */,
5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */,