vChewing-macOS/Packages/vChewing_Shared/Sources/Shared/Shared.swift

357 lines
14 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) 2022 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
import SwiftExtension
// MARK: - UserDef Snapshot Manager
public enum UserDef: String, CaseIterable {
case kIsDebugModeEnabled = "_DebugMode"
case kFailureFlagForUOMObservation = "_FailureFlag_UOMObservation"
case kFailureFlagForIMKCandidates = "_FailureFlag_IMKCandidates"
case kDeltaOfCalendarYears = "DeltaOfCalendarYears"
case kMostRecentInputMode = "MostRecentInputMode"
case kCassettePath = "CassettePath"
case kUserDataFolderSpecified = "UserDataFolderSpecified"
case kCheckUpdateAutomatically = "CheckUpdateAutomatically"
case kUseExternalFactoryDict = "UseExternalFactoryDict"
case kKeyboardParser = "KeyboardParser"
case kBasicKeyboardLayout = "BasicKeyboardLayout"
case kAlphanumericalKeyboardLayout = "AlphanumericalKeyboardLayout"
case kShowNotificationsWhenTogglingCapsLock = "ShowNotificationsWhenTogglingCapsLock"
case kCandidateListTextSize = "CandidateListTextSize"
case kCandidateWindowShowOnlyOneLine = "CandidateWindowShowOnlyOneLine"
case kAppleLanguages = "AppleLanguages"
case kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles"
case kUseRearCursorMode = "UseRearCursorMode"
case kUseHorizontalCandidateList = "UseHorizontalCandidateList"
case kChooseCandidateUsingSpace = "ChooseCandidateUsingSpace"
case kCassetteEnabled = "CassetteEnabled"
case kCNS11643Enabled = "CNS11643Enabled"
case kSymbolInputEnabled = "SymbolInputEnabled"
case kChineseConversionEnabled = "ChineseConversionEnabled"
case kShiftJISShinjitaiOutputEnabled = "ShiftJISShinjitaiOutputEnabled"
case kCurrencyNumeralsEnabled = "CurrencyNumeralsEnabled"
case kHalfWidthPunctuationEnabled = "HalfWidthPunctuationEnable"
case kMoveCursorAfterSelectingCandidate = "MoveCursorAfterSelectingCandidate"
case kEscToCleanInputBuffer = "EscToCleanInputBuffer"
case kSpecifyIntonationKeyBehavior = "SecifyIntonationKeyBehavior"
case kSpecifyShiftBackSpaceKeyBehavior = "SpecifyShiftBackSpaceKeyBehavior"
case kSpecifyShiftTabKeyBehavior = "SpecifyShiftTabKeyBehavior"
case kSpecifyShiftSpaceKeyBehavior = "SpecifyShiftSpaceKeyBehavior"
case kAllowBoostingSingleKanjiAsUserPhrase = "AllowBoostingSingleKanjiAsUserPhrase"
case kUseSCPCTypingMode = "UseSCPCTypingMode"
case kMaxCandidateLength = "MaxCandidateLength"
case kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep"
case kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer"
case kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin"
case kFetchSuggestionsFromUserOverrideModel = "FetchSuggestionsFromUserOverrideModel"
case kUseFixecCandidateOrderOnSelection = "UseFixecCandidateOrderOnSelection"
case kAutoCorrectReadingCombination = "AutoCorrectReadingCombination"
case kAlsoConfirmAssociatedCandidatesByEnter = "AlsoConfirmAssociatedCandidatesByEnter"
case kKeepReadingUponCompositionError = "KeepReadingUponCompositionError"
case kTogglingAlphanumericalModeWithLShift = "TogglingAlphanumericalModeWithLShift"
case kUpperCaseLetterKeyBehavior = "UpperCaseLetterKeyBehavior"
case kTogglingAlphanumericalModeWithRShift = "togglingAlphanumericalModeWithRShift"
case kConsolidateContextOnCandidateSelection = "ConsolidateContextOnCandidateSelection"
case kHardenVerticalPunctuations = "HardenVerticalPunctuations"
case kTrimUnfinishedReadingsOnCommit = "TrimUnfinishedReadingsOnCommit"
case kAlwaysShowTooltipTextsHorizontally = "AlwaysShowTooltipTextsHorizontally"
case kClientsIMKTextInputIncapable = "ClientsIMKTextInputIncapable"
case kOnlyLoadFactoryLangModelsIfNeeded = "OnlyLoadFactoryLangModelsIfNeeded"
case kShowTranslatedStrokesInCompositionBuffer = "ShowTranslatedStrokesInCompositionBuffer"
case kForceCassetteChineseConversion = "ForceCassetteChineseConversion"
case kShowReverseLookupInCandidateUI = "ShowReverseLookupInCandidateUI"
case kAutoCompositeWithLongestPossibleCassetteKey = "AutoCompositeWithLongestPossibleCassetteKey"
case kShareAlphanumericalModeStatusAcrossClients = "ShareAlphanumericalModeStatusAcrossClients"
case kPhraseEditorAutoReloadExternalModifications = "PhraseEditorAutoReloadExternalModifications"
case kClassicHaninKeyboardSymbolModeShortcutEnabled = "ClassicHaninKeyboardSymbolModeShortcutEnabled"
case kUseIMKCandidateWindow = "UseIMKCandidateWindow"
case kEnableSwiftUIForTDKCandidates = "EnableSwiftUIForTDKCandidates"
case kEnableMouseScrollingForTDKCandidatesCocoa = "EnableMouseScrollingForTDKCandidatesCocoa"
case kDisableSegmentedThickUnderlineInMarkingModeForManagedClients
= "DisableSegmentedThickUnderlineInMarkingModeForManagedClients"
case kCandidateTextFontName = "CandidateTextFontName"
case kCandidateKeys = "CandidateKeys"
case kAssociatedPhrasesEnabled = "AssociatedPhrasesEnabled"
case kPhraseReplacementEnabled = "PhraseReplacementEnabled"
case kUsingHotKeySCPC = "UsingHotKeySCPC"
case kUsingHotKeyAssociates = "UsingHotKeyAssociates"
case kUsingHotKeyCNS = "UsingHotKeyCNS"
case kUsingHotKeyKangXi = "UsingHotKeyKangXi"
case kUsingHotKeyJIS = "UsingHotKeyJIS"
case kUsingHotKeyHalfWidthASCII = "UsingHotKeyHalfWidthASCII"
case kUsingHotKeyCurrencyNumerals = "UsingHotKeyCurrencyNumerals"
case kUsingHotKeyCassette = "UsingHotKeyCassette"
case kUsingHotKeyRevLookup = "UsingHotKeyRevLookup"
public static func resetAll() {
UserDef.allCases.forEach {
UserDefaults.standard.removeObject(forKey: $0.rawValue)
}
}
public static func load(from snapshot: Snapshot) {
let data = snapshot.data
guard !data.isEmpty else { return }
UserDef.allCases.forEach {
UserDefaults.standard.set(data[$0.rawValue], forKey: $0.rawValue)
}
}
public struct Snapshot {
public var data: [String: Any] = [:]
public init() {
UserDef.allCases.forEach {
data[$0.rawValue] = UserDefaults.standard.object(forKey: $0.rawValue)
}
}
}
}
// MARK: - Tooltip Color States
public enum TooltipColorState {
case normal
case redAlert
case warning
case denialOverflow
case denialInsufficiency
case prompt
case succeeded
}
// MARK: - IMEState types.
/// enum
public enum StateType: String {
/// ** .ofDeactivated**: 使使使
case ofDeactivated = "Deactivated"
/// ** .ofEmpty**: 使
///
/// /
/// 使
///
case ofEmpty = "Empty"
/// ** .ofAbortion**: .ofEmpty()
/// .ofEmpty()
case ofAbortion = "Abortion"
/// ** .ofCommitting**:
/// .ofEmpty()
/// .ofEmpty()
case ofCommitting = "Committing"
/// ** .ofAssociates**:
case ofAssociates = "Associates"
/// ** .ofInputting**: 使Compositor
case ofInputting = "Inputting"
/// ** .ofMarking**: 使
///
case ofMarking = "Marking"
/// ** .ofCandidates**: 使
case ofCandidates = "Candidates"
/// ** .ofSymbolTable**:
case ofSymbolTable = "SymbolTable"
}
// MARK: - Parser for Syllable composer
public enum KeyboardParser: Int, CaseIterable {
case ofStandard = 0
case ofETen = 1
case ofIBM = 4
case ofMiTAC = 5
case ofSeigyou = 8
case ofFakeSeigyou = 6
case ofDachen26 = 7
case ofETen26 = 3
case ofHsu = 2
case ofStarlight = 9
case ofHanyuPinyin = 10
case ofSecondaryPinyin = 11
case ofYalePinyin = 12
case ofHualuoPinyin = 13
case ofUniversalPinyin = 14
public var localizedMenuName: String {
let rawString: String = {
switch self {
case .ofStandard: return "Dachen (Microsoft Standard / Wang / 01, etc.)"
case .ofETen: return "Eten Traditional"
case .ofIBM: return "IBM"
case .ofMiTAC: return "MiTAC"
case .ofSeigyou: return "Seigyou"
case .ofFakeSeigyou: return "Fake Seigyou"
case .ofDachen26: return "Dachen 26 (libChewing)"
case .ofETen26: return "Eten 26"
case .ofHsu: return "Hsu"
case .ofStarlight: return "Starlight"
case .ofHanyuPinyin: return "Hanyu Pinyin with Numeral Intonation"
case .ofSecondaryPinyin: return "Secondary Pinyin with Numeral Intonation"
case .ofYalePinyin: return "Yale Pinyin with Numeral Intonation"
case .ofHualuoPinyin: return "Hualuo Pinyin with Numeral Intonation"
case .ofUniversalPinyin: return "Universal Pinyin with Numeral Intonation"
}
}()
return NSLocalizedString(rawString, comment: "")
}
public var name: String {
switch self {
case .ofStandard:
return "Standard"
case .ofETen:
return "ETen"
case .ofHsu:
return "Hsu"
case .ofETen26:
return "ETen26"
case .ofIBM:
return "IBM"
case .ofMiTAC:
return "MiTAC"
case .ofFakeSeigyou:
return "FakeSeigyou"
case .ofDachen26:
return "Dachen26"
case .ofSeigyou:
return "Seigyou"
case .ofStarlight:
return "Starlight"
case .ofHanyuPinyin:
return "HanyuPinyin"
case .ofSecondaryPinyin:
return "SecondaryPinyin"
case .ofYalePinyin:
return "YalePinyin"
case .ofHualuoPinyin:
return "HualuoPinyin"
case .ofUniversalPinyin:
return "UniversalPinyin"
}
}
}
public enum CandidateKey {
public static var defaultKeys: String { suggestions[0] }
public static let suggestions: [String] = [
"123456", "123456789", "234567890", "QWERTYUIO", "QWERTASDF", "ASDFGHJKL", "ASDFZXCVB",
]
///
public enum ValidationError {
case noError
case invalidCharacters
case countMismatch
public var description: String {
switch self {
case .invalidCharacters:
return "- "
+ NSLocalizedString(
"Candidate keys can only contain printable ASCII characters like alphanumericals.",
comment: ""
) + "\n" + "- " + NSLocalizedString("Candidate keys cannot contain space.", comment: "")
case .countMismatch:
return "- "
+ NSLocalizedString(
"Minimum 6 candidate keys allowed.", comment: ""
) + "\n" + "- " + NSLocalizedString("Maximum 9 candidate keys allowed.", comment: "")
case .noError:
return ""
}
}
}
///
/// - Remark:
/// ```
/// .trimmingCharacters(in: .whitespacesAndNewlines).deduplicated
/// ```
/// - Parameter candidateKeys:
/// - Returns: nil
public static func validate(keys candidateKeys: String) -> String? {
var result = ValidationError.noError
charValidityCheck: for neta in candidateKeys {
if String(neta) == " " {
result = CandidateKey.ValidationError.invalidCharacters
break charValidityCheck
}
for subNeta in neta.unicodeScalars {
if !subNeta.isPrintableASCII {
result = CandidateKey.ValidationError.invalidCharacters
break charValidityCheck
}
}
}
if !(6 ... 9).contains(candidateKeys.count) {
result = CandidateKey.ValidationError.countMismatch
}
return result == ValidationError.noError ? nil : result.description
}
}
public func vCLog(_ strPrint: StringLiteralType) {
if UserDefaults.standard.bool(forKey: "_DebugMode") {
NSLog("vChewingDebug: %@", strPrint)
}
}
public enum Shared {
// Supported locales.
public static let arrSupportedLocales: [String] = ["en", "zh-Hant", "zh-Hans", "ja"]
// The type of input modes.
public enum InputMode: String, CaseIterable, Identifiable {
public var id: ObjectIdentifier { .init(rawValue as AnyObject) }
case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
case imeModeNULL = ""
public var reversed: Shared.InputMode {
switch self {
case .imeModeCHS:
return .imeModeCHT
case .imeModeCHT:
return .imeModeCHS
case .imeModeNULL:
return .imeModeNULL
}
}
public var localizedDescription: String { NSLocalizedString(description, comment: "") }
public var description: String {
switch self {
case .imeModeCHS:
return "Simplified Chinese"
case .imeModeCHT:
return "Traditional Chinese"
case .imeModeNULL:
return "Please select…"
}
}
}
}
// MARK: - Observable Object
@available(macOS 10.15, *)
public class FileObserveProject: ObservableObject, Equatable {
public static let shared = FileObserveProject()
@Published public var id = UUID().uuidString
public static func == (lhs: FileObserveProject, rhs: FileObserveProject) -> Bool { lhs.id == rhs.id }
public func touch() {
id = UUID().uuidString
}
}