Repo // Refactor APIs related to LM access and configs.
This commit is contained in:
parent
23ef3124d4
commit
586822c981
|
@ -30,26 +30,36 @@ public extension vChewingLM {
|
||||||
/// LMI 會根據需要分別載入原廠語言模組和其他個別的子語言模組。LMI 本身不會記錄這些
|
/// LMI 會根據需要分別載入原廠語言模組和其他個別的子語言模組。LMI 本身不會記錄這些
|
||||||
/// 語言模組的相關資料的存放位置,僅藉由參數來讀取相關訊息。
|
/// 語言模組的相關資料的存放位置,僅藉由參數來讀取相關訊息。
|
||||||
class LMInstantiator: LangModelProtocol {
|
class LMInstantiator: LangModelProtocol {
|
||||||
|
public struct Config {
|
||||||
|
public var isCassetteEnabled = false
|
||||||
|
public var isPhraseReplacementEnabled = false
|
||||||
|
public var isCNSEnabled = false
|
||||||
|
public var isSymbolEnabled = false
|
||||||
|
public var isSCPCEnabled = false
|
||||||
|
public var deltaOfCalendarYears: Int = -2000
|
||||||
|
}
|
||||||
|
|
||||||
// SQLite 連線所在的記憶體位置。
|
// SQLite 連線所在的記憶體位置。
|
||||||
static var ptrSQL: OpaquePointer?
|
static var ptrSQL: OpaquePointer?
|
||||||
|
|
||||||
// SQLite 連線是否已經建立。
|
// SQLite 連線是否已經建立。
|
||||||
public private(set) static var isSQLDBConnected: Bool = false
|
public private(set) static var isSQLDBConnected: Bool = false
|
||||||
|
|
||||||
|
// 簡體中文模型?
|
||||||
|
public let isCHS: Bool
|
||||||
|
|
||||||
// 在函式內部用以記錄狀態的開關。
|
// 在函式內部用以記錄狀態的開關。
|
||||||
public var isCassetteEnabled = false
|
public var config = Config()
|
||||||
public var isPhraseReplacementEnabled = false
|
|
||||||
public var isCNSEnabled = false
|
|
||||||
public var isSymbolEnabled = false
|
|
||||||
public var isSCPCEnabled = false
|
|
||||||
public var isCHS = false
|
|
||||||
public var deltaOfCalendarYears: Int = -2000
|
|
||||||
|
|
||||||
// 這句需要留著,不然無法被 package 外界存取。
|
// 這句需要留著,不然無法被 package 外界存取。
|
||||||
public init(isCHS: Bool = false) {
|
public init(isCHS: Bool = false) {
|
||||||
self.isCHS = isCHS
|
self.isCHS = isCHS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setOptions(handler: (inout Config) -> Void) {
|
||||||
|
handler(&config)
|
||||||
|
}
|
||||||
|
|
||||||
@discardableResult public static func connectSQLDB(dbPath: String, dropPreviousConnection: Bool = true) -> Bool {
|
@discardableResult public static func connectSQLDB(dbPath: String, dropPreviousConnection: Bool = true) -> Bool {
|
||||||
if dropPreviousConnection { disconnectSQLDB() }
|
if dropPreviousConnection { disconnectSQLDB() }
|
||||||
vCLog("Establishing SQLite connection to: \(dbPath)")
|
vCLog("Establishing SQLite connection to: \(dbPath)")
|
||||||
|
@ -315,10 +325,10 @@ public extension vChewingLM {
|
||||||
/// 準備不同的語言模組容器,開始逐漸往容器陣列內塞入資料。
|
/// 準備不同的語言模組容器,開始逐漸往容器陣列內塞入資料。
|
||||||
var rawAllUnigrams: [Megrez.Unigram] = []
|
var rawAllUnigrams: [Megrez.Unigram] = []
|
||||||
|
|
||||||
if isCassetteEnabled { rawAllUnigrams += Self.lmCassette.unigramsFor(key: keyChain) }
|
if config.isCassetteEnabled { rawAllUnigrams += Self.lmCassette.unigramsFor(key: keyChain) }
|
||||||
|
|
||||||
// 如果有檢測到使用者自訂逐字選字語料庫內的相關資料的話,在這裡先插入。
|
// 如果有檢測到使用者自訂逐字選字語料庫內的相關資料的話,在這裡先插入。
|
||||||
if isSCPCEnabled {
|
if config.isSCPCEnabled {
|
||||||
rawAllUnigrams += lmPlainBopomofo.valuesFor(key: keyChain).map { Megrez.Unigram(value: $0, score: 0) }
|
rawAllUnigrams += lmPlainBopomofo.valuesFor(key: keyChain).map { Megrez.Unigram(value: $0, score: 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,18 +337,18 @@ public extension vChewingLM {
|
||||||
// 將兩句差分也是為了讓 rawUserUnigrams 的類型不受可能的影響。
|
// 將兩句差分也是為了讓 rawUserUnigrams 的類型不受可能的影響。
|
||||||
rawAllUnigrams += lmUserPhrases.unigramsFor(key: keyChain).reversed()
|
rawAllUnigrams += lmUserPhrases.unigramsFor(key: keyChain).reversed()
|
||||||
|
|
||||||
if !isCassetteEnabled || isCassetteEnabled && keyChain.map(\.description)[0] == "_" {
|
if !config.isCassetteEnabled || config.isCassetteEnabled && keyChain.map(\.description)[0] == "_" {
|
||||||
// LMMisc 與 LMCore 的 score 在 (-10.0, 0.0) 這個區間內。
|
// LMMisc 與 LMCore 的 score 在 (-10.0, 0.0) 這個區間內。
|
||||||
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataCHEW)
|
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataCHEW)
|
||||||
rawAllUnigrams += factoryCoreUnigramsFor(key: keyChain)
|
rawAllUnigrams += factoryCoreUnigramsFor(key: keyChain)
|
||||||
if isCNSEnabled {
|
if config.isCNSEnabled {
|
||||||
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataCNS)
|
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataCNS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSymbolEnabled {
|
if config.isSymbolEnabled {
|
||||||
rawAllUnigrams += lmUserSymbols.unigramsFor(key: keyChain)
|
rawAllUnigrams += lmUserSymbols.unigramsFor(key: keyChain)
|
||||||
if !isCassetteEnabled {
|
if !config.isCassetteEnabled {
|
||||||
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataSYMB)
|
rawAllUnigrams += factoryUnigramsFor(key: keyChain, column: .theDataSYMB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,7 +373,7 @@ public extension vChewingLM {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提前處理語彙置換。
|
// 提前處理語彙置換。
|
||||||
if isPhraseReplacementEnabled {
|
if config.isPhraseReplacementEnabled {
|
||||||
for i in 0 ..< rawAllUnigrams.count {
|
for i in 0 ..< rawAllUnigrams.count {
|
||||||
let newValue = lmReplacements.valuesFor(key: rawAllUnigrams[i].value)
|
let newValue = lmReplacements.valuesFor(key: rawAllUnigrams[i].value)
|
||||||
guard !newValue.isEmpty else { continue }
|
guard !newValue.isEmpty else { continue }
|
||||||
|
|
|
@ -19,14 +19,14 @@ extension vChewingLM.LMInstantiator {
|
||||||
|
|
||||||
func processDateWithDayDelta(_ delta: Int) {
|
func processDateWithDayDelta(_ delta: Int) {
|
||||||
tokens = ["MACRO@DATE_DAYDELTA:\(delta)"]
|
tokens = ["MACRO@DATE_DAYDELTA:\(delta)"]
|
||||||
if deltaOfCalendarYears != 0 { tokens.append("MACRO@DATE_DAYDELTA:\(delta)_YEARDELTA:\(deltaOfCalendarYears)") }
|
if config.deltaOfCalendarYears != 0 { tokens.append("MACRO@DATE_DAYDELTA:\(delta)_YEARDELTA:\(config.deltaOfCalendarYears)") }
|
||||||
tokens.append("MACRO@DATE_DAYDELTA:\(delta)_SHORTENED")
|
tokens.append("MACRO@DATE_DAYDELTA:\(delta)_SHORTENED")
|
||||||
tokens.append("MACRO@DATE_DAYDELTA:\(delta)_LUNA")
|
tokens.append("MACRO@DATE_DAYDELTA:\(delta)_LUNA")
|
||||||
}
|
}
|
||||||
|
|
||||||
func processYearWithYearDelta(_ delta: Int) {
|
func processYearWithYearDelta(_ delta: Int) {
|
||||||
tokens = ["MACRO@YEAR_YEARDELTA:\(delta)"]
|
tokens = ["MACRO@YEAR_YEARDELTA:\(delta)"]
|
||||||
if deltaOfCalendarYears != 0 { tokens.append("MACRO@YEAR_YEARDELTA:\(delta + deltaOfCalendarYears)") }
|
if config.deltaOfCalendarYears != 0 { tokens.append("MACRO@YEAR_YEARDELTA:\(delta + config.deltaOfCalendarYears)") }
|
||||||
tokens.append("MACRO@YEAR_GANZHI_YEARDELTA:\(delta)")
|
tokens.append("MACRO@YEAR_GANZHI_YEARDELTA:\(delta)")
|
||||||
tokens.append("MACRO@YEAR_ZODIAC_YEARDELTA:\(delta)")
|
tokens.append("MACRO@YEAR_ZODIAC_YEARDELTA:\(delta)")
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,10 @@ final class InputTokenTests: XCTestCase {
|
||||||
func testGeneratedResultsFromLMInstantiator() throws {
|
func testGeneratedResultsFromLMInstantiator() throws {
|
||||||
let instance = vChewingLM.LMInstantiator(isCHS: true)
|
let instance = vChewingLM.LMInstantiator(isCHS: true)
|
||||||
XCTAssertTrue(vChewingLM.LMInstantiator.connectToTestSQLDB())
|
XCTAssertTrue(vChewingLM.LMInstantiator.connectToTestSQLDB())
|
||||||
instance.isCNSEnabled = false
|
instance.setOptions { config in
|
||||||
instance.isSymbolEnabled = false
|
config.isCNSEnabled = false
|
||||||
|
config.isSymbolEnabled = false
|
||||||
|
}
|
||||||
instance.insertTemporaryData(
|
instance.insertTemporaryData(
|
||||||
keyArray: ["ㄐㄧㄣ", "ㄊㄧㄢ", "ㄖˋ", "ㄑㄧˊ"],
|
keyArray: ["ㄐㄧㄣ", "ㄊㄧㄢ", "ㄖˋ", "ㄑㄧˊ"],
|
||||||
unigram: .init(value: "MACRO@DATE_YEARDELTA:-1945", score: -97.5),
|
unigram: .init(value: "MACRO@DATE_YEARDELTA:-1945", score: -97.5),
|
||||||
|
|
|
@ -24,14 +24,18 @@ final class LMInstantiatorSQLTests: XCTestCase {
|
||||||
func testSQL() throws {
|
func testSQL() throws {
|
||||||
let instance = vChewingLM.LMInstantiator(isCHS: true)
|
let instance = vChewingLM.LMInstantiator(isCHS: true)
|
||||||
XCTAssertTrue(vChewingLM.LMInstantiator.connectToTestSQLDB())
|
XCTAssertTrue(vChewingLM.LMInstantiator.connectToTestSQLDB())
|
||||||
instance.isCNSEnabled = false
|
instance.setOptions { config in
|
||||||
instance.isSymbolEnabled = false
|
config.isCNSEnabled = false
|
||||||
|
config.isSymbolEnabled = false
|
||||||
|
}
|
||||||
XCTAssertEqual(instance.unigramsFor(keyArray: strBloatingKey).description, "[(吹牛逼,-7.375), (吹牛屄,-7.399)]")
|
XCTAssertEqual(instance.unigramsFor(keyArray: strBloatingKey).description, "[(吹牛逼,-7.375), (吹牛屄,-7.399)]")
|
||||||
XCTAssertEqual(instance.unigramsFor(keyArray: strHaninSymbolMenuKey)[1].description, "(,,-9.9)")
|
XCTAssertEqual(instance.unigramsFor(keyArray: strHaninSymbolMenuKey)[1].description, "(,,-9.9)")
|
||||||
XCTAssertEqual(instance.unigramsFor(keyArray: strRefutationKey).description, "[(㨃,-9.544)]")
|
XCTAssertEqual(instance.unigramsFor(keyArray: strRefutationKey).description, "[(㨃,-9.544)]")
|
||||||
XCTAssertEqual(instance.unigramsFor(keyArray: strBoobsKey).description, "[(ㄋㄟㄋㄟ,-1.0)]")
|
XCTAssertEqual(instance.unigramsFor(keyArray: strBoobsKey).description, "[(ㄋㄟㄋㄟ,-1.0)]")
|
||||||
instance.isCNSEnabled = true
|
instance.setOptions { config in
|
||||||
instance.isSymbolEnabled = true
|
config.isCNSEnabled = true
|
||||||
|
config.isSymbolEnabled = true
|
||||||
|
}
|
||||||
XCTAssertEqual(instance.unigramsFor(keyArray: strBloatingKey).last?.description, "(🌳🆕🐝,-13.0)")
|
XCTAssertEqual(instance.unigramsFor(keyArray: strBloatingKey).last?.description, "(🌳🆕🐝,-13.0)")
|
||||||
XCTAssertEqual(instance.unigramsFor(keyArray: strHaninSymbolMenuKey)[1].description, "(,,-9.9)")
|
XCTAssertEqual(instance.unigramsFor(keyArray: strHaninSymbolMenuKey)[1].description, "(,,-9.9)")
|
||||||
XCTAssertEqual(instance.unigramsFor(keyArray: strRefutationKey).count, 10)
|
XCTAssertEqual(instance.unigramsFor(keyArray: strRefutationKey).count, 10)
|
||||||
|
|
|
@ -12,50 +12,42 @@ import NotifierUI
|
||||||
import Shared
|
import Shared
|
||||||
import SwiftExtension
|
import SwiftExtension
|
||||||
|
|
||||||
|
// MARK: - Input Mode Extension for Language Models
|
||||||
|
|
||||||
|
public extension Shared.InputMode {
|
||||||
|
private static let lmCHS = vChewingLM.LMInstantiator(isCHS: true)
|
||||||
|
private static let lmCHT = vChewingLM.LMInstantiator(isCHS: false)
|
||||||
|
private static let uomCHS = vChewingLM.LMUserOverride(dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHS))
|
||||||
|
private static let uomCHT = vChewingLM.LMUserOverride(dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHT))
|
||||||
|
|
||||||
|
var langModel: vChewingLM.LMInstantiator {
|
||||||
|
switch self {
|
||||||
|
case .imeModeCHS: return Self.lmCHS
|
||||||
|
case .imeModeCHT: return Self.lmCHT
|
||||||
|
case .imeModeNULL: return .init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var uom: vChewingLM.LMUserOverride {
|
||||||
|
switch self {
|
||||||
|
case .imeModeCHS: return Self.uomCHS
|
||||||
|
case .imeModeCHT: return Self.uomCHT
|
||||||
|
case .imeModeNULL: return .init(dataURL: LMMgr.userOverrideModelDataURL(IMEApp.currentInputMode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Language Model Manager.
|
||||||
|
|
||||||
public class LMMgr {
|
public class LMMgr {
|
||||||
public static var shared = LMMgr()
|
public static var shared = LMMgr()
|
||||||
public private(set) static var lmCHS = vChewingLM.LMInstantiator(isCHS: true)
|
|
||||||
public private(set) static var lmCHT = vChewingLM.LMInstantiator(isCHS: false)
|
|
||||||
public private(set) static var uomCHS = vChewingLM.LMUserOverride(
|
|
||||||
dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHS))
|
|
||||||
public private(set) static var uomCHT = vChewingLM.LMUserOverride(
|
|
||||||
dataURL: LMMgr.userOverrideModelDataURL(.imeModeCHT))
|
|
||||||
|
|
||||||
public static var currentLM: vChewingLM.LMInstantiator {
|
|
||||||
Self.getLM(mode: IMEApp.currentInputMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static var currentUOM: vChewingLM.LMUserOverride {
|
|
||||||
Self.getUOM(mode: IMEApp.currentInputMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func getLM(mode: Shared.InputMode) -> vChewingLM.LMInstantiator {
|
|
||||||
switch mode {
|
|
||||||
case .imeModeCHS:
|
|
||||||
return Self.lmCHS
|
|
||||||
case .imeModeCHT:
|
|
||||||
return Self.lmCHT
|
|
||||||
case .imeModeNULL:
|
|
||||||
return .init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func getUOM(mode: Shared.InputMode) -> vChewingLM.LMUserOverride {
|
|
||||||
switch mode {
|
|
||||||
case .imeModeCHS:
|
|
||||||
return Self.uomCHS
|
|
||||||
case .imeModeCHT:
|
|
||||||
return Self.uomCHT
|
|
||||||
case .imeModeNULL:
|
|
||||||
return .init(dataURL: Self.userOverrideModelDataURL(IMEApp.currentInputMode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Functions reacting directly with language models.
|
// MARK: - Functions reacting directly with language models.
|
||||||
|
|
||||||
public static func initUserLangModels() {
|
public static func initUserLangModels() {
|
||||||
Self.chkUserLMFilesExist(.imeModeCHT)
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
Self.chkUserLMFilesExist(.imeModeCHS)
|
Self.chkUserLMFilesExist(mode)
|
||||||
|
}
|
||||||
// LMMgr 的 loadUserPhrases 等函式在自動讀取 dataFolderPath 時,
|
// LMMgr 的 loadUserPhrases 等函式在自動讀取 dataFolderPath 時,
|
||||||
// 如果發現自訂目錄不可用,則會自動抹去自訂目錄設定、改採預設目錄。
|
// 如果發現自訂目錄不可用,則會自動抹去自訂目錄設定、改採預設目錄。
|
||||||
// 所以這裡不需要特別處理。
|
// 所以這裡不需要特別處理。
|
||||||
|
@ -84,83 +76,69 @@ public class LMMgr {
|
||||||
|
|
||||||
public static func loadUserPhrasesData(type: vChewingLM.ReplacableUserDataType? = nil) {
|
public static func loadUserPhrasesData(type: vChewingLM.ReplacableUserDataType? = nil) {
|
||||||
guard let type = type else {
|
guard let type = type else {
|
||||||
Self.lmCHT.loadUserPhrasesData(
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
path: userDictDataURL(mode: .imeModeCHT, type: .thePhrases).path,
|
mode.langModel.loadUserPhrasesData(
|
||||||
filterPath: userDictDataURL(mode: .imeModeCHT, type: .theFilter).path
|
path: userDictDataURL(mode: mode, type: .thePhrases).path,
|
||||||
)
|
filterPath: userDictDataURL(mode: mode, type: .theFilter).path
|
||||||
Self.lmCHS.loadUserPhrasesData(
|
)
|
||||||
path: userDictDataURL(mode: .imeModeCHS, type: .thePhrases).path,
|
mode.langModel.loadUserSymbolData(path: userDictDataURL(mode: mode, type: .theSymbols).path)
|
||||||
filterPath: userDictDataURL(mode: .imeModeCHS, type: .theFilter).path
|
mode.uom.loadData(fromURL: userOverrideModelDataURL(mode))
|
||||||
)
|
}
|
||||||
Self.lmCHT.loadUserSymbolData(path: userDictDataURL(mode: .imeModeCHT, type: .theSymbols).path)
|
|
||||||
Self.lmCHS.loadUserSymbolData(path: userDictDataURL(mode: .imeModeCHS, type: .theSymbols).path)
|
|
||||||
|
|
||||||
if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() }
|
if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() }
|
||||||
if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() }
|
if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() }
|
||||||
if PrefMgr.shared.useSCPCTypingMode { Self.loadSCPCSequencesData() }
|
if PrefMgr.shared.useSCPCTypingMode { Self.loadSCPCSequencesData() }
|
||||||
|
|
||||||
Self.uomCHT.loadData(fromURL: userOverrideModelDataURL(.imeModeCHT))
|
|
||||||
Self.uomCHS.loadData(fromURL: userOverrideModelDataURL(.imeModeCHS))
|
|
||||||
|
|
||||||
CandidateNode.load(url: Self.userSymbolMenuDataURL())
|
CandidateNode.load(url: Self.userSymbolMenuDataURL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch type {
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
case .thePhrases:
|
switch type {
|
||||||
Self.lmCHT.loadUserPhrasesData(
|
case .thePhrases:
|
||||||
path: userDictDataURL(mode: .imeModeCHT, type: .thePhrases).path,
|
mode.langModel.loadUserPhrasesData(
|
||||||
filterPath: nil
|
path: userDictDataURL(mode: mode, type: .thePhrases).path,
|
||||||
)
|
filterPath: nil
|
||||||
Self.lmCHS.loadUserPhrasesData(
|
)
|
||||||
path: userDictDataURL(mode: .imeModeCHS, type: .thePhrases).path,
|
case .theFilter:
|
||||||
filterPath: nil
|
DispatchQueue.main.async {
|
||||||
)
|
Self.reloadUserFilterDirectly(mode: mode)
|
||||||
case .theFilter:
|
}
|
||||||
DispatchQueue.main.async {
|
case .theReplacements:
|
||||||
Self.reloadUserFilterDirectly(mode: IMEApp.currentInputMode)
|
if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() }
|
||||||
|
case .theAssociates:
|
||||||
|
if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() }
|
||||||
|
case .theSymbols:
|
||||||
|
mode.langModel.loadUserSymbolData(
|
||||||
|
path: Self.userDictDataURL(mode: mode, type: .theSymbols).path
|
||||||
|
)
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
|
||||||
Self.reloadUserFilterDirectly(mode: IMEApp.currentInputMode.reversed)
|
|
||||||
}
|
|
||||||
case .theReplacements:
|
|
||||||
if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() }
|
|
||||||
case .theAssociates:
|
|
||||||
if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() }
|
|
||||||
case .theSymbols:
|
|
||||||
Self.lmCHT.loadUserSymbolData(
|
|
||||||
path: Self.userDictDataURL(mode: .imeModeCHT, type: .theSymbols).path
|
|
||||||
)
|
|
||||||
Self.lmCHS.loadUserSymbolData(
|
|
||||||
path: Self.userDictDataURL(mode: .imeModeCHS, type: .theSymbols).path
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func loadUserAssociatesData() {
|
public static func loadUserAssociatesData() {
|
||||||
Self.lmCHT.loadUserAssociatesData(
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
path: Self.userDictDataURL(mode: .imeModeCHT, type: .theAssociates).path
|
mode.langModel.loadUserAssociatesData(
|
||||||
)
|
path: Self.userDictDataURL(mode: mode, type: .theAssociates).path
|
||||||
Self.lmCHS.loadUserAssociatesData(
|
)
|
||||||
path: Self.userDictDataURL(mode: .imeModeCHS, type: .theAssociates).path
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func loadUserPhraseReplacement() {
|
public static func loadUserPhraseReplacement() {
|
||||||
Self.lmCHT.loadReplacementsData(
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
path: Self.userDictDataURL(mode: .imeModeCHT, type: .theReplacements).path
|
mode.langModel.loadReplacementsData(
|
||||||
)
|
path: Self.userDictDataURL(mode: mode, type: .theReplacements).path
|
||||||
Self.lmCHS.loadReplacementsData(
|
)
|
||||||
path: Self.userDictDataURL(mode: .imeModeCHS, type: .theReplacements).path
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func loadSCPCSequencesData() {
|
public static func loadSCPCSequencesData() {
|
||||||
Self.lmCHT.loadSCPCSequencesData()
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
Self.lmCHS.loadSCPCSequencesData()
|
mode.langModel.loadSCPCSequencesData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func reloadUserFilterDirectly(mode: Shared.InputMode) {
|
public static func reloadUserFilterDirectly(mode: Shared.InputMode) {
|
||||||
Self.getLM(mode: mode).reloadUserFilterDirectly(path: userDictDataURL(mode: mode, type: .theFilter).path)
|
mode.langModel.reloadUserFilterDirectly(path: userDictDataURL(mode: mode, type: .theFilter).path)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func checkIfPhrasePairExists(
|
public static func checkIfPhrasePairExists(
|
||||||
|
@ -169,7 +147,7 @@ public class LMMgr {
|
||||||
keyArray: [String],
|
keyArray: [String],
|
||||||
factoryDictionaryOnly: Bool = false
|
factoryDictionaryOnly: Bool = false
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
Self.getLM(mode: mode).hasKeyValuePairFor(
|
mode.langModel.hasKeyValuePairFor(
|
||||||
keyArray: keyArray, value: userPhrase, factoryDictionaryOnly: factoryDictionaryOnly
|
keyArray: keyArray, value: userPhrase, factoryDictionaryOnly: factoryDictionaryOnly
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -179,7 +157,7 @@ public class LMMgr {
|
||||||
mode: Shared.InputMode,
|
mode: Shared.InputMode,
|
||||||
keyArray: [String]
|
keyArray: [String]
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
Self.getLM(mode: mode).isPairFiltered(pair: .init(keyArray: keyArray, value: userPhrase))
|
mode.langModel.isPairFiltered(pair: .init(keyArray: keyArray, value: userPhrase))
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func countPhrasePairs(
|
public static func countPhrasePairs(
|
||||||
|
@ -187,39 +165,22 @@ public class LMMgr {
|
||||||
mode: Shared.InputMode,
|
mode: Shared.InputMode,
|
||||||
factoryDictionaryOnly: Bool = false
|
factoryDictionaryOnly: Bool = false
|
||||||
) -> Int {
|
) -> Int {
|
||||||
Self.getLM(mode: mode).countKeyValuePairs(
|
mode.langModel.countKeyValuePairs(
|
||||||
keyArray: keyArray, factoryDictionaryOnly: factoryDictionaryOnly
|
keyArray: keyArray, factoryDictionaryOnly: factoryDictionaryOnly
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func setPhraseReplacementEnabled(_ state: Bool) {
|
public static func syncLMPrefs() {
|
||||||
Self.lmCHT.isPhraseReplacementEnabled = state
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
Self.lmCHS.isPhraseReplacementEnabled = state
|
mode.langModel.setOptions { config in
|
||||||
}
|
config.isPhraseReplacementEnabled = PrefMgr.shared.phraseReplacementEnabled
|
||||||
|
config.isCNSEnabled = PrefMgr.shared.cns11643Enabled
|
||||||
public static func setCNSEnabled(_ state: Bool) {
|
config.isSymbolEnabled = PrefMgr.shared.symbolInputEnabled
|
||||||
Self.lmCHT.isCNSEnabled = state
|
config.isSCPCEnabled = PrefMgr.shared.useSCPCTypingMode
|
||||||
Self.lmCHS.isCNSEnabled = state
|
config.isCassetteEnabled = PrefMgr.shared.cassetteEnabled
|
||||||
}
|
config.deltaOfCalendarYears = PrefMgr.shared.deltaOfCalendarYears
|
||||||
|
}
|
||||||
public static func setSymbolEnabled(_ state: Bool) {
|
}
|
||||||
Self.lmCHT.isSymbolEnabled = state
|
|
||||||
Self.lmCHS.isSymbolEnabled = state
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func setSCPCEnabled(_ state: Bool) {
|
|
||||||
Self.lmCHT.isSCPCEnabled = state
|
|
||||||
Self.lmCHS.isSCPCEnabled = state
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func setCassetteEnabled(_ state: Bool) {
|
|
||||||
Self.lmCHT.isCassetteEnabled = state
|
|
||||||
Self.lmCHS.isCassetteEnabled = state
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func setDeltaOfCalendarYears(_ delta: Int) {
|
|
||||||
Self.lmCHT.deltaOfCalendarYears = delta
|
|
||||||
Self.lmCHS.deltaOfCalendarYears = delta
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UOM
|
// MARK: UOM
|
||||||
|
@ -227,26 +188,23 @@ public class LMMgr {
|
||||||
public static func saveUserOverrideModelData() {
|
public static func saveUserOverrideModelData() {
|
||||||
let globalQueue = DispatchQueue(label: "vChewingLM_UOM", qos: .unspecified, attributes: .concurrent)
|
let globalQueue = DispatchQueue(label: "vChewingLM_UOM", qos: .unspecified, attributes: .concurrent)
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
group.enter()
|
Shared.InputMode.validCases.forEach { mode in
|
||||||
globalQueue.async {
|
group.enter()
|
||||||
Self.uomCHT.saveData(toURL: userOverrideModelDataURL(.imeModeCHT))
|
globalQueue.async {
|
||||||
group.leave()
|
mode.uom.saveData(toURL: userOverrideModelDataURL(mode))
|
||||||
}
|
group.leave()
|
||||||
group.enter()
|
}
|
||||||
globalQueue.async {
|
|
||||||
Self.uomCHS.saveData(toURL: userOverrideModelDataURL(.imeModeCHS))
|
|
||||||
group.leave()
|
|
||||||
}
|
}
|
||||||
_ = group.wait(timeout: .distantFuture)
|
_ = group.wait(timeout: .distantFuture)
|
||||||
group.notify(queue: DispatchQueue.main) {}
|
group.notify(queue: DispatchQueue.main) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func bleachSpecifiedSuggestions(targets: [String], mode: Shared.InputMode) {
|
public static func bleachSpecifiedSuggestions(targets: [String], mode: Shared.InputMode) {
|
||||||
Self.getUOM(mode: mode).bleachSpecifiedSuggestions(targets: targets, saveCallback: { Self.getUOM(mode: mode).saveData() })
|
mode.uom.bleachSpecifiedSuggestions(targets: targets, saveCallback: { mode.uom.saveData() })
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func removeUnigramsFromUserOverrideModel(_ mode: Shared.InputMode) {
|
public static func removeUnigramsFromUserOverrideModel(_ mode: Shared.InputMode) {
|
||||||
Self.getUOM(mode: mode).bleachUnigrams(saveCallback: { Self.getUOM(mode: mode).saveData() })
|
mode.uom.bleachUnigrams(saveCallback: { mode.uom.saveData() })
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func relocateWreckedUOMData() {
|
public static func relocateWreckedUOMData() {
|
||||||
|
@ -268,6 +226,6 @@ public class LMMgr {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func clearUserOverrideModelData(_ mode: Shared.InputMode = .imeModeNULL) {
|
public static func clearUserOverrideModelData(_ mode: Shared.InputMode = .imeModeNULL) {
|
||||||
Self.getUOM(mode: mode).clearData(withURL: userOverrideModelDataURL(mode))
|
mode.uom.clearData(withURL: userOverrideModelDataURL(mode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ public extension LMMgr {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var isAlreadyFiltered: Bool {
|
public var isAlreadyFiltered: Bool {
|
||||||
LMMgr.getLM(mode: inputMode).isPairFiltered(pair: .init(keyArray: keyArray, value: value))
|
inputMode.langModel.isPairFiltered(pair: .init(keyArray: keyArray, value: value))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func write(toFilter: Bool) -> Bool {
|
public func write(toFilter: Bool) -> Bool {
|
||||||
|
|
|
@ -230,21 +230,21 @@ import SwiftExtension
|
||||||
@AppProperty(key: UserDef.kCNS11643Enabled.rawValue, defaultValue: false)
|
@AppProperty(key: UserDef.kCNS11643Enabled.rawValue, defaultValue: false)
|
||||||
public dynamic var cns11643Enabled: Bool {
|
public dynamic var cns11643Enabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
LMMgr.setCNSEnabled(cns11643Enabled) // 很重要
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AppProperty(key: UserDef.kSymbolInputEnabled.rawValue, defaultValue: true)
|
@AppProperty(key: UserDef.kSymbolInputEnabled.rawValue, defaultValue: true)
|
||||||
public dynamic var symbolInputEnabled: Bool {
|
public dynamic var symbolInputEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
LMMgr.setSymbolEnabled(symbolInputEnabled) // 很重要
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AppProperty(key: UserDef.kCassetteEnabled.rawValue, defaultValue: false)
|
@AppProperty(key: UserDef.kCassetteEnabled.rawValue, defaultValue: false)
|
||||||
public dynamic var cassetteEnabled: Bool {
|
public dynamic var cassetteEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
LMMgr.setCassetteEnabled(cassetteEnabled) // 很重要
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ import SwiftExtension
|
||||||
willSet {
|
willSet {
|
||||||
if newValue {
|
if newValue {
|
||||||
LMMgr.loadSCPCSequencesData()
|
LMMgr.loadSCPCSequencesData()
|
||||||
LMMgr.setSCPCEnabled(true)
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,7 +333,7 @@ import SwiftExtension
|
||||||
@AppProperty(key: UserDef.kPhraseReplacementEnabled.rawValue, defaultValue: false)
|
@AppProperty(key: UserDef.kPhraseReplacementEnabled.rawValue, defaultValue: false)
|
||||||
public dynamic var phraseReplacementEnabled: Bool {
|
public dynamic var phraseReplacementEnabled: Bool {
|
||||||
willSet {
|
willSet {
|
||||||
LMMgr.setPhraseReplacementEnabled(newValue)
|
LMMgr.syncLMPrefs()
|
||||||
if newValue {
|
if newValue {
|
||||||
LMMgr.loadUserPhraseReplacement()
|
LMMgr.loadUserPhraseReplacement()
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,8 +177,8 @@ public class SessionCtl: IMKInputController {
|
||||||
resetInputHandler()
|
resetInputHandler()
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
/// 重設所有語言模組。這裡不需要做按需重設,因為對運算量沒有影響。
|
/// 重設所有語言模組。這裡不需要做按需重設,因為對運算量沒有影響。
|
||||||
inputHandler?.currentLM = LMMgr.currentLM // 會自動更新組字引擎內的模組。
|
inputHandler?.currentLM = inputMode.langModel // 會自動更新組字引擎內的模組。
|
||||||
inputHandler?.currentUOM = LMMgr.currentUOM
|
inputHandler?.currentUOM = inputMode.uom
|
||||||
/// 清空注拼槽+同步最新的注拼槽排列設定。
|
/// 清空注拼槽+同步最新的注拼槽排列設定。
|
||||||
inputHandler?.ensureKeyboardParser()
|
inputHandler?.ensureKeyboardParser()
|
||||||
/// 將輸入法偏好設定同步至語言模組內。
|
/// 將輸入法偏好設定同步至語言模組內。
|
||||||
|
@ -215,7 +215,7 @@ public class SessionCtl: IMKInputController {
|
||||||
Self.current?.hidePalettes()
|
Self.current?.hidePalettes()
|
||||||
Self.current = self
|
Self.current = self
|
||||||
self.inputHandler = InputHandler(
|
self.inputHandler = InputHandler(
|
||||||
lm: LMMgr.currentLM, uom: LMMgr.currentUOM, pref: PrefMgr.shared
|
lm: self.inputMode.langModel, uom: self.inputMode.uom, pref: PrefMgr.shared
|
||||||
)
|
)
|
||||||
self.inputHandler?.delegate = self
|
self.inputHandler?.delegate = self
|
||||||
self.syncBaseLMPrefs()
|
self.syncBaseLMPrefs()
|
||||||
|
@ -314,7 +314,7 @@ public extension SessionCtl {
|
||||||
|
|
||||||
// 這裡不需要 setValue(),因為 IMK 會在自動呼叫 activateServer() 之後自動執行 setValue()。
|
// 這裡不需要 setValue(),因為 IMK 會在自動呼叫 activateServer() 之後自動執行 setValue()。
|
||||||
self.inputHandler = InputHandler(
|
self.inputHandler = InputHandler(
|
||||||
lm: LMMgr.currentLM, uom: LMMgr.currentUOM, pref: PrefMgr.shared
|
lm: self.inputMode.langModel, uom: self.inputMode.uom, pref: PrefMgr.shared
|
||||||
)
|
)
|
||||||
self.inputHandler?.delegate = self
|
self.inputHandler?.delegate = self
|
||||||
self.syncBaseLMPrefs()
|
self.syncBaseLMPrefs()
|
||||||
|
@ -408,12 +408,7 @@ public extension SessionCtl {
|
||||||
|
|
||||||
/// 將輸入法偏好設定同步至語言模組內。
|
/// 將輸入法偏好設定同步至語言模組內。
|
||||||
func syncBaseLMPrefs() {
|
func syncBaseLMPrefs() {
|
||||||
LMMgr.currentLM.isPhraseReplacementEnabled = PrefMgr.shared.phraseReplacementEnabled
|
LMMgr.syncLMPrefs()
|
||||||
LMMgr.currentLM.isCNSEnabled = PrefMgr.shared.cns11643Enabled
|
|
||||||
LMMgr.currentLM.isSymbolEnabled = PrefMgr.shared.symbolInputEnabled
|
|
||||||
LMMgr.currentLM.isSCPCEnabled = PrefMgr.shared.useSCPCTypingMode
|
|
||||||
LMMgr.currentLM.isCassetteEnabled = PrefMgr.shared.cassetteEnabled
|
|
||||||
LMMgr.currentLM.deltaOfCalendarYears = PrefMgr.shared.deltaOfCalendarYears
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ extension SessionCtl: InputHandlerDelegate {
|
||||||
|
|
||||||
// 因為上述操作不會立即生效(除非遞交組字區),所以暫時塞入臨時資料記錄。
|
// 因為上述操作不會立即生效(除非遞交組字區),所以暫時塞入臨時資料記錄。
|
||||||
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
||||||
LMMgr.currentLM.insertTemporaryData(
|
inputMode.langModel.insertTemporaryData(
|
||||||
keyArray: userPhrase.keyArray,
|
keyArray: userPhrase.keyArray,
|
||||||
unigram: .init(value: userPhrase.value, score: userPhrase.weight ?? 0),
|
unigram: .init(value: userPhrase.value, score: userPhrase.weight ?? 0),
|
||||||
isFiltering: addToFilter
|
isFiltering: addToFilter
|
||||||
|
@ -119,7 +119,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
||||||
if state.type == .ofAssociates {
|
if state.type == .ofAssociates {
|
||||||
return shortened ? "⇧" : NSLocalizedString("Hold ⇧ to choose associates.", comment: "")
|
return shortened ? "⇧" : NSLocalizedString("Hold ⇧ to choose associates.", comment: "")
|
||||||
} else if state.type == .ofInputting, state.isCandidateContainer {
|
} else if state.type == .ofInputting, state.isCandidateContainer {
|
||||||
let useShift = LMMgr.currentLM.areCassetteCandidateKeysShiftHeld
|
let useShift = inputMode.langModel.areCassetteCandidateKeysShiftHeld
|
||||||
let theEmoji = useShift ? "⬆️" : "⚡️"
|
let theEmoji = useShift ? "⬆️" : "⚡️"
|
||||||
return shortened ? theEmoji : "\(theEmoji) " + NSLocalizedString("Quick Candidates", comment: "")
|
return shortened ? theEmoji : "\(theEmoji) " + NSLocalizedString("Quick Candidates", comment: "")
|
||||||
} else if PrefMgr.shared.cassetteEnabled {
|
} else if PrefMgr.shared.cassetteEnabled {
|
||||||
|
@ -141,14 +141,14 @@ extension SessionCtl: CtlCandidateDelegate {
|
||||||
if value.isEmpty { return blankResult } // 空字串沒有需要反查的東西。
|
if value.isEmpty { return blankResult } // 空字串沒有需要反查的東西。
|
||||||
if value.contains("_") { return blankResult }
|
if value.contains("_") { return blankResult }
|
||||||
// 因為威注音輸入法的反查結果僅由磁帶模組負責,所以相關運算挪至 LMInstantiator 內處理。
|
// 因為威注音輸入法的反查結果僅由磁帶模組負責,所以相關運算挪至 LMInstantiator 內處理。
|
||||||
return LMMgr.currentLM.cassetteReverseLookup(for: value)
|
return inputMode.langModel.cassetteReverseLookup(for: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var selectionKeys: String {
|
public var selectionKeys: String {
|
||||||
// 磁帶模式的 `%quick` 有單獨的選字鍵判定,會在資料不合規時使用 1234567890 選字鍵。
|
// 磁帶模式的 `%quick` 有單獨的選字鍵判定,會在資料不合規時使用 1234567890 選字鍵。
|
||||||
cassetteQuick: if state.type == .ofInputting, state.isCandidateContainer {
|
cassetteQuick: if state.type == .ofInputting, state.isCandidateContainer {
|
||||||
guard PrefMgr.shared.cassetteEnabled else { break cassetteQuick }
|
guard PrefMgr.shared.cassetteEnabled else { break cassetteQuick }
|
||||||
guard let cinCandidateKey = LMMgr.currentLM.cassetteSelectionKey,
|
guard let cinCandidateKey = inputMode.langModel.cassetteSelectionKey,
|
||||||
CandidateKey.validate(keys: cinCandidateKey) == nil
|
CandidateKey.validate(keys: cinCandidateKey) == nil
|
||||||
else {
|
else {
|
||||||
return "1234567890"
|
return "1234567890"
|
||||||
|
@ -267,7 +267,7 @@ extension SessionCtl: CtlCandidateDelegate {
|
||||||
|
|
||||||
// 因為上述操作不會立即生效(除非遞交組字區),所以暫時塞入臨時資料記錄。
|
// 因為上述操作不會立即生效(除非遞交組字區),所以暫時塞入臨時資料記錄。
|
||||||
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
// 該臨時資料記錄會在接下來的語言模組資料重載過程中被自動清除。
|
||||||
LMMgr.currentLM.insertTemporaryData(
|
inputMode.langModel.insertTemporaryData(
|
||||||
keyArray: userPhrase.keyArray,
|
keyArray: userPhrase.keyArray,
|
||||||
unigram: .init(value: userPhrase.value, score: userPhrase.weight ?? 0),
|
unigram: .init(value: userPhrase.value, score: userPhrase.weight ?? 0),
|
||||||
isFiltering: action == .toFilter
|
isFiltering: action == .toFilter
|
||||||
|
|
|
@ -140,7 +140,7 @@ public struct VwrSettingsPaneCassette: View {
|
||||||
} else {
|
} else {
|
||||||
LMMgr.loadCassetteData()
|
LMMgr.loadCassetteData()
|
||||||
}
|
}
|
||||||
LMMgr.setCassetteEnabled(cassetteEnabled)
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,13 +177,13 @@ public struct VwrSettingsPaneDictionary: View {
|
||||||
Toggle(
|
Toggle(
|
||||||
LocalizedStringKey("Enable CNS11643 Support (2023-11-06)"),
|
LocalizedStringKey("Enable CNS11643 Support (2023-11-06)"),
|
||||||
isOn: $cns11643Enabled.onChange {
|
isOn: $cns11643Enabled.onChange {
|
||||||
LMMgr.setCNSEnabled(cns11643Enabled)
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Toggle(
|
Toggle(
|
||||||
LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"),
|
LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"),
|
||||||
isOn: $symbolInputEnabled.onChange {
|
isOn: $symbolInputEnabled.onChange {
|
||||||
LMMgr.setSymbolEnabled(symbolInputEnabled)
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
|
@ -200,7 +200,7 @@ public struct VwrSettingsPaneDictionary: View {
|
||||||
Toggle(
|
Toggle(
|
||||||
LocalizedStringKey("Enable phrase replacement table"),
|
LocalizedStringKey("Enable phrase replacement table"),
|
||||||
isOn: $phraseReplacementEnabled.onChange {
|
isOn: $phraseReplacementEnabled.onChange {
|
||||||
LMMgr.setPhraseReplacementEnabled(phraseReplacementEnabled)
|
LMMgr.syncLMPrefs()
|
||||||
if phraseReplacementEnabled {
|
if phraseReplacementEnabled {
|
||||||
LMMgr.loadUserPhraseReplacement()
|
LMMgr.loadUserPhraseReplacement()
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,6 +226,10 @@ public enum Shared {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static var validCases: [InputMode] {
|
||||||
|
[.imeModeCHS, .imeModeCHT]
|
||||||
|
}
|
||||||
|
|
||||||
public var localizedDescription: String { NSLocalizedString(description, comment: "") }
|
public var localizedDescription: String { NSLocalizedString(description, comment: "") }
|
||||||
public var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
|
|
@ -272,7 +272,7 @@ public extension SessionCtl {
|
||||||
? "NotificationSwitchON".localized
|
? "NotificationSwitchON".localized
|
||||||
: "NotificationSwitchOFF".localized)
|
: "NotificationSwitchOFF".localized)
|
||||||
)
|
)
|
||||||
if !LMMgr.currentLM.isCassetteDataLoaded {
|
if !inputMode.langModel.isCassetteDataLoaded {
|
||||||
LMMgr.loadCassetteData()
|
LMMgr.loadCassetteData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,11 +233,11 @@ class CtlPrefWindow: NSWindowController, NSWindowDelegate {
|
||||||
// 這裡有必要加上這段處理,用來確保藉由偏好設定介面動過的 CNS 開關能夠立刻生效。
|
// 這裡有必要加上這段處理,用來確保藉由偏好設定介面動過的 CNS 開關能夠立刻生效。
|
||||||
// 所有涉及到語言模型開關的內容均需要這樣處理。
|
// 所有涉及到語言模型開關的內容均需要這樣處理。
|
||||||
@IBAction func toggleCNSSupport(_: Any) {
|
@IBAction func toggleCNSSupport(_: Any) {
|
||||||
LMMgr.setCNSEnabled(PrefMgr.shared.cns11643Enabled)
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func toggleSymbolInputEnabled(_: Any) {
|
@IBAction func toggleSymbolInputEnabled(_: Any) {
|
||||||
LMMgr.setSymbolEnabled(PrefMgr.shared.symbolInputEnabled)
|
LMMgr.syncLMPrefs()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func toggleTrad2KangXiAction(_: Any) {
|
@IBAction func toggleTrad2KangXiAction(_: Any) {
|
||||||
|
|
Loading…
Reference in New Issue