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 // 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 /// ctlInputMethod
/// ///
/// Finite State Machine/ /// Finite State Machine/
@ -56,7 +35,7 @@ public protocol InputStateProtocol {
/// 西 .NotEmpty /// 西 .NotEmpty
/// - .Empty: 使 /// - .Empty: 使
/// ///
/// - .EmptyIgnoringPreviousState: Empty /// - .Abortion: Empty
/// .Empty() /// .Empty()
/// - .Committing: /// - .Committing:
/// - .NotEmpty: /// - .NotEmpty:
@ -68,6 +47,12 @@ public protocol InputStateProtocol {
public enum InputState { public enum InputState {
/// .Deactivated: 使使 /// .Deactivated: 使使
class Deactivated: InputStateProtocol { 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 hasBuffer: Bool = false
let isCandidateContainer: Bool = false let isCandidateContainer: Bool = false
public var type: StateType { .ofDeactivated } public var type: StateType { .ofDeactivated }
@ -78,32 +63,41 @@ public enum InputState {
/// .Empty: 使 /// .Empty: 使
/// ///
class Empty: InputStateProtocol { 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 hasBuffer: Bool = false
let isCandidateContainer: Bool = false let isCandidateContainer: Bool = false
public var type: StateType { .ofEmpty } public var type: StateType { .ofEmpty }
let composingBuffer: String = "" let displayedText: String = ""
} }
// MARK: - // MARK: -
/// .EmptyIgnoringPreviousState: Empty /// .Abortion: Empty
/// ///
/// .Empty() /// .Empty()
class EmptyIgnoringPreviousState: Empty { class Abortion: Empty {
override public var type: StateType { .ofEmptyIgnoringPreviousState } override public var type: StateType { .ofAbortion }
} }
// MARK: - // MARK: -
/// .Committing: /// .Committing:
class Committing: InputStateProtocol { 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 hasBuffer: Bool = false
let isCandidateContainer: Bool = false let isCandidateContainer: Bool = false
public var type: StateType { .ofCommitting } public var type: StateType { .ofCommitting }
private(set) var textToCommit: String = ""
convenience init(textToCommit: String) { init(textToCommit: String) {
self.init()
self.textToCommit = textToCommit self.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &self.textToCommit) ChineseConverter.ensureCurrencyNumerals(target: &self.textToCommit)
} }
@ -113,26 +107,29 @@ public enum InputState {
/// .AssociatedPhrases: /// .AssociatedPhrases:
/// 西 .NotEmpty /// 西 .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 hasBuffer: Bool = false
let isCandidateContainer: Bool = true let isCandidateContainer: Bool = true
public var type: StateType { .ofAssociatedPhrases } public var type: StateType { .ofAssociates }
private(set) var candidates: [(String, String)] = [] var candidates: [(String, String)] { data.candidates }
private(set) var isTypingVertical: Bool = false init(candidates: [(String, String)]) {
init(candidates: [(String, String)], isTypingVertical: Bool) { data.candidates = candidates
self.candidates = candidates attributedString = {
self.isTypingVertical = isTypingVertical let attributedString = NSMutableAttributedString(
} string: " ",
attributes: [
var attributedString: NSMutableAttributedString { .underlineStyle: NSUnderlineStyle.single.rawValue,
let attributedString = NSMutableAttributedString( .markedClauseSegment: 0,
string: " ", ]
attributes: [ )
.underlineStyle: NSUnderlineStyle.single.rawValue, return attributedString
.markedClauseSegment: 0, }()
]
)
return attributedString
} }
} }
@ -145,27 +142,32 @@ public enum InputState {
/// - .ChoosingCandidate: 使 /// - .ChoosingCandidate: 使
/// - .SymbolTable: /// - .SymbolTable:
class NotEmpty: InputStateProtocol { 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 let hasBuffer: Bool = true
var isCandidateContainer: Bool { false } var isCandidateContainer: Bool { false }
public var type: StateType { .ofNotEmpty } 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 cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } }
private(set) var reading: String = "" private(set) var reading: String = ""
private(set) var nodeValuesArray = [String]() private(set) var nodeValuesArray = [String]()
public var composingBufferConverted: String { public var displayedTextConverted: String {
let converted = IME.kanjiConversionIfRequired(composingBuffer) let converted = IME.kanjiConversionIfRequired(displayedText)
if converted.utf16.count != composingBuffer.utf16.count if converted.utf16.count != displayedText.utf16.count
|| converted.count != composingBuffer.count || converted.count != displayedText.count
{ {
return composingBuffer return displayedText
} }
return converted return converted
} }
public var committingBufferConverted: String { composingBufferConverted } public var committingBufferConverted: String { displayedTextConverted }
init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) { init(displayedText: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
self.composingBuffer = composingBuffer self.displayedText = displayedText
self.reading = reading self.reading = reading
// reading // reading
if !reading.isEmpty { if !reading.isEmpty {
@ -189,25 +191,26 @@ public enum InputState {
} else { } else {
self.nodeValuesArray = nodeValuesArray self.nodeValuesArray = nodeValuesArray
} }
defer { self.cursorIndex = cursorIndex } defer {
} self.cursorIndex = cursorIndex
self.attributedString = {
var attributedString: NSMutableAttributedString { ///
/// /// JIS
/// JIS let attributedString = NSMutableAttributedString(string: displayedTextConverted)
let attributedString = NSMutableAttributedString(string: composingBufferConverted) var newBegin = 0
var newBegin = 0 for (i, neta) in nodeValuesArray.enumerated() {
for (i, neta) in nodeValuesArray.enumerated() { attributedString.setAttributes(
attributedString.setAttributes( [
[ /// .thick
/// .thick .underlineStyle: NSUnderlineStyle.single.rawValue,
.underlineStyle: NSUnderlineStyle.single.rawValue, .markedClauseSegment: i,
.markedClauseSegment: i, ], range: NSRange(location: newBegin, length: neta.utf16.count)
], range: NSRange(location: newBegin, length: neta.utf16.count) )
) newBegin += neta.utf16.count
newBegin += neta.utf16.count }
return attributedString
}()
} }
return attributedString
} }
} }
@ -216,23 +219,20 @@ public enum InputState {
/// .Inputting: 使Compositor /// .Inputting: 使Compositor
class Inputting: NotEmpty { class Inputting: NotEmpty {
override public var type: StateType { .ofInputting } override public var type: StateType { .ofInputting }
var textToCommit: String = ""
var tooltip: String = ""
override public var committingBufferConverted: String { override public var committingBufferConverted: String {
let committingBuffer = nodeValuesArray.joined() let committingBuffer = nodeValuesArray.joined()
let converted = IME.kanjiConversionIfRequired(committingBuffer) let converted = IME.kanjiConversionIfRequired(committingBuffer)
if converted.utf16.count != composingBuffer.utf16.count if converted.utf16.count != displayedText.utf16.count
|| converted.count != composingBuffer.count || converted.count != displayedText.count
{ {
return composingBuffer return displayedText
} }
return converted return converted
} }
override init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) { override init(displayedText: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
super.init( 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 markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } }
private(set) var markedRange: Range<Int> private(set) var markedRange: Range<Int>
private var literalMarkedRange: Range<Int> { private var literalMarkedRange: Range<Int> {
let lowerBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.lowerBound) let lowerBoundLiteral = displayedText.charIndexLiteral(from: markedRange.lowerBound)
let upperBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.upperBound) let upperBoundLiteral = displayedText.charIndexLiteral(from: markedRange.upperBound)
return lowerBoundLiteral..<upperBoundLiteral return lowerBoundLiteral..<upperBoundLiteral
} }
@ -274,8 +274,9 @@ public enum InputState {
} }
private var markedTargetExists = false 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) ctlInputMethod.tooltipController.setColor(state: .redAlert)
return NSLocalizedString( return NSLocalizedString(
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: "" "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: ""
@ -291,7 +292,7 @@ public enum InputState {
return "" return ""
} }
let text = composingBuffer.utf16SubString(with: markedRange) let text = displayedText.utf16SubString(with: markedRange)
if literalMarkedRange.count < allowedMarkRange.lowerBound { if literalMarkedRange.count < allowedMarkRange.lowerBound {
ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency) ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency)
return String( return String(
@ -331,71 +332,73 @@ public enum InputState {
) )
} }
var tooltipForInputting: String = "" var tooltipBackupForInputting: String = ""
private(set) var readings: [String] private(set) var readings: [String]
init( 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 begin = min(cursorIndex, markerIndex)
let end = max(cursorIndex, markerIndex) let end = max(cursorIndex, markerIndex)
markedRange = begin..<end markedRange = begin..<end
self.readings = readings self.readings = readings
super.init( super.init(
composingBuffer: composingBuffer, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray displayedText: displayedText, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray
) )
defer { self.markerIndex = markerIndex } defer {
} self.markerIndex = markerIndex
tooltip = tooltipForMarking
attributedString = {
///
/// JIS
let attributedString = NSMutableAttributedString(string: displayedTextConverted)
let end = markedRange.upperBound
override var attributedString: NSMutableAttributedString { attributedString.setAttributes(
/// [
/// JIS .underlineStyle: NSUnderlineStyle.single.rawValue,
let attributedString = NSMutableAttributedString(string: composingBufferConverted) .markedClauseSegment: 0,
let end = markedRange.upperBound ], range: NSRange(location: 0, length: markedRange.lowerBound)
)
attributedString.setAttributes( attributedString.setAttributes(
[ [
.underlineStyle: NSUnderlineStyle.single.rawValue, .underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 0, .markedClauseSegment: 1,
], range: NSRange(location: 0, length: markedRange.lowerBound) ],
) range: NSRange(
attributedString.setAttributes( location: markedRange.lowerBound,
[ length: markedRange.upperBound - markedRange.lowerBound
.underlineStyle: NSUnderlineStyle.thick.rawValue, )
.markedClauseSegment: 1, )
], attributedString.setAttributes(
range: NSRange( [
location: markedRange.lowerBound, .underlineStyle: NSUnderlineStyle.single.rawValue,
length: markedRange.upperBound - markedRange.lowerBound .markedClauseSegment: 2,
) ],
) range: NSRange(
attributedString.setAttributes( location: end,
[ length: displayedText.utf16.count - end
.underlineStyle: NSUnderlineStyle.single.rawValue, )
.markedClauseSegment: 2, )
], return attributedString
range: NSRange( }()
location: end, }
length: composingBuffer.utf16.count - end
)
)
return attributedString
} }
var convertedToInputting: Inputting { var convertedToInputting: Inputting {
let state = 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 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 { var chkIfUserPhraseExists: Bool {
let text = composingBuffer.utf16SubString(with: markedRange) let text = displayedText.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange] let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-") let joined = selectedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist( return mgrLangModel.checkIfUserPhraseExist(
@ -404,7 +407,7 @@ public enum InputState {
} }
var userPhrase: String { var userPhrase: String {
let text = composingBuffer.utf16SubString(with: markedRange) let text = displayedText.utf16SubString(with: markedRange)
let selectedReadings = readings[literalMarkedRange] let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-") let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : "" let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
@ -413,7 +416,7 @@ public enum InputState {
var userPhraseConverted: String { var userPhraseConverted: String {
let text = let text =
ChineseConverter.crossConvert(composingBuffer.utf16SubString(with: markedRange)) ?? "" ChineseConverter.crossConvert(displayedText.utf16SubString(with: markedRange)) ?? ""
let selectedReadings = readings[literalMarkedRange] let selectedReadings = readings[literalMarkedRange]
let joined = selectedReadings.joined(separator: "-") let joined = selectedReadings.joined(separator: "-")
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : "" let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
@ -427,9 +430,8 @@ public enum InputState {
/// .ChoosingCandidate: 使 /// .ChoosingCandidate: 使
class ChoosingCandidate: NotEmpty { class ChoosingCandidate: NotEmpty {
override var isCandidateContainer: Bool { true } override var isCandidateContainer: Bool { true }
override public var type: StateType { .ofChoosingCandidate } override public var type: StateType { .ofCandidates }
private(set) var candidates: [(String, String)] var candidates: [(String, String)]
private(set) var isTypingVertical: Bool
// ctlInputMethod.candidateSelectionChanged() // ctlInputMethod.candidateSelectionChanged()
public var chosenCandidateString: String = "" { public var chosenCandidateString: String = "" {
didSet { didSet {
@ -443,76 +445,11 @@ public enum InputState {
} }
init( init(
composingBuffer: String, cursorIndex: Int, candidates: [(String, String)], isTypingVertical: Bool, displayedText: String, cursorIndex: Int, candidates: [(String, String)],
nodeValuesArray: [String] = [] nodeValuesArray: [String] = []
) { ) {
self.candidates = candidates self.candidates = candidates
self.isTypingVertical = isTypingVertical super.init(displayedText: displayedText, cursorIndex: cursorIndex, nodeValuesArray: nodeValuesArray)
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
} }
} }
@ -521,34 +458,31 @@ public enum InputState {
/// .SymbolTable: /// .SymbolTable:
class SymbolTable: ChoosingCandidate { class SymbolTable: ChoosingCandidate {
override public var type: StateType { .ofSymbolTable } 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 self.node = node
if let previous = previous { if let previous = previous {
self.node.previous = previous self.node.previous = previous
} }
let candidates = node.children?.map(\.title) ?? [String]() let candidates = node.children?.map(\.title) ?? [String]()
super.init( self.candidates = candidates.map { ("", $0) }
composingBuffer: "", cursorIndex: 0, candidates: candidates.map { ("", $0) },
isTypingVertical: isTypingVertical
)
}
// InputState.SymbolTable // InputState.SymbolTable
// MS Word // MS Word
// //
// //
// Crediting Qwertyyb: https://github.com/qwertyyb/Fire/issues/55#issuecomment-1133497700 // Crediting Qwertyyb: https://github.com/qwertyyb/Fire/issues/55#issuecomment-1133497700
override var attributedString: NSMutableAttributedString { attributedString = {
let attributedString = NSMutableAttributedString( let attributedString = NSMutableAttributedString(
string: " ", string: " ",
attributes: [ attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue, .underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0, .markedClauseSegment: 0,
] ]
) )
return attributedString return attributedString
}()
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -117,6 +117,8 @@
5BEDB724283B4C250078EB25 /* data-symbols.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71E283B4AEA0078EB25 /* data-symbols.plist */; }; 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 */; }; 5BEDB725283B4C250078EB25 /* data-chs.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71C283B4AEA0078EB25 /* data-chs.plist */; };
5BF0B84C28C070B000795FC6 /* NSEventExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */; }; 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 */; }; 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 */; }; 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 */; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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; }; 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; }; 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 */, 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */,
5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */, 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */,
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */, 5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */,
5BF56F9728C39A2700DD6839 /* IMEState.swift */,
5BF56F9928C39D1800DD6839 /* IMEStateData.swift */,
D461B791279DAC010070E734 /* InputState.swift */, D461B791279DAC010070E734 /* InputState.swift */,
5BD0113C2818543900609769 /* KeyHandler_Core.swift */, 5BD0113C2818543900609769 /* KeyHandler_Core.swift */,
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */, 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
@ -1206,6 +1212,7 @@
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */, 5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */,
5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */, 5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */,
D461B792279DAC010070E734 /* InputState.swift in Sources */, D461B792279DAC010070E734 /* InputState.swift in Sources */,
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */,
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */, 5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */,
D47B92C027972AD100458394 /* main.swift in Sources */, D47B92C027972AD100458394 /* main.swift in Sources */,
D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */, D4A13D5A27A59F0B003BE359 /* ctlInputMethod_Core.swift in Sources */,
@ -1232,6 +1239,7 @@
D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */, D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */,
5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */, 5BD0113D2818543900609769 /* KeyHandler_Core.swift in Sources */,
5B2170E4289FACAD00BE7304 /* 2_Walker.swift in Sources */, 5B2170E4289FACAD00BE7304 /* 2_Walker.swift in Sources */,
5BF56F9A28C39D1800DD6839 /* IMEStateData.swift in Sources */,
5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */, 5BA9FD4227FEF3C8002DE248 /* PreferencePane.swift in Sources */,
5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */, 5BA0DF312817857D009E73BB /* lmUserOverride.swift in Sources */,
5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */, 5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */,