Repo // Move IMEState-related protocols to Shared package.

This commit is contained in:
ShikiSuen 2022-09-23 23:03:12 +08:00
parent cab7eb28f7
commit 1648152b24
10 changed files with 160 additions and 108 deletions

View File

@ -0,0 +1,64 @@
// (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 Cocoa
// IMEState
public protocol IMEStateProtocol {
var type: StateType { get }
var data: StateDataProtocol { get }
var isASCIIMode: Bool { get set }
var isVerticalTyping: Bool { get set }
var isVerticalCandidateWindow: Bool { get set }
var candidates: [(String, String)] { get set }
var hasComposition: Bool { get }
var isCandidateContainer: Bool { get }
var displayedText: String { get }
var displayedTextConverted: String { get }
var textToCommit: String { get set }
var tooltip: String { get set }
var attributedString: NSAttributedString { get }
var convertedToInputting: IMEStateProtocol { get }
var isFilterable: Bool { get }
var isMarkedLengthValid: Bool { get }
var node: CandidateNode { get set }
var displayTextSegments: [String] { get }
var tooltipBackupForInputting: String { get set }
var markedRange: Range<Int> { get }
var u16MarkedRange: Range<Int> { get }
var u16Cursor: Int { get }
var cursor: Int { get set }
var marker: Int { get set }
}
public protocol StateDataProtocol {
var cursor: Int { get set }
var marker: Int { get set }
var markedRange: Range<Int> { get }
var u16MarkedRange: Range<Int> { get }
var u16Cursor: Int { get }
var textToCommit: String { get set }
var markedReadings: [String] { get set }
var displayTextSegments: [String] { get set }
var isFilterable: Bool { get }
var isVerticalTyping: Bool { get set }
var isMarkedLengthValid: Bool { get }
var candidates: [(String, String)] { get set }
var displayedText: String { get set }
var displayedTextConverted: String { get }
var tooltipBackupForInputting: String { get set }
var tooltip: String { get set }
var attributedStringNormal: NSAttributedString { get }
var attributedStringMarking: NSAttributedString { get }
var attributedStringPlaceholder: NSAttributedString { get }
var userPhraseDumped: String { get }
var userPhraseDumpedConverted: String { get }
var doesUserPhraseExist: Bool { get }
var tooltipColorState: TooltipColorState { get set }
mutating func updateTooltipForMarking()
}

View File

@ -103,6 +103,33 @@ public enum UserDef: String, CaseIterable {
} }
} }
// MARK: - Tooltip Color States
public enum TooltipColorState {
case normal
case redAlert
case warning
case denialOverflow
case denialInsufficiency
case prompt
}
// MARK: - IMEState types.
// 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"
}
// MARK: - Parser for Syllable composer // MARK: - Parser for Syllable composer
public enum KeyboardParser: Int, CaseIterable { public enum KeyboardParser: Int, CaseIterable {

View File

@ -15,6 +15,7 @@ let package = Package(
dependencies: [ dependencies: [
.package(path: "../Fuziki_NSAttributedTextView"), .package(path: "../Fuziki_NSAttributedTextView"),
.package(path: "../vChewing_CocoaExtension"), .package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_Shared"),
], ],
targets: [ targets: [
.target( .target(
@ -22,6 +23,7 @@ let package = Package(
dependencies: [ dependencies: [
.product(name: "NSAttributedTextView", package: "Fuziki_NSAttributedTextView"), .product(name: "NSAttributedTextView", package: "Fuziki_NSAttributedTextView"),
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"), .product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "Shared", package: "vChewing_Shared"),
] ]
) )
] ]

View File

@ -9,17 +9,9 @@
import Cocoa import Cocoa
import CocoaExtension import CocoaExtension
import NSAttributedTextView import NSAttributedTextView
import Shared
public class TooltipUI: NSWindowController { public class TooltipUI: NSWindowController {
public enum ColorStates {
case normal
case redAlert
case warning
case denialOverflow
case denialInsufficiency
case prompt
}
private var messageText: NSAttributedTooltipTextView private var messageText: NSAttributedTooltipTextView
private var tooltip: String = "" { private var tooltip: String = "" {
didSet { didSet {
@ -68,7 +60,7 @@ public class TooltipUI: NSWindowController {
set(windowTopLeftPoint: point, bottomOutOfScreenAdjustmentHeight: heightDelta) set(windowTopLeftPoint: point, bottomOutOfScreenAdjustmentHeight: heightDelta)
} }
public func setColor(state: ColorStates) { public func setColor(state: TooltipColorState) {
var backgroundColor = NSColor.controlBackgroundColor var backgroundColor = NSColor.controlBackgroundColor
var textColor = NSColor.textColor var textColor = NSColor.textColor
switch state { switch state {

View File

@ -7,47 +7,7 @@
// requirements defined in MIT License. // requirements defined in MIT License.
import LangModelAssembly import LangModelAssembly
import Shared
// 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 isASCIIMode: Bool { get set }
var isVerticalTyping: Bool { get set }
var isVerticalCandidateWindow: Bool { get set }
var candidates: [(String, String)] { get }
var hasComposition: Bool { get }
var isCandidateContainer: Bool { get }
var displayedText: String { get }
var displayedTextConverted: 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: CandidateNode { get set }
var displayTextSegments: [String] { get }
var tooltipBackupForInputting: String { get set }
var markedRange: Range<Int> { get }
var cursor: Int { get }
var u16MarkedRange: Range<Int> { get }
var u16Cursor: Int { get }
}
/// ctlInputMethod /// ctlInputMethod
/// ///
@ -82,17 +42,17 @@ public protocol IMEStateProtocol {
/// - .SymbolTable: /// - .SymbolTable:
public struct IMEState: IMEStateProtocol { public struct IMEState: IMEStateProtocol {
public var type: StateType = .ofEmpty public var type: StateType = .ofEmpty
public var data: StateData = .init() public var data: StateDataProtocol = StateData() as StateDataProtocol
public var node: CandidateNode = .init(name: "") public var node: CandidateNode = .init(name: "")
public var isASCIIMode = false public var isASCIIMode = false
public var isVerticalCandidateWindow = false public var isVerticalCandidateWindow = false
init(_ data: StateData = .init(), type: StateType = .ofEmpty) { init(_ data: StateDataProtocol = StateData() as StateDataProtocol, type: StateType = .ofEmpty) {
self.data = data self.data = data
self.type = type self.type = type
isVerticalTyping = ctlInputMethod.isVerticalTyping isVerticalTyping = ctlInputMethod.isVerticalTyping
} }
init(_ data: StateData = .init(), type: StateType = .ofEmpty, node: CandidateNode) { init(_ data: StateDataProtocol = StateData() as StateDataProtocol, type: StateType = .ofEmpty, node: CandidateNode) {
self.data = data self.data = data
self.type = type self.type = type
self.node = node self.node = node
@ -108,14 +68,14 @@ extension IMEState {
public static func ofAbortion() -> IMEState { .init(type: .ofAbortion) } public static func ofAbortion() -> IMEState { .init(type: .ofAbortion) }
public static func ofCommitting(textToCommit: String) -> IMEState { public static func ofCommitting(textToCommit: String) -> IMEState {
var result = IMEState(type: .ofCommitting) var result = IMEState(type: .ofCommitting)
result.data.textToCommit = textToCommit result.textToCommit = textToCommit
ChineseConverter.ensureCurrencyNumerals(target: &result.data.textToCommit) ChineseConverter.ensureCurrencyNumerals(target: &result.data.textToCommit)
return result return result
} }
public static func ofAssociates(candidates: [(String, String)]) -> IMEState { public static func ofAssociates(candidates: [(String, String)]) -> IMEState {
var result = IMEState(type: .ofAssociates) var result = IMEState(type: .ofAssociates)
result.data.candidates = candidates result.candidates = candidates
return result return result
} }
@ -175,15 +135,24 @@ extension IMEState {
extension IMEState { extension IMEState {
public var isFilterable: Bool { data.isFilterable } public var isFilterable: Bool { data.isFilterable }
public var isMarkedLengthValid: Bool { data.isMarkedLengthValid } public var isMarkedLengthValid: Bool { data.isMarkedLengthValid }
public var candidates: [(String, String)] { data.candidates }
public var displayedText: String { data.displayedText } public var displayedText: String { data.displayedText }
public var displayedTextConverted: String { data.displayedTextConverted } public var displayedTextConverted: String { data.displayedTextConverted }
public var cursor: Int { data.cursor }
public var displayTextSegments: [String] { data.displayTextSegments } public var displayTextSegments: [String] { data.displayTextSegments }
public var markedRange: Range<Int> { data.markedRange } public var markedRange: Range<Int> { data.markedRange }
public var u16MarkedRange: Range<Int> { data.u16MarkedRange } public var u16MarkedRange: Range<Int> { data.u16MarkedRange }
public var u16Cursor: Int { data.u16Cursor } public var u16Cursor: Int { data.u16Cursor }
public var convertedToInputting: IMEState {
public var cursor: Int {
get { data.cursor }
set { data.cursor = newValue }
}
public var marker: Int {
get { data.marker }
set { data.marker = newValue }
}
public var convertedToInputting: IMEStateProtocol {
if type == .ofInputting { return self } if type == .ofInputting { return self }
var result = IMEState.ofInputting(displayTextSegments: data.displayTextSegments, cursor: data.cursor) var result = IMEState.ofInputting(displayTextSegments: data.displayTextSegments, cursor: data.cursor)
result.tooltip = data.tooltipBackupForInputting result.tooltip = data.tooltipBackupForInputting
@ -191,22 +160,19 @@ extension IMEState {
return result return result
} }
public var candidates: [(String, String)] {
get { data.candidates }
set { data.candidates = newValue }
}
public var textToCommit: String { public var textToCommit: String {
get { get { data.textToCommit }
data.textToCommit set { data.textToCommit = newValue }
}
set {
data.textToCommit = newValue
}
} }
public var tooltip: String { public var tooltip: String {
get { get { data.tooltip }
data.tooltip set { data.tooltip = newValue }
}
set {
data.tooltip = newValue
}
} }
public var attributedString: NSAttributedString { public var attributedString: NSAttributedString {
@ -237,11 +203,7 @@ extension IMEState {
} }
public var tooltipBackupForInputting: String { public var tooltipBackupForInputting: String {
get { get { data.tooltipBackupForInputting }
data.tooltipBackupForInputting set { data.tooltipBackupForInputting = newValue }
}
set {
data.tooltipBackupForInputting = newValue
}
} }
} }

View File

@ -6,10 +6,11 @@
// marks, or product names of Contributor, except as required to fulfill notice // marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License. // requirements defined in MIT License.
import Shared
import Tekkon import Tekkon
import TooltipUI import TooltipUI
public struct StateData { public struct StateData: StateDataProtocol {
private static var minCandidateLength: Int { private static var minCandidateLength: Int {
PrefMgr.shared.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2 PrefMgr.shared.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2
} }
@ -18,8 +19,8 @@ public struct StateData {
Self.minCandidateLength...PrefMgr.shared.maxCandidateLength Self.minCandidateLength...PrefMgr.shared.maxCandidateLength
} }
var displayedText: String = "" public var displayedText: String = ""
var displayedTextConverted: String { public var displayedTextConverted: String {
/// ///
var result = ChineseConverter.kanjiConversionIfRequired(displayedText) var result = ChineseConverter.kanjiConversionIfRequired(displayedText)
if result.utf16.count != displayedText.utf16.count if result.utf16.count != displayedText.utf16.count
@ -32,19 +33,19 @@ public struct StateData {
// MARK: Cursor & Marker & Range for UTF8 // MARK: Cursor & Marker & Range for UTF8
var cursor: Int = 0 { public var cursor: Int = 0 {
didSet { didSet {
cursor = min(max(cursor, 0), displayedText.count) cursor = min(max(cursor, 0), displayedText.count)
} }
} }
var marker: Int = 0 { public var marker: Int = 0 {
didSet { didSet {
marker = min(max(marker, 0), displayedText.count) marker = min(max(marker, 0), displayedText.count)
} }
} }
var markedRange: Range<Int> { public var markedRange: Range<Int> {
min(cursor, marker)..<max(cursor, marker) min(cursor, marker)..<max(cursor, marker)
} }
@ -53,58 +54,58 @@ public struct StateData {
/// IMK UTF8 emoji /// IMK UTF8 emoji
/// Swift.utf16NSString.length() /// Swift.utf16NSString.length()
/// ///
var u16Cursor: Int { public var u16Cursor: Int {
displayedText.charComponents[0..<cursor].joined().utf16.count displayedText.charComponents[0..<cursor].joined().utf16.count
} }
var u16Marker: Int { public var u16Marker: Int {
displayedText.charComponents[0..<marker].joined().utf16.count displayedText.charComponents[0..<marker].joined().utf16.count
} }
var u16MarkedRange: Range<Int> { public var u16MarkedRange: Range<Int> {
min(u16Cursor, u16Marker)..<max(u16Cursor, u16Marker) min(u16Cursor, u16Marker)..<max(u16Cursor, u16Marker)
} }
// MARK: Other data for non-empty states. // MARK: Other data for non-empty states.
var isVerticalTyping = false public var isVerticalTyping = false
var markedTargetExists: Bool { public var markedTargetExists: Bool {
let pair = userPhraseKVPair let pair = userPhraseKVPair
return LMMgr.checkIfUserPhraseExist( return LMMgr.checkIfUserPhraseExist(
userPhrase: pair.1, mode: IMEApp.currentInputMode, key: pair.0 userPhrase: pair.1, mode: IMEApp.currentInputMode, key: pair.0
) )
} }
var displayTextSegments = [String]() { public var displayTextSegments = [String]() {
didSet { didSet {
displayedText = displayTextSegments.joined() displayedText = displayTextSegments.joined()
} }
} }
var reading: String = "" public var reading: String = ""
var markedReadings = [String]() public var markedReadings = [String]()
var candidates = [(String, String)]() public var candidates = [(String, String)]()
var textToCommit: String = "" public var textToCommit: String = ""
var tooltip: String = "" public var tooltip: String = ""
var tooltipBackupForInputting: String = "" public var tooltipBackupForInputting: String = ""
var attributedStringPlaceholder: NSAttributedString = .init( public var attributedStringPlaceholder: NSAttributedString = .init(
string: " ", string: " ",
attributes: [ attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue, .underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0, .markedClauseSegment: 0,
] ]
) )
var isFilterable: Bool { public var isFilterable: Bool {
markedTargetExists ? isMarkedLengthValid : false markedTargetExists ? isMarkedLengthValid : false
} }
var isMarkedLengthValid: Bool { public var isMarkedLengthValid: Bool {
Self.allowedMarkLengthRange.contains(markedRange.count) Self.allowedMarkLengthRange.contains(markedRange.count)
} }
var tooltipColorState: TooltipUI.ColorStates = .normal public var tooltipColorState: TooltipColorState = .normal
var attributedStringNormal: NSAttributedString { public var attributedStringNormal: NSAttributedString {
/// ///
/// JIS /// JIS
let attributedString = NSMutableAttributedString(string: displayedTextConverted) let attributedString = NSMutableAttributedString(string: displayedTextConverted)
@ -122,7 +123,7 @@ public struct StateData {
return attributedString return attributedString
} }
var attributedStringMarking: NSAttributedString { public var attributedStringMarking: NSAttributedString {
/// ///
/// JIS /// JIS
let attributedString = NSMutableAttributedString(string: displayedTextConverted) let attributedString = NSMutableAttributedString(string: displayedTextConverted)
@ -161,7 +162,7 @@ public struct StateData {
// MARK: - IMEState // MARK: - IMEState
extension StateData { extension StateData {
var chkIfUserPhraseExists: Bool { public var doesUserPhraseExist: Bool {
let text = displayedText.charComponents[markedRange].joined() let text = displayedText.charComponents[markedRange].joined()
let joined = markedReadings.joined(separator: "-") let joined = markedReadings.joined(separator: "-")
return LMMgr.checkIfUserPhraseExist( return LMMgr.checkIfUserPhraseExist(
@ -169,7 +170,7 @@ extension StateData {
) )
} }
var readingThreadForDisplay: String { public var readingThreadForDisplay: String {
var arrOutput = [String]() var arrOutput = [String]()
for neta in markedReadings { for neta in markedReadings {
var neta = neta var neta = neta
@ -193,19 +194,19 @@ extension StateData {
return arrOutput.joined(separator: "\u{A0}") return arrOutput.joined(separator: "\u{A0}")
} }
var userPhraseKVPair: (String, String) { public var userPhraseKVPair: (String, String) {
let key = markedReadings.joined(separator: "-") let key = markedReadings.joined(separator: "-")
let value = displayedText.charComponents[markedRange].joined() let value = displayedText.charComponents[markedRange].joined()
return (key, value) return (key, value)
} }
var userPhraseDumped: String { public var userPhraseDumped: String {
let pair = userPhraseKVPair let pair = userPhraseKVPair
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : "" let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
return "\(pair.1) \(pair.0)\(nerfedScore)" return "\(pair.1) \(pair.0)\(nerfedScore)"
} }
var userPhraseDumpedConverted: String { public var userPhraseDumpedConverted: String {
let pair = userPhraseKVPair let pair = userPhraseKVPair
let text = ChineseConverter.crossConvert(pair.1) ?? "" let text = ChineseConverter.crossConvert(pair.1) ?? ""
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : "" let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
@ -213,7 +214,7 @@ extension StateData {
return "\(text) \(pair.0)\(nerfedScore)\t\(convertedMark)" return "\(text) \(pair.0)\(nerfedScore)\t\(convertedMark)"
} }
mutating func updateTooltipForMarking() { public mutating func updateTooltipForMarking() {
var tooltipForMarking: String { var tooltipForMarking: String {
let pair = userPhraseKVPair let pair = userPhraseKVPair
if PrefMgr.shared.phraseReplacementEnabled { if PrefMgr.shared.phraseReplacementEnabled {

View File

@ -6,6 +6,8 @@
// marks, or product names of Contributor, except as required to fulfill notice // marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License. // requirements defined in MIT License.
import Shared
public class ctlPopupCompositionBuffer: NSWindowController { public class ctlPopupCompositionBuffer: NSWindowController {
public var isTypingDirectionVertical = false { public var isTypingDirectionVertical = false {
didSet { didSet {

View File

@ -32,7 +32,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
(inputMode == .imeModeCHT) ? .imeModeCHS : .imeModeCHT (inputMode == .imeModeCHT) ? .imeModeCHS : .imeModeCHT
if !LMMgr.writeUserPhrase( if !LMMgr.writeUserPhrase(
state.data.userPhraseDumped, inputMode: inputMode, state.data.userPhraseDumped, inputMode: inputMode,
areWeDuplicating: state.data.chkIfUserPhraseExists, areWeDuplicating: state.data.doesUserPhraseExist,
areWeDeleting: addToFilter areWeDeleting: addToFilter
) )
|| !LMMgr.writeUserPhrase( || !LMMgr.writeUserPhrase(

View File

@ -8,6 +8,8 @@
// marks, or product names of Contributor, except as required to fulfill notice // marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License. // requirements defined in MIT License.
import Shared
// MARK: - 調 (State Handling) // MARK: - 調 (State Handling)
extension ctlInputMethod { extension ctlInputMethod {