vChewing-macOS/Source/Modules/ControllerModules/IMEState.swift

210 lines
8.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// (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: String {
case ofDeactivated = "Deactivated"
case ofEmpty = "Empty"
case ofAbortion = "Abortion" // Empty
case ofCommitting = "Committing"
case ofAssociates = "Associates"
case ofNotEmpty = "NotEmpty"
case ofInputting = "Inputting"
case ofMarking = "Marking"
case ofCandidates = "Candidates"
case ofSymbolTable = "SymbolTable"
}
// IMEState
public protocol IMEStateProtocol {
var type: StateType { get }
var data: StateData { get }
var candidates: [(String, String)] { get }
var hasComposition: Bool { get }
var isCandidateContainer: Bool { get }
var displayedText: String { get }
var textToCommit: String { get set }
var tooltip: String { get set }
var attributedString: NSAttributedString { get }
var convertedToInputting: IMEState { get }
var isFilterable: Bool { get }
var isMarkedLengthValid: Bool { get }
var node: SymbolNode { get set }
}
/// ctlInputMethod
///
/// Finite State Machine/
/// 使
/// 使
///
///
/// IMEState
/// 使
///
///
///
/// IMEState
/// IMEState.ofMarking IMEState.ofInputting
///
/// (Constructor)
///
///
///
/// - .Deactivated: 使使
/// - .AssociatedPhrases:
/// 西 .NotEmpty
/// - .Empty: 使
///
/// - .Abortion: Empty
/// .Empty()
/// - .Committing:
/// - .NotEmpty:
/// - .Inputting: 使Compositor
/// - .Marking: 使
///
/// - .ChoosingCandidate: 使
/// - .SymbolTable:
public struct IMEState: IMEStateProtocol {
public var type: StateType = .ofEmpty
public var data: StateData = .init()
public var node: SymbolNode = .init("")
init(_ data: StateData = .init(), type: StateType = .ofEmpty) {
self.data = data
self.type = type
}
init(_ data: StateData = .init(), type: StateType = .ofEmpty, node: SymbolNode) {
self.data = data
self.type = type
self.node = node
self.data.candidates = { node.children?.map(\.title) ?? [String]() }().map { ("", $0) }
}
}
// MARK: -
extension IMEState {
public static func ofDeactivated() -> IMEState { .init(type: .ofDeactivated) }
public static func ofEmpty() -> IMEState { .init(type: .ofEmpty) }
public static func ofAbortion() -> IMEState { .init(type: .ofAbortion) }
public static func ofCommitting(textToCommit: String) -> IMEState {
var result = IMEState(type: .ofCommitting)
result.data.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &result.data.textToCommit)
return result
}
public static func ofAssociates(candidates: [(String, String)]) -> IMEState {
var result = IMEState(type: .ofAssociates)
result.data.candidates = candidates
return result
}
public static func ofNotEmpty(displayTextSegments: [String], cursor: Int) -> IMEState {
var result = IMEState(type: .ofNotEmpty)
// displayTextSegments
result.data.displayTextSegments = displayTextSegments
result.data.cursor = cursor
result.data.marker = cursor
return result
}
public static func ofInputting(displayTextSegments: [String], cursor: Int) -> IMEState {
var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofInputting
return result
}
public static func ofMarking(
displayTextSegments: [String], markedReadings: [String], cursor: Int, marker: Int
)
-> IMEState
{
var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofMarking
result.data.marker = marker
result.data.markedReadings = markedReadings
StateData.Marking.updateParameters(&result.data)
return result
}
public static func ofCandidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int)
-> IMEState
{
var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
result.type = .ofCandidates
result.data.candidates = candidates
return result
}
public static func ofSymbolTable(node: SymbolNode) -> IMEState {
var result = IMEState(type: .ofNotEmpty, node: node)
result.type = .ofSymbolTable
return result
}
}
// MARK: -
extension IMEState {
public var isFilterable: Bool { data.isFilterable }
public var isMarkedLengthValid: Bool { data.isMarkedLengthValid }
public var candidates: [(String, String)] { data.candidates }
public var convertedToInputting: IMEState {
if type == .ofInputting { return self }
var result = IMEState.ofInputting(displayTextSegments: data.displayTextSegments, 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 hasComposition: 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 }
}