1.8.7 // Rescue. Merge Gitee PR!73 from dev/bleed
This commit is contained in:
commit
d5f0ed4b6f
|
@ -97,7 +97,7 @@
|
|||
- 天權星語彙處理引擎:Shiki Suen (MIT-NTL License)。
|
||||
- 威注音詞庫由 Shiki Suen 維護,以 3-Clause BSD License 授權釋出。其中的詞頻數據[由 NAER 授權用於非商業用途](https://twitter.com/ShikiSuen/status/1479329302713831424)。
|
||||
|
||||
使用者可自由使用、散播本軟體,惟散播時必須完整保留版權聲明及軟體授權、且一旦經過修改便不可以再繼續使用威注音的產品名稱。
|
||||
使用者可自由使用、散播本軟體,惟散播時必須完整保留版權聲明及軟體授權、且「一旦經過修改便不可以再繼續使用威注音的產品名稱」。換言之,這條相對上游 MIT 而言新增的規定就是:你 Fork 可以,但 Fork 成單獨發行的產品名稱時就必須修改產品名稱。這條新增規定對 OpenVanilla 與威注音雙方都有益,免得各自的旗號被盜版下載販子等挪用做意外用途。
|
||||
|
||||
## 資料來源
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
- 天权星语汇处理引擎:Shiki Suen (MIT-NTL License)。
|
||||
- 威注音词库由 Shiki Suen 维护,以 3-Clause BSD License 授权释出。其中的词频数据[由 NAER 授权用于非商业用途](https://twitter.com/ShikiSuen/status/1479329302713831424)。
|
||||
|
||||
使用者可自由使用、散播本软件,惟散播时必须完整保留版权声明及软件授权、且一旦经过修改便不可以再继续使用威注音的产品名称。
|
||||
使用者可自由使用、散播本软件,惟散播时必须完整保留版权声明及软件授权、且「一旦经过修改便不可以再继续使用威注音的产品名称」。换言之,这条相对上游 MIT 而言新增的规定就是:你 Fork 可以,但 Fork 成单独发行的产品名称时就必须修改产品名称。这条新增规定对 OpenVanilla 与威注音双方都有益,免得各自的旗号被盗版下载贩子等挪用做意外用途。
|
||||
|
||||
## 资料来源
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 30a5bc773a21d67c3f117eef5346d3b35433bbaa
|
||||
Subproject commit 144db528de84caff0fa20802b144421bcc58419f
|
|
@ -29,7 +29,7 @@ import InputMethodKit
|
|||
|
||||
@objc(AppDelegate)
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelegate,
|
||||
FSEventStreamHelperDelegate
|
||||
FSEventStreamHelperDelegate, NSUserNotificationCenterDelegate
|
||||
{
|
||||
func helper(_: FSEventStreamHelper, didReceive _: [FSEventStreamHelper.Event]) {
|
||||
// 拖 100ms 再重載,畢竟有些有特殊需求的使用者可能會想使用巨型自訂語彙檔案。
|
||||
|
@ -63,7 +63,25 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
|
|||
fsStreamHelper.delegate = nil
|
||||
}
|
||||
|
||||
func userNotificationCenter(_: NSUserNotificationCenter, shouldPresent _: NSUserNotification) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_: Notification) {
|
||||
NSUserNotificationCenter.default.delegate = self
|
||||
// 一旦發現與使用者半衰模組的觀察行為有關的崩潰標記被開啟,就清空既有的半衰記憶資料檔案。
|
||||
if mgrPrefs.failureFlagForUOMObservation {
|
||||
mgrLangModel.clearUserOverrideModelData(.imeModeCHS)
|
||||
mgrLangModel.clearUserOverrideModelData(.imeModeCHT)
|
||||
mgrPrefs.failureFlagForUOMObservation = false
|
||||
let userNotification = NSUserNotification()
|
||||
userNotification.title = NSLocalizedString("vChewing", comment: "")
|
||||
userNotification.informativeText =
|
||||
"\(NSLocalizedString("vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability.", comment: ""))"
|
||||
userNotification.soundName = NSUserNotificationDefaultSoundName
|
||||
NSUserNotificationCenter.default.deliver(userNotification)
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
|
||||
IME.initLangModels(userOnly: false)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import Cocoa
|
|||
// 註:所有 InputState 型別均不適合使用 Struct,因為 Struct 無法相互繼承派生。
|
||||
|
||||
// 用以讓每個狀態自描述的 enum。
|
||||
enum StateType {
|
||||
public enum StateType {
|
||||
case ofDeactivated
|
||||
case ofAssociatedPhrases
|
||||
case ofEmpty
|
||||
|
@ -38,12 +38,12 @@ enum StateType {
|
|||
case ofNotEmpty
|
||||
case ofInputting
|
||||
case ofMarking
|
||||
case ofChooseCandidate
|
||||
case ofChoosingCandidate
|
||||
case ofSymbolTable
|
||||
}
|
||||
|
||||
// 所有 InputState 均遵守該協定:
|
||||
protocol InputStateProtocol {
|
||||
public protocol InputStateProtocol {
|
||||
var type: StateType { get }
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ protocol InputStateProtocol {
|
|||
/// 詞音組合放入語彙濾除清單。
|
||||
/// - .ChoosingCandidate: 叫出選字窗、允許使用者選字。
|
||||
/// - .SymbolTable: 波浪鍵符號選單專用的狀態,有自身的特殊處理。
|
||||
enum InputState {
|
||||
public enum InputState {
|
||||
/// .Deactivated: 使用者沒在使用輸入法。
|
||||
class Deactivated: InputStateProtocol {
|
||||
public var type: StateType { .ofDeactivated }
|
||||
|
@ -175,6 +175,8 @@ enum InputState {
|
|||
public var type: StateType { .ofNotEmpty }
|
||||
private(set) var composingBuffer: String
|
||||
private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } }
|
||||
private(set) var reading: String = ""
|
||||
private(set) var nodeValuesArray = [String]()
|
||||
public var composingBufferConverted: String {
|
||||
let converted = IME.kanjiConversionIfRequired(composingBuffer)
|
||||
if converted.utf16.count != composingBuffer.utf16.count
|
||||
|
@ -185,21 +187,39 @@ enum InputState {
|
|||
return converted
|
||||
}
|
||||
|
||||
init(composingBuffer: String, cursorIndex: Int) {
|
||||
init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
|
||||
self.composingBuffer = composingBuffer
|
||||
self.reading = reading
|
||||
self.nodeValuesArray = nodeValuesArray
|
||||
defer { self.cursorIndex = cursorIndex }
|
||||
}
|
||||
|
||||
var attributedString: NSMutableAttributedString {
|
||||
/// 考慮到因為滑鼠點擊等其它行為導致的組字區內容遞交情況,
|
||||
/// 這裡對組字區內容也加上康熙字轉換或者 JIS 漢字轉換處理。
|
||||
let attributedString = NSMutableAttributedString(
|
||||
string: composingBufferConverted,
|
||||
attributes: [
|
||||
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||
.markedClauseSegment: 0,
|
||||
]
|
||||
)
|
||||
guard reading.isEmpty else {
|
||||
let attributedString = NSMutableAttributedString(
|
||||
string: composingBufferConverted,
|
||||
attributes: [
|
||||
/// 不能用 .thick,否則會看不到游標。
|
||||
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||
.markedClauseSegment: 0,
|
||||
]
|
||||
)
|
||||
return attributedString
|
||||
}
|
||||
let attributedString = NSMutableAttributedString(string: composingBufferConverted)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -216,8 +236,9 @@ enum InputState {
|
|||
var textToCommit: String = ""
|
||||
var tooltip: String = ""
|
||||
|
||||
override init(composingBuffer: String, cursorIndex: Int) {
|
||||
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||
override init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) {
|
||||
super.init(
|
||||
composingBuffer: composingBuffer, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray)
|
||||
}
|
||||
|
||||
override var description: String {
|
||||
|
@ -264,9 +285,9 @@ enum InputState {
|
|||
private var deleteTargetExists = false
|
||||
var tooltip: String {
|
||||
if composingBuffer.count != readings.count {
|
||||
ctlInputMethod.tooltipController.setColor(state: .redAlert)
|
||||
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
|
||||
return NSLocalizedString(
|
||||
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: ""
|
||||
"⚠︎ Beware: Chars and Readings in buffer doesn't match.", comment: ""
|
||||
)
|
||||
}
|
||||
if mgrPrefs.phraseReplacementEnabled {
|
||||
|
@ -376,14 +397,9 @@ enum InputState {
|
|||
}
|
||||
|
||||
var validToWrite: Bool {
|
||||
/// The input method allows users to input a string whose length differs
|
||||
/// from the amount of Bopomofo readings. In this case, the range
|
||||
/// in the composing buffer and the readings could not match, so
|
||||
/// we disable the function to write user phrases in this case.
|
||||
/// 這裡的 deleteTargetExists 是防止使用者排除「詞庫內尚未存在的詞」,
|
||||
/// 免得使用者誤操作之後靠北「我怎麼敲不了這個詞?」之類的。
|
||||
((composingBuffer.count != readings.count)
|
||||
|| (ctlInputMethod.areWeDeleting && !deleteTargetExists))
|
||||
/// 與小麥注音不同,威注音會自動解消「游標插斷字符」的異常狀態,所以允許在字音長度不相符的情況下加詞。
|
||||
/// 這裡的 deleteTargetExists 是防止使用者排除「詞庫內尚未存在的詞」。
|
||||
(ctlInputMethod.areWeDeleting && !deleteTargetExists)
|
||||
? false
|
||||
: allowedMarkRange.contains(literalMarkedRange.count)
|
||||
}
|
||||
|
@ -418,7 +434,7 @@ enum InputState {
|
|||
|
||||
/// .ChoosingCandidate: 叫出選字窗、允許使用者選字。
|
||||
class ChoosingCandidate: NotEmpty {
|
||||
override public var type: StateType { .ofChooseCandidate }
|
||||
override public var type: StateType { .ofChoosingCandidate }
|
||||
private(set) var candidates: [(String, String)]
|
||||
private(set) var isTypingVertical: Bool
|
||||
|
||||
|
|
|
@ -34,10 +34,10 @@ import Cocoa
|
|||
|
||||
/// KeyHandler 委任協定
|
||||
protocol KeyHandlerDelegate {
|
||||
func ctlCandidate() -> ctlCandidate
|
||||
func ctlCandidate() -> ctlCandidateProtocol
|
||||
func keyHandler(
|
||||
_: KeyHandler, didSelectCandidateAt index: Int,
|
||||
ctlCandidate controller: ctlCandidate
|
||||
ctlCandidate controller: ctlCandidateProtocol
|
||||
)
|
||||
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol)
|
||||
-> Bool
|
||||
|
@ -46,9 +46,11 @@ protocol KeyHandlerDelegate {
|
|||
// MARK: - 核心 (Kernel).
|
||||
|
||||
/// KeyHandler 按鍵調度模組。
|
||||
class KeyHandler {
|
||||
public class KeyHandler {
|
||||
/// 半衰模組的衰減指數
|
||||
let kEpsilon: Double = 0.000001
|
||||
/// 檢測是否出現游標切斷組字圈內字符的情況
|
||||
var isCursorCuttingChar = false
|
||||
|
||||
/// 規定最大動態爬軌範圍。組字器內超出該範圍的節錨都會被自動標記為「已經手動選字過」,減少爬軌運算負擔。
|
||||
let kMaxComposingBufferNeedsToWalkSize = Int(max(12, ceil(Double(mgrPrefs.composingBufferSize) / 2)))
|
||||
|
@ -182,14 +184,20 @@ class KeyHandler {
|
|||
addToUserOverrideModel = false
|
||||
}
|
||||
}
|
||||
if addToUserOverrideModel {
|
||||
if addToUserOverrideModel, mgrPrefs.fetchSuggestionsFromUserOverrideModel {
|
||||
IME.prtDebugIntel("UOM: Start Observation.")
|
||||
// 這個過程可能會因為使用者半衰記憶模組內部資料錯亂、而導致輸入法在選字時崩潰。
|
||||
// 於是在這裡引入災後狀況察覺專用變數,且先開啟該開關。順利執行完觀察後會關閉。
|
||||
// 一旦輸入法崩潰,會在重啟時發現這個開關是開著的,屆時 AppDelegate 會做出應對。
|
||||
mgrPrefs.failureFlagForUOMObservation = true
|
||||
// 令半衰記憶模組觀測給定的三元圖。
|
||||
// 這個過程會讓半衰引擎根據當前上下文生成三元圖索引鍵。
|
||||
currentUOM.observe(
|
||||
walkedAnchors: walkedAnchors, cursorIndex: adjustedCursor, candidate: theCandidate.value,
|
||||
timestamp: NSDate().timeIntervalSince1970, saveCallback: { mgrLangModel.saveUserOverrideModelData() }
|
||||
)
|
||||
// 如果沒有出現崩框的話,那就將這個開關復位。
|
||||
mgrPrefs.failureFlagForUOMObservation = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ extension KeyHandler {
|
|||
) -> Bool {
|
||||
let inputText = input.inputText
|
||||
let charCode: UniChar = input.charCode
|
||||
guard let ctlCandidateCurrent = delegate?.ctlCandidate() else {
|
||||
guard var ctlCandidateCurrent = delegate?.ctlCandidate() else {
|
||||
IME.prtDebugIntel("06661F6E")
|
||||
errorCallback()
|
||||
return true
|
||||
|
@ -69,6 +69,7 @@ extension KeyHandler {
|
|||
// 所以這裡需要對 compositor.isEmpty 做判定。
|
||||
clear()
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
} else {
|
||||
stateCallback(buildInputtingState)
|
||||
}
|
||||
|
@ -84,6 +85,7 @@ extension KeyHandler {
|
|||
if state is InputState.AssociatedPhrases, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
|
||||
clear()
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
return true
|
||||
}
|
||||
delegate?.keyHandler(
|
||||
|
@ -293,14 +295,12 @@ extension KeyHandler {
|
|||
let match: String =
|
||||
(state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText
|
||||
|
||||
var j = 0
|
||||
while j < ctlCandidateCurrent.keyLabels.count {
|
||||
for j in 0..<ctlCandidateCurrent.keyLabels.count {
|
||||
let label: CandidateKeyLabel = ctlCandidateCurrent.keyLabels[j]
|
||||
if match.compare(label.key, options: .caseInsensitive, range: nil, locale: .current) == .orderedSame {
|
||||
index = j
|
||||
break
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
|
||||
if index != NSNotFound {
|
||||
|
@ -352,10 +352,10 @@ extension KeyHandler {
|
|||
ctlCandidate: ctlCandidateCurrent
|
||||
)
|
||||
clear()
|
||||
let empty = InputState.EmptyIgnoringPreviousState()
|
||||
stateCallback(empty)
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
return handle(
|
||||
input: input, state: empty, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
input: input, state: InputState.Empty(), stateCallback: stateCallback, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -30,17 +30,14 @@ extension KeyHandler {
|
|||
/// 用來處理 KeyHandler.HandleInput() 當中的與組字有關的行為。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - state: 給定狀態(通常為當前狀態)。
|
||||
/// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
func handleComposition(
|
||||
input: InputSignal,
|
||||
state: InputStateProtocol,
|
||||
stateCallback: @escaping (InputStateProtocol) -> Void,
|
||||
errorCallback: @escaping () -> Void
|
||||
) -> Bool? {
|
||||
|
||||
// MARK: 注音按鍵輸入處理 (Handle BPMF Keys)
|
||||
|
||||
var keyConsumedByReading = false
|
||||
|
@ -63,7 +60,7 @@ extension KeyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
var composeReading = composer.hasToneMarker() // 這裡不需要做排他性判斷。
|
||||
var composeReading = composer.hasToneMarker() && composer.inputValidityCheck(key: input.charCode) // 這裡不需要做排他性判斷。
|
||||
|
||||
// 如果當前的按鍵是 Enter 或 Space 的話,這時就可以取出 _composer 內的注音來做檢查了。
|
||||
// 來看看詞庫內到底有沒有對應的讀音索引。這裡用了類似「|=」的判斷處理方式。
|
||||
|
@ -81,9 +78,21 @@ extension KeyHandler {
|
|||
if !currentLM.hasUnigramsFor(key: readingKey) {
|
||||
IME.prtDebugIntel("B49C0979:語彙庫內無「\(readingKey)」的匹配記錄。")
|
||||
errorCallback()
|
||||
|
||||
if mgrPrefs.keepReadingUponCompositionError {
|
||||
composer.intonation.clear() // 砍掉聲調。
|
||||
stateCallback(buildInputtingState)
|
||||
return true
|
||||
}
|
||||
|
||||
composer.clear()
|
||||
// 根據「組字器是否為空」來判定回呼哪一種狀態。
|
||||
stateCallback((compositor.isEmpty) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
||||
switch compositor.isEmpty {
|
||||
case false: stateCallback(buildInputtingState)
|
||||
case true:
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
}
|
||||
return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。
|
||||
}
|
||||
|
||||
|
@ -141,9 +150,7 @@ extension KeyHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
/// 如果此時這個選項是 true 的話,可知當前注拼槽輸入了聲調、且上一次按鍵不是聲調按鍵。
|
||||
/// 比方說大千傳統佈局敲「6j」會出現「ˊㄨ」但並不會被認為是「ㄨˊ」,因為先輸入的調號
|
||||
/// 並非用來確認這個注音的調號。除非是:「ㄨˊ」「ˊㄨˊ」「ˊㄨˇ」「ˊㄨ 」等。
|
||||
/// 是說此時注拼槽並非為空、卻還沒組音。這種情況下只可能是「注拼槽內只有聲調」。
|
||||
if keyConsumedByReading {
|
||||
// 以回呼組字狀態的方式來執行 updateClientComposingBuffer()。
|
||||
stateCallback(buildInputtingState)
|
||||
|
|
|
@ -159,8 +159,8 @@ extension KeyHandler {
|
|||
// MARK: 注音按鍵輸入處理 (Handle BPMF Keys)
|
||||
|
||||
if let compositionHandled = handleComposition(
|
||||
input: input, state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
{
|
||||
input: input, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
) {
|
||||
return compositionHandled
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ extension KeyHandler {
|
|||
/// 「更新內文組字區 (Update the composing buffer)」是指要求客體軟體將組字緩衝區的內容
|
||||
/// 換成由此處重新生成的組字字串(NSAttributeString,否則會不顯示)。
|
||||
var tooltipParameterRef: [String] = ["", ""]
|
||||
var composingBuffer = ""
|
||||
let nodeValuesArray: [String] = walkedAnchors.map(\.node.currentPair.value)
|
||||
var composedStringCursorIndex = 0
|
||||
var readingCursorIndex = 0
|
||||
/// IMK 協定的內文組字區的游標長度與游標位置無法正確統計 UTF8 高萬字(比如 emoji)的長度,
|
||||
|
@ -47,7 +47,6 @@ extension KeyHandler {
|
|||
for theAnchor in walkedAnchors {
|
||||
let theNode = theAnchor.node
|
||||
let strNodeValue = theNode.currentPair.value
|
||||
composingBuffer += strNodeValue
|
||||
let arrSplit: [String] = Array(strNodeValue).map { String($0) }
|
||||
let codepointCount = arrSplit.count
|
||||
/// 藉下述步驟重新將「可見游標位置」對齊至「組字器內的游標所在的讀音位置」。
|
||||
|
@ -57,57 +56,45 @@ extension KeyHandler {
|
|||
if readingCursorIndex + spanningLength <= compositor.cursor {
|
||||
composedStringCursorIndex += strNodeValue.utf16.count
|
||||
readingCursorIndex += spanningLength
|
||||
} else {
|
||||
if codepointCount == spanningLength {
|
||||
var i = 0
|
||||
while i < codepointCount, readingCursorIndex < compositor.cursor {
|
||||
composedStringCursorIndex += arrSplit[i].utf16.count
|
||||
readingCursorIndex += 1
|
||||
i += 1
|
||||
}
|
||||
} else {
|
||||
if readingCursorIndex < compositor.cursor {
|
||||
composedStringCursorIndex += strNodeValue.utf16.count
|
||||
readingCursorIndex += spanningLength
|
||||
readingCursorIndex = min(readingCursorIndex, compositor.cursor)
|
||||
/// 接下來再處理這麼一種情況:
|
||||
/// 某些錨點內的當前候選字詞長度與讀音長度不相等。
|
||||
/// 但此時游標還是按照每個讀音單位來移動的,
|
||||
/// 所以需要上下文工具提示來顯示游標的相對位置。
|
||||
/// 這裡先計算一下要用在工具提示當中的顯示參數的內容。
|
||||
switch compositor.cursor {
|
||||
case compositor.readings.count...:
|
||||
// 這裡的 compositor.cursor 數值不可能大於 readings.count,因為會被 Megrez 自動糾正。
|
||||
tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1]
|
||||
case 0:
|
||||
tooltipParameterRef[1] = compositor.readings[compositor.cursor]
|
||||
default:
|
||||
tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1]
|
||||
tooltipParameterRef[1] = compositor.readings[compositor.cursor]
|
||||
}
|
||||
/// 注音轉拼音
|
||||
for (i, _) in tooltipParameterRef.enumerated() {
|
||||
if tooltipParameterRef[i].isEmpty { continue }
|
||||
if tooltipParameterRef[i].contains("_") { continue }
|
||||
if mgrPrefs.showHanyuPinyinInCompositionBuffer { // 恢復陰平標記->注音轉拼音->轉教科書式標調
|
||||
tooltipParameterRef[i] = Tekkon.restoreToneOneInZhuyinKey(target: tooltipParameterRef[i])
|
||||
tooltipParameterRef[i] = Tekkon.cnvPhonaToHanyuPinyin(target: tooltipParameterRef[i])
|
||||
tooltipParameterRef[i] = Tekkon.cnvHanyuPinyinToTextbookStyle(target: tooltipParameterRef[i])
|
||||
} else {
|
||||
tooltipParameterRef[i] = Tekkon.cnvZhuyinChainToTextbookReading(target: tooltipParameterRef[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if codepointCount == spanningLength {
|
||||
for i in 0..<codepointCount {
|
||||
guard readingCursorIndex < compositor.cursor else { continue }
|
||||
composedStringCursorIndex += arrSplit[i].utf16.count
|
||||
readingCursorIndex += 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
guard readingCursorIndex < compositor.cursor else { continue }
|
||||
composedStringCursorIndex += strNodeValue.utf16.count
|
||||
readingCursorIndex += spanningLength
|
||||
readingCursorIndex = min(readingCursorIndex, compositor.cursor)
|
||||
/// 接下來再處理這麼一種情況:
|
||||
/// 某些錨點內的當前候選字詞長度與讀音長度不相等。
|
||||
/// 但此時游標還是按照每個讀音單位來移動的,
|
||||
/// 所以需要上下文工具提示來顯示游標的相對位置。
|
||||
/// 這裡先計算一下要用在工具提示當中的顯示參數的內容。
|
||||
switch compositor.cursor {
|
||||
case compositor.readings.count...:
|
||||
// 這裡的 compositor.cursor 數值不可能大於 readings.count,因為會被 Megrez 自動糾正。
|
||||
tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1]
|
||||
case 0:
|
||||
tooltipParameterRef[1] = compositor.readings[compositor.cursor]
|
||||
default:
|
||||
tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1]
|
||||
tooltipParameterRef[1] = compositor.readings[compositor.cursor]
|
||||
}
|
||||
}
|
||||
|
||||
isCursorCuttingChar = !tooltipParameterRef[0].isEmpty || !tooltipParameterRef[1].isEmpty
|
||||
|
||||
/// 再接下來,藉由已經計算成功的「可見游標位置」,咱們計算一下在這個游標之前與之後的
|
||||
/// 組字區內容,以便之後在這之間插入正在輸入的漢字讀音(藉由鐵恨 composer 注拼槽取得)。
|
||||
var arrHead = [String.UTF16View.Element]()
|
||||
var arrTail = [String.UTF16View.Element]()
|
||||
|
||||
for (i, n) in composingBuffer.utf16.enumerated() {
|
||||
for (i, n) in nodeValuesArray.joined().utf16.enumerated() {
|
||||
if i < composedStringCursorIndex {
|
||||
arrHead.append(n)
|
||||
} else {
|
||||
|
@ -126,42 +113,16 @@ extension KeyHandler {
|
|||
// 防止組字區內出現不可列印的字元。
|
||||
var cleanedComposition = ""
|
||||
for theChar in composedText {
|
||||
if let charCode = theChar.utf16.first {
|
||||
if !(theChar.isASCII && !(charCode.isPrintable)) {
|
||||
cleanedComposition += String(theChar)
|
||||
}
|
||||
guard let charCode = theChar.utf16.first else { continue }
|
||||
if !(theChar.isASCII && !(charCode.isPrintable)) {
|
||||
cleanedComposition += String(theChar)
|
||||
}
|
||||
}
|
||||
|
||||
/// 這裡生成準備要拿來回呼的「正在輸入」狀態,但還不能立即使用,因為工具提示仍未完成。
|
||||
let stateResult = InputState.Inputting(composingBuffer: cleanedComposition, cursorIndex: cursorIndex)
|
||||
|
||||
/// 根據上文的參數結果來決定生成怎樣的工具提示。
|
||||
switch (tooltipParameterRef[0].isEmpty, tooltipParameterRef[1].isEmpty) {
|
||||
case (true, true): stateResult.tooltip.removeAll()
|
||||
case (true, false):
|
||||
stateResult.tooltip = String(
|
||||
format: NSLocalizedString("Cursor is to the rear of \"%@\".", comment: ""),
|
||||
tooltipParameterRef[1]
|
||||
)
|
||||
case (false, true):
|
||||
stateResult.tooltip = String(
|
||||
format: NSLocalizedString("Cursor is in front of \"%@\".", comment: ""),
|
||||
tooltipParameterRef[0]
|
||||
)
|
||||
case (false, false):
|
||||
stateResult.tooltip = String(
|
||||
format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""),
|
||||
tooltipParameterRef[0], tooltipParameterRef[1]
|
||||
)
|
||||
}
|
||||
|
||||
/// 給工具提示設定提示配色。
|
||||
if !stateResult.tooltip.isEmpty {
|
||||
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
|
||||
}
|
||||
|
||||
return stateResult
|
||||
return InputState.Inputting(
|
||||
composingBuffer: cleanedComposition, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 用以生成候選詞陣列及狀態
|
||||
|
@ -459,8 +420,12 @@ extension KeyHandler {
|
|||
composer.doBackSpace()
|
||||
}
|
||||
|
||||
stateCallback(
|
||||
composer.isEmpty && compositor.isEmpty ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
||||
switch composer.isEmpty && compositor.isEmpty {
|
||||
case false: stateCallback(buildInputtingState)
|
||||
case true:
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -497,7 +462,12 @@ extension KeyHandler {
|
|||
walk()
|
||||
let inputting = buildInputtingState
|
||||
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||
stateCallback(inputting.composingBuffer.isEmpty ? InputState.EmptyIgnoringPreviousState() : inputting)
|
||||
switch inputting.composingBuffer.isEmpty {
|
||||
case false: stateCallback(inputting)
|
||||
case true:
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -609,11 +579,17 @@ extension KeyHandler {
|
|||
/// 此乃 macOS 內建注音輸入法預設之行為,但不太受 Windows 使用者群體之待見。
|
||||
clear()
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
} else {
|
||||
if composer.isEmpty { return true }
|
||||
/// 如果注拼槽不是空的話,則清空之。
|
||||
composer.clear()
|
||||
stateCallback(compositor.isEmpty ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
||||
switch compositor.isEmpty {
|
||||
case false: stateCallback(buildInputtingState)
|
||||
case true:
|
||||
stateCallback(InputState.EmptyIgnoringPreviousState())
|
||||
stateCallback(InputState.Empty())
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -661,6 +637,7 @@ extension KeyHandler {
|
|||
stateCallback(state)
|
||||
}
|
||||
} else if input.isOptionHold {
|
||||
isCursorCuttingChar = false
|
||||
if input.isControlHold {
|
||||
return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
}
|
||||
|
@ -675,7 +652,12 @@ extension KeyHandler {
|
|||
} else {
|
||||
if compositor.cursor < compositor.length {
|
||||
compositor.cursor += 1
|
||||
stateCallback(buildInputtingState)
|
||||
var inputtingState = buildInputtingState
|
||||
if isCursorCuttingChar == true {
|
||||
compositor.jumpCursorBySpan(to: .front)
|
||||
inputtingState = buildInputtingState
|
||||
}
|
||||
stateCallback(inputtingState)
|
||||
} else {
|
||||
IME.prtDebugIntel("A96AAD58")
|
||||
errorCallback()
|
||||
|
@ -729,6 +711,7 @@ extension KeyHandler {
|
|||
stateCallback(state)
|
||||
}
|
||||
} else if input.isOptionHold {
|
||||
isCursorCuttingChar = false
|
||||
if input.isControlHold {
|
||||
return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
}
|
||||
|
@ -743,7 +726,12 @@ extension KeyHandler {
|
|||
} else {
|
||||
if compositor.cursor > 0 {
|
||||
compositor.cursor -= 1
|
||||
stateCallback(buildInputtingState)
|
||||
var inputtingState = buildInputtingState
|
||||
if isCursorCuttingChar == true {
|
||||
compositor.jumpCursorBySpan(to: .rear)
|
||||
inputtingState = buildInputtingState
|
||||
}
|
||||
stateCallback(inputtingState)
|
||||
} else {
|
||||
IME.prtDebugIntel("7045E6F3")
|
||||
errorCallback()
|
||||
|
|
|
@ -41,7 +41,7 @@ class ctlInputMethod: IMKInputController {
|
|||
static var areWeDeleting = false
|
||||
|
||||
/// 目前在用的的選字窗副本。
|
||||
static var ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
|
||||
static var ctlCandidateCurrent: ctlCandidateProtocol = ctlCandidateUniversal.init(.horizontal)
|
||||
|
||||
/// 工具提示視窗的副本。
|
||||
static let tooltipController = TooltipController()
|
||||
|
@ -85,6 +85,7 @@ class ctlInputMethod: IMKInputController {
|
|||
keyHandler.delegate = self
|
||||
// 下述兩行很有必要,否則輸入法會在手動重啟之後無法立刻生效。
|
||||
activateServer(inputClient)
|
||||
keyHandler.ensureParser()
|
||||
resetKeyHandler()
|
||||
}
|
||||
|
||||
|
@ -99,7 +100,7 @@ class ctlInputMethod: IMKInputController {
|
|||
// 因為偶爾會收到與 activateServer 有關的以「強制拆 nil」為理由的報錯,
|
||||
// 所以這裡添加這句、來試圖應對這種情況。
|
||||
if keyHandler.delegate == nil { keyHandler.delegate = self }
|
||||
|
||||
setValue(IME.currentInputMode.rawValue, forTag: 114514, client: client())
|
||||
keyHandler.clear()
|
||||
keyHandler.ensureParser()
|
||||
|
||||
|
@ -236,4 +237,30 @@ class ctlInputMethod: IMKInputController {
|
|||
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||
resetKeyHandler()
|
||||
}
|
||||
|
||||
/// 生成 IMK 選字窗專用的候選字串陣列。
|
||||
/// - Parameter sender: 呼叫了該函式的客體(無須使用)。
|
||||
/// - Returns: IMK 選字窗專用的候選字串陣列。
|
||||
override func candidates(_ sender: Any!) -> [Any]! {
|
||||
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||
if let state = state as? InputState.AssociatedPhrases {
|
||||
return state.candidates.map { theCandidate -> String in
|
||||
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
|
||||
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
|
||||
}
|
||||
}
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
return state.candidates.map { theCandidate -> String in
|
||||
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
|
||||
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
|
||||
}
|
||||
}
|
||||
if let state = state as? InputState.SymbolTable {
|
||||
return state.candidates.map { theCandidate -> String in
|
||||
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
|
||||
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
|
||||
}
|
||||
}
|
||||
return .init()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,16 +24,16 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
// MARK: - KeyHandler Delegate
|
||||
|
||||
extension ctlInputMethod: KeyHandlerDelegate {
|
||||
func ctlCandidate() -> ctlCandidate { ctlInputMethod.ctlCandidateCurrent }
|
||||
func ctlCandidate() -> ctlCandidateProtocol { ctlInputMethod.ctlCandidateCurrent }
|
||||
|
||||
func keyHandler(
|
||||
_: KeyHandler, didSelectCandidateAt index: Int,
|
||||
ctlCandidate controller: ctlCandidate
|
||||
ctlCandidate controller: ctlCandidateProtocol
|
||||
) {
|
||||
ctlCandidate(controller, didSelectCandidateAtIndex: index)
|
||||
}
|
||||
|
@ -70,7 +70,54 @@ extension ctlInputMethod: KeyHandlerDelegate {
|
|||
// MARK: - Candidate Controller Delegate
|
||||
|
||||
extension ctlInputMethod: ctlCandidateDelegate {
|
||||
func candidateCountForController(_ controller: ctlCandidate) -> Int {
|
||||
func handleDelegateEvent(_ event: NSEvent!) -> Bool {
|
||||
/// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。
|
||||
/// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。
|
||||
/// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false,
|
||||
/// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。
|
||||
if event.type == .flagsChanged {
|
||||
return false
|
||||
}
|
||||
|
||||
// 準備修飾鍵,用來判定是否需要利用就地新增語彙時的 Enter 鍵來砍詞。
|
||||
ctlInputMethod.areWeDeleting = event.modifierFlags.contains([.shift, .command])
|
||||
|
||||
var textFrame = NSRect.zero
|
||||
|
||||
let attributes: [AnyHashable: Any]? = client().attributes(
|
||||
forCharacterIndex: 0, lineHeightRectangle: &textFrame
|
||||
)
|
||||
|
||||
let isTypingVertical =
|
||||
(attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false
|
||||
|
||||
if client().bundleIdentifier()
|
||||
== "org.atelierInmu.vChewing.vChewingPhraseEditor"
|
||||
{
|
||||
IME.areWeUsingOurOwnPhraseEditor = true
|
||||
} else {
|
||||
IME.areWeUsingOurOwnPhraseEditor = false
|
||||
}
|
||||
|
||||
let input = InputSignal(event: event, isVerticalTyping: isTypingVertical)
|
||||
|
||||
// 無法列印的訊號輸入,一概不作處理。
|
||||
// 這個過程不能放在 KeyHandler 內,否則不會起作用。
|
||||
if !input.charCode.isPrintable {
|
||||
return false
|
||||
}
|
||||
|
||||
/// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。
|
||||
/// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。
|
||||
let result = keyHandler.handleCandidate(state: state, input: input) { newState in
|
||||
self.handle(state: newState)
|
||||
} errorCallback: {
|
||||
clsSFX.beep()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int {
|
||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
return state.candidates.count
|
||||
|
@ -80,7 +127,20 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
return 0
|
||||
}
|
||||
|
||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
|
||||
/// 直接給出全部的候選字詞的字音配對陣列
|
||||
/// - Parameter controller: 對應的控制器。因為有唯一解,所以填錯了也不會有影響。
|
||||
/// - Returns: 候選字詞陣列(字音配對)。
|
||||
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] {
|
||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
return state.candidates
|
||||
} else if let state = state as? InputState.AssociatedPhrases {
|
||||
return state.candidates
|
||||
}
|
||||
return .init()
|
||||
}
|
||||
|
||||
func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)
|
||||
-> (String, String)
|
||||
{
|
||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||
|
@ -92,7 +152,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
return ("", "")
|
||||
}
|
||||
|
||||
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) {
|
||||
func ctlCandidate(_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int) {
|
||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||
|
||||
if let state = state as? InputState.SymbolTable,
|
||||
|
|
|
@ -81,11 +81,11 @@ extension ctlInputMethod {
|
|||
/// 該問題徹底解決的價值並不大,直接等到 macOS 10.x 全線淘汰之後用 SwiftUI 重寫選字窗吧。
|
||||
|
||||
if isCandidateWindowVertical { // 縱排輸入時強制使用縱排選字窗
|
||||
ctlInputMethod.ctlCandidateCurrent = .init(.vertical)
|
||||
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical)
|
||||
} else if mgrPrefs.useHorizontalCandidateList {
|
||||
ctlInputMethod.ctlCandidateCurrent = .init(.horizontal)
|
||||
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
|
||||
} else {
|
||||
ctlInputMethod.ctlCandidateCurrent = .init(.vertical)
|
||||
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical)
|
||||
}
|
||||
|
||||
// set the attributes for the candidate panel (which uses NSAttributedString)
|
||||
|
@ -100,12 +100,22 @@ extension ctlInputMethod {
|
|||
}
|
||||
|
||||
func candidateFont(name: String?, size: CGFloat) -> NSFont {
|
||||
let currentMUIFont =
|
||||
(keyHandler.inputMode == InputMode.imeModeCHS)
|
||||
? "Sarasa Term Slab SC" : "Sarasa Term Slab TC"
|
||||
var finalReturnFont =
|
||||
NSFont(name: currentMUIFont, size: size) ?? NSFont.systemFont(ofSize: size)
|
||||
// 對更紗黑體的依賴到 macOS 11 Big Sur 為止。macOS 12 Monterey 開始則依賴系統內建的函式使用蘋方來處理。
|
||||
var finalReturnFont: NSFont =
|
||||
{
|
||||
switch IME.currentInputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
return CTFontCreateUIFontForLanguage(.system, size, "zh-Hans" as CFString)
|
||||
case InputMode.imeModeCHT:
|
||||
return (mgrPrefs.shiftJISShinjitaiOutputEnabled || mgrPrefs.chineseConversionEnabled)
|
||||
? CTFontCreateUIFontForLanguage(.system, size, "ja" as CFString)
|
||||
: CTFontCreateUIFontForLanguage(.system, size, "zh-Hant" as CFString)
|
||||
default:
|
||||
return CTFontCreateUIFontForLanguage(.system, size, nil)
|
||||
}
|
||||
}()
|
||||
?? NSFont.systemFont(ofSize: size)
|
||||
// 上述方法對 macOS 10.11-10.15 有效,但對 macOS 12 Monterey 無效(懷疑是 Bug)。
|
||||
// macOS 12 Monterey 開始就用系統內建的函式來處理,相關流程直接寫在 ctlCandidateUniversal 內。
|
||||
if #available(macOS 12.0, *) { finalReturnFont = NSFont.systemFont(ofSize: size) }
|
||||
if let name = name {
|
||||
return NSFont(name: name, size: size) ?? finalReturnFont
|
||||
|
|
|
@ -26,56 +26,58 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
import Cocoa
|
||||
|
||||
struct UserDef {
|
||||
static let kIsDebugModeEnabled = "_DebugMode"
|
||||
static let kMostRecentInputMode = "MostRecentInputMode"
|
||||
static let kUserDataFolderSpecified = "UserDataFolderSpecified"
|
||||
static let kCheckUpdateAutomatically = "CheckUpdateAutomatically"
|
||||
static let kMandarinParser = "MandarinParser"
|
||||
static let kBasicKeyboardLayout = "BasicKeyboardLayout"
|
||||
static let kShowPageButtonsInCandidateWindow = "ShowPageButtonsInCandidateWindow"
|
||||
static let kCandidateListTextSize = "CandidateListTextSize"
|
||||
static let kAppleLanguages = "AppleLanguages"
|
||||
static let kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles"
|
||||
static let kuseRearCursorMode = "useRearCursorMode"
|
||||
static let kUseHorizontalCandidateList = "UseHorizontalCandidateList"
|
||||
static let kComposingBufferSize = "ComposingBufferSize"
|
||||
static let kChooseCandidateUsingSpace = "ChooseCandidateUsingSpace"
|
||||
static let kCNS11643Enabled = "CNS11643Enabled"
|
||||
static let kSymbolInputEnabled = "SymbolInputEnabled"
|
||||
static let kChineseConversionEnabled = "ChineseConversionEnabled"
|
||||
static let kShiftJISShinjitaiOutputEnabled = "ShiftJISShinjitaiOutputEnabled"
|
||||
static let kCurrencyNumeralsEnabled = "CurrencyNumeralsEnabled"
|
||||
static let kHalfWidthPunctuationEnabled = "HalfWidthPunctuationEnable"
|
||||
static let kMoveCursorAfterSelectingCandidate = "MoveCursorAfterSelectingCandidate"
|
||||
static let kEscToCleanInputBuffer = "EscToCleanInputBuffer"
|
||||
static let kSpecifyShiftTabKeyBehavior = "SpecifyShiftTabKeyBehavior"
|
||||
static let kSpecifyShiftSpaceKeyBehavior = "SpecifyShiftSpaceKeyBehavior"
|
||||
static let kAllowBoostingSingleKanjiAsUserPhrase = "AllowBoostingSingleKanjiAsUserPhrase"
|
||||
static let kUseSCPCTypingMode = "UseSCPCTypingMode"
|
||||
static let kMaxCandidateLength = "MaxCandidateLength"
|
||||
static let kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep"
|
||||
static let kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer"
|
||||
static let kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin"
|
||||
static let kFetchSuggestionsFromUserOverrideModel = "FetchSuggestionsFromUserOverrideModel"
|
||||
static let kUseFixecCandidateOrderOnSelection = "UseFixecCandidateOrderOnSelection"
|
||||
static let kAutoCorrectReadingCombination = "AutoCorrectReadingCombination"
|
||||
static let kAlsoConfirmAssociatedCandidatesByEnter = "AlsoConfirmAssociatedCandidatesByEnter"
|
||||
public enum UserDef: String, CaseIterable {
|
||||
case kIsDebugModeEnabled = "_DebugMode"
|
||||
case kFailureFlagForUOMObservation = "_FailureFlag_UOMObservation"
|
||||
case kMostRecentInputMode = "MostRecentInputMode"
|
||||
case kUserDataFolderSpecified = "UserDataFolderSpecified"
|
||||
case kCheckUpdateAutomatically = "CheckUpdateAutomatically"
|
||||
case kMandarinParser = "MandarinParser"
|
||||
case kBasicKeyboardLayout = "BasicKeyboardLayout"
|
||||
case kShowPageButtonsInCandidateWindow = "ShowPageButtonsInCandidateWindow"
|
||||
case kCandidateListTextSize = "CandidateListTextSize"
|
||||
case kAppleLanguages = "AppleLanguages"
|
||||
case kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles"
|
||||
case kUseRearCursorMode = "useRearCursorMode"
|
||||
case kUseHorizontalCandidateList = "UseHorizontalCandidateList"
|
||||
case kComposingBufferSize = "ComposingBufferSize"
|
||||
case kChooseCandidateUsingSpace = "ChooseCandidateUsingSpace"
|
||||
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 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"
|
||||
|
||||
static let kCandidateTextFontName = "CandidateTextFontName"
|
||||
static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
|
||||
static let kCandidateKeys = "CandidateKeys"
|
||||
case kCandidateTextFontName = "CandidateTextFontName"
|
||||
case kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
|
||||
case kCandidateKeys = "CandidateKeys"
|
||||
|
||||
static let kAssociatedPhrasesEnabled = "AssociatedPhrasesEnabled"
|
||||
static let kPhraseReplacementEnabled = "PhraseReplacementEnabled"
|
||||
case kAssociatedPhrasesEnabled = "AssociatedPhrasesEnabled"
|
||||
case kPhraseReplacementEnabled = "PhraseReplacementEnabled"
|
||||
|
||||
static let kUsingHotKeySCPC = "UsingHotKeySCPC"
|
||||
static let kUsingHotKeyAssociates = "UsingHotKeyAssociates"
|
||||
static let kUsingHotKeyCNS = "UsingHotKeyCNS"
|
||||
static let kUsingHotKeyKangXi = "UsingHotKeyKangXi"
|
||||
static let kUsingHotKeyJIS = "UsingHotKeyJIS"
|
||||
static let kUsingHotKeyHalfWidthASCII = "UsingHotKeyHalfWidthASCII"
|
||||
static let kUsingHotKeyCurrencyNumerals = "UsingHotKeyCurrencyNumerals"
|
||||
case kUsingHotKeySCPC = "UsingHotKeySCPC"
|
||||
case kUsingHotKeyAssociates = "UsingHotKeyAssociates"
|
||||
case kUsingHotKeyCNS = "UsingHotKeyCNS"
|
||||
case kUsingHotKeyKangXi = "UsingHotKeyKangXi"
|
||||
case kUsingHotKeyJIS = "UsingHotKeyJIS"
|
||||
case kUsingHotKeyHalfWidthASCII = "UsingHotKeyHalfWidthASCII"
|
||||
case kUsingHotKeyCurrencyNumerals = "UsingHotKeyCurrencyNumerals"
|
||||
}
|
||||
|
||||
private let kDefaultCandidateListTextSize: CGFloat = 18
|
||||
|
@ -237,92 +239,117 @@ enum MandarinParser: Int {
|
|||
|
||||
public enum mgrPrefs {
|
||||
public static func setMissingDefaults() {
|
||||
UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled.rawValue)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.showPageButtonsInCandidateWindow, forKey: UserDef.kShowPageButtonsInCandidateWindow
|
||||
mgrPrefs.failureFlagForUOMObservation, forKey: UserDef.kFailureFlagForUOMObservation.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.candidateListTextSize, forKey: UserDef.kCandidateListTextSize)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.chooseCandidateUsingSpace, forKey: UserDef.kChooseCandidateUsingSpace)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode.rawValue)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.shouldAutoReloadUserDataFiles, forKey: UserDef.kShouldAutoReloadUserDataFiles
|
||||
mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.specifyShiftTabKeyBehavior, forKey: UserDef.kSpecifyShiftTabKeyBehavior
|
||||
mgrPrefs.showPageButtonsInCandidateWindow, forKey: UserDef.kShowPageButtonsInCandidateWindow.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled.rawValue)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.candidateListTextSize, forKey: UserDef.kCandidateListTextSize.rawValue)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.chooseCandidateUsingSpace, forKey: UserDef.kChooseCandidateUsingSpace.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.specifyShiftSpaceKeyBehavior, forKey: UserDef.kSpecifyShiftSpaceKeyBehavior
|
||||
)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.useRearCursorMode, forKey: UserDef.kuseRearCursorMode
|
||||
mgrPrefs.shouldAutoReloadUserDataFiles, forKey: UserDef.kShouldAutoReloadUserDataFiles.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.moveCursorAfterSelectingCandidate, forKey: UserDef.kMoveCursorAfterSelectingCandidate
|
||||
mgrPrefs.specifyShiftTabKeyBehavior, forKey: UserDef.kSpecifyShiftTabKeyBehavior.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.useHorizontalCandidateList, forKey: UserDef.kUseHorizontalCandidateList
|
||||
mgrPrefs.specifyShiftSpaceKeyBehavior, forKey: UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.cns11643Enabled, forKey: UserDef.kCNS11643Enabled)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode.rawValue)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled
|
||||
)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.showHanyuPinyinInCompositionBuffer, forKey: UserDef.kShowHanyuPinyinInCompositionBuffer
|
||||
mgrPrefs.associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.inlineDumpPinyinInLieuOfZhuyin, forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin
|
||||
mgrPrefs.useRearCursorMode, forKey: UserDef.kUseRearCursorMode.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase, forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase
|
||||
mgrPrefs.moveCursorAfterSelectingCandidate, forKey: UserDef.kMoveCursorAfterSelectingCandidate.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.fetchSuggestionsFromUserOverrideModel, forKey: UserDef.kFetchSuggestionsFromUserOverrideModel
|
||||
mgrPrefs.useHorizontalCandidateList, forKey: UserDef.kUseHorizontalCandidateList.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.cns11643Enabled, forKey: UserDef.kCNS11643Enabled.rawValue)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.useFixecCandidateOrderOnSelection, forKey: UserDef.kUseFixecCandidateOrderOnSelection
|
||||
mgrPrefs.shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.autoCorrectReadingCombination, forKey: UserDef.kAutoCorrectReadingCombination
|
||||
mgrPrefs.phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.alsoConfirmAssociatedCandidatesByEnter, forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter
|
||||
mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.currencyNumeralsEnabled, forKey: UserDef.kCurrencyNumeralsEnabled
|
||||
mgrPrefs.showHanyuPinyinInCompositionBuffer, forKey: UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.inlineDumpPinyinInLieuOfZhuyin, forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase, forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.fetchSuggestionsFromUserOverrideModel, forKey: UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.useFixecCandidateOrderOnSelection, forKey: UserDef.kUseFixecCandidateOrderOnSelection.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.autoCorrectReadingCombination, forKey: UserDef.kAutoCorrectReadingCombination.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.alsoConfirmAssociatedCandidatesByEnter, forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.currencyNumeralsEnabled, forKey: UserDef.kCurrencyNumeralsEnabled.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.keepReadingUponCompositionError, forKey: UserDef.kKeepReadingUponCompositionError.rawValue
|
||||
)
|
||||
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyCNS, forKey: UserDef.kUsingHotKeyCNS)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyKangXi, forKey: UserDef.kUsingHotKeyKangXi)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyJIS, forKey: UserDef.kUsingHotKeyJIS)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyHalfWidthASCII, forKey: UserDef.kUsingHotKeyHalfWidthASCII)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyCurrencyNumerals, forKey: UserDef.kUsingHotKeyCurrencyNumerals)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC.rawValue)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates.rawValue)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyCNS, forKey: UserDef.kUsingHotKeyCNS.rawValue)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyKangXi, forKey: UserDef.kUsingHotKeyKangXi.rawValue)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyJIS, forKey: UserDef.kUsingHotKeyJIS.rawValue)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.usingHotKeyHalfWidthASCII, forKey: UserDef.kUsingHotKeyHalfWidthASCII.rawValue
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.usingHotKeyCurrencyNumerals, forKey: UserDef.kUsingHotKeyCurrencyNumerals.rawValue
|
||||
)
|
||||
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kIsDebugModeEnabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kIsDebugModeEnabled.rawValue, defaultValue: false)
|
||||
static var isDebugModeEnabled: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kMostRecentInputMode, defaultValue: "")
|
||||
@UserDefault(key: UserDef.kFailureFlagForUOMObservation.rawValue, defaultValue: false)
|
||||
static var failureFlagForUOMObservation: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kMostRecentInputMode.rawValue, defaultValue: "")
|
||||
static var mostRecentInputMode: String
|
||||
|
||||
@UserDefault(key: UserDef.kCheckUpdateAutomatically, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kCheckUpdateAutomatically.rawValue, defaultValue: false)
|
||||
static var checkUpdateAutomatically: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUserDataFolderSpecified, defaultValue: "")
|
||||
@UserDefault(key: UserDef.kUserDataFolderSpecified.rawValue, defaultValue: "")
|
||||
static var userDataFolderSpecified: String
|
||||
|
||||
static func ifSpecifiedUserDataPathExistsInPlist() -> Bool {
|
||||
UserDefaults.standard.object(forKey: UserDef.kUserDataFolderSpecified) != nil
|
||||
UserDefaults.standard.object(forKey: UserDef.kUserDataFolderSpecified.rawValue) != nil
|
||||
}
|
||||
|
||||
static func resetSpecifiedUserDataFolder() {
|
||||
|
@ -330,10 +357,10 @@ public enum mgrPrefs {
|
|||
IME.initLangModels(userOnly: true)
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kAppleLanguages, defaultValue: [])
|
||||
@UserDefault(key: UserDef.kAppleLanguages.rawValue, defaultValue: [])
|
||||
static var appleLanguages: [String]
|
||||
|
||||
@UserDefault(key: UserDef.kMandarinParser, defaultValue: 0)
|
||||
@UserDefault(key: UserDef.kMandarinParser.rawValue, defaultValue: 0)
|
||||
static var mandarinParser: Int
|
||||
|
||||
static var mandarinParserName: String {
|
||||
|
@ -341,103 +368,106 @@ public enum mgrPrefs {
|
|||
}
|
||||
|
||||
@UserDefault(
|
||||
key: UserDef.kBasicKeyboardLayout, defaultValue: "com.apple.keylayout.ZhuyinBopomofo"
|
||||
key: UserDef.kBasicKeyboardLayout.rawValue, defaultValue: "com.apple.keylayout.ZhuyinBopomofo"
|
||||
)
|
||||
static var basicKeyboardLayout: String
|
||||
|
||||
@UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow.rawValue, defaultValue: true)
|
||||
static var showPageButtonsInCandidateWindow: Bool
|
||||
|
||||
@CandidateListTextSize(key: UserDef.kCandidateListTextSize)
|
||||
@CandidateListTextSize(key: UserDef.kCandidateListTextSize.rawValue)
|
||||
static var candidateListTextSize: CGFloat
|
||||
|
||||
static var minKeyLabelSize: CGFloat { kDefaultMinKeyLabelSize }
|
||||
|
||||
@UserDefault(key: UserDef.kShouldAutoReloadUserDataFiles, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kShouldAutoReloadUserDataFiles.rawValue, defaultValue: true)
|
||||
static var shouldAutoReloadUserDataFiles: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kuseRearCursorMode, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kUseRearCursorMode.rawValue, defaultValue: false)
|
||||
static var useRearCursorMode: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kMoveCursorAfterSelectingCandidate, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kMoveCursorAfterSelectingCandidate.rawValue, defaultValue: true)
|
||||
static var moveCursorAfterSelectingCandidate: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUseHorizontalCandidateList, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUseHorizontalCandidateList.rawValue, defaultValue: true)
|
||||
static var useHorizontalCandidateList: Bool
|
||||
|
||||
@ComposingBufferSize(key: UserDef.kComposingBufferSize)
|
||||
@ComposingBufferSize(key: UserDef.kComposingBufferSize.rawValue)
|
||||
static var composingBufferSize: Int
|
||||
|
||||
@UserDefault(key: UserDef.kChooseCandidateUsingSpace, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kChooseCandidateUsingSpace.rawValue, defaultValue: true)
|
||||
static var chooseCandidateUsingSpace: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue, defaultValue: false)
|
||||
static var allowBoostingSingleKanjiAsUserPhrase: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kFetchSuggestionsFromUserOverrideModel, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue, defaultValue: true)
|
||||
static var fetchSuggestionsFromUserOverrideModel: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUseFixecCandidateOrderOnSelection, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kUseFixecCandidateOrderOnSelection.rawValue, defaultValue: false)
|
||||
static var useFixecCandidateOrderOnSelection: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kAutoCorrectReadingCombination, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kAutoCorrectReadingCombination.rawValue, defaultValue: true)
|
||||
static var autoCorrectReadingCombination: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue, defaultValue: true)
|
||||
static var alsoConfirmAssociatedCandidatesByEnter: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue, defaultValue: false)
|
||||
static var keepReadingUponCompositionError: Bool
|
||||
|
||||
static var minCandidateLength: Int {
|
||||
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kUseSCPCTypingMode, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kUseSCPCTypingMode.rawValue, defaultValue: false)
|
||||
static var useSCPCTypingMode: Bool
|
||||
|
||||
static func toggleSCPCTypingModeEnabled() -> Bool {
|
||||
useSCPCTypingMode = !useSCPCTypingMode
|
||||
UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode)
|
||||
UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode.rawValue)
|
||||
return useSCPCTypingMode
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kMaxCandidateLength, defaultValue: 10)
|
||||
@UserDefault(key: UserDef.kMaxCandidateLength.rawValue, defaultValue: 10)
|
||||
static var maxCandidateLength: Int
|
||||
|
||||
@UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep.rawValue, defaultValue: true)
|
||||
static var shouldNotFartInLieuOfBeep: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kShowHanyuPinyinInCompositionBuffer, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue, defaultValue: false)
|
||||
static var showHanyuPinyinInCompositionBuffer: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kInlineDumpPinyinInLieuOfZhuyin, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue, defaultValue: false)
|
||||
static var inlineDumpPinyinInLieuOfZhuyin: Bool
|
||||
|
||||
static func toggleShouldNotFartInLieuOfBeep() -> Bool {
|
||||
shouldNotFartInLieuOfBeep = !shouldNotFartInLieuOfBeep
|
||||
UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep)
|
||||
UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep.rawValue)
|
||||
return shouldNotFartInLieuOfBeep
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kCNS11643Enabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kCNS11643Enabled.rawValue, defaultValue: false)
|
||||
static var cns11643Enabled: Bool
|
||||
|
||||
static func toggleCNS11643Enabled() -> Bool {
|
||||
cns11643Enabled = !cns11643Enabled
|
||||
mgrLangModel.setCNSEnabled(cns11643Enabled) // 很重要
|
||||
UserDefaults.standard.set(cns11643Enabled, forKey: UserDef.kCNS11643Enabled)
|
||||
UserDefaults.standard.set(cns11643Enabled, forKey: UserDef.kCNS11643Enabled.rawValue)
|
||||
return cns11643Enabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kSymbolInputEnabled, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kSymbolInputEnabled.rawValue, defaultValue: true)
|
||||
static var symbolInputEnabled: Bool
|
||||
|
||||
static func toggleSymbolInputEnabled() -> Bool {
|
||||
symbolInputEnabled = !symbolInputEnabled
|
||||
mgrLangModel.setSymbolEnabled(symbolInputEnabled) // 很重要
|
||||
UserDefaults.standard.set(symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled)
|
||||
UserDefaults.standard.set(symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled.rawValue)
|
||||
return symbolInputEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kChineseConversionEnabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kChineseConversionEnabled.rawValue, defaultValue: false)
|
||||
static var chineseConversionEnabled: Bool
|
||||
|
||||
@discardableResult static func toggleChineseConversionEnabled() -> Bool {
|
||||
|
@ -446,14 +476,14 @@ public enum mgrPrefs {
|
|||
if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled {
|
||||
toggleShiftJISShinjitaiOutputEnabled()
|
||||
UserDefaults.standard.set(
|
||||
shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled
|
||||
shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue
|
||||
)
|
||||
}
|
||||
UserDefaults.standard.set(chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled)
|
||||
UserDefaults.standard.set(chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled.rawValue)
|
||||
return chineseConversionEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kShiftJISShinjitaiOutputEnabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue, defaultValue: false)
|
||||
static var shiftJISShinjitaiOutputEnabled: Bool
|
||||
|
||||
@discardableResult static func toggleShiftJISShinjitaiOutputEnabled() -> Bool {
|
||||
|
@ -463,12 +493,12 @@ public enum mgrPrefs {
|
|||
toggleChineseConversionEnabled()
|
||||
}
|
||||
UserDefaults.standard.set(
|
||||
shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled
|
||||
shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue
|
||||
)
|
||||
return shiftJISShinjitaiOutputEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kCurrencyNumeralsEnabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kCurrencyNumeralsEnabled.rawValue, defaultValue: false)
|
||||
static var currencyNumeralsEnabled: Bool
|
||||
|
||||
static func toggleCurrencyNumeralsEnabled() -> Bool {
|
||||
|
@ -476,7 +506,7 @@ public enum mgrPrefs {
|
|||
return currencyNumeralsEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kHalfWidthPunctuationEnabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kHalfWidthPunctuationEnabled.rawValue, defaultValue: false)
|
||||
static var halfWidthPunctuationEnabled: Bool
|
||||
|
||||
static func toggleHalfWidthPunctuationEnabled() -> Bool {
|
||||
|
@ -484,24 +514,24 @@ public enum mgrPrefs {
|
|||
return halfWidthPunctuationEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kEscToCleanInputBuffer, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kEscToCleanInputBuffer.rawValue, defaultValue: true)
|
||||
static var escToCleanInputBuffer: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kSpecifyShiftTabKeyBehavior, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kSpecifyShiftTabKeyBehavior.rawValue, defaultValue: false)
|
||||
static var specifyShiftTabKeyBehavior: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kSpecifyShiftSpaceKeyBehavior, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue, defaultValue: false)
|
||||
static var specifyShiftSpaceKeyBehavior: Bool
|
||||
|
||||
// MARK: - Optional settings
|
||||
|
||||
@UserDefault(key: UserDef.kCandidateTextFontName, defaultValue: nil)
|
||||
@UserDefault(key: UserDef.kCandidateTextFontName.rawValue, defaultValue: nil)
|
||||
static var candidateTextFontName: String?
|
||||
|
||||
@UserDefault(key: UserDef.kCandidateKeyLabelFontName, defaultValue: nil)
|
||||
@UserDefault(key: UserDef.kCandidateKeyLabelFontName.rawValue, defaultValue: nil)
|
||||
static var candidateKeyLabelFontName: String?
|
||||
|
||||
@UserDefault(key: UserDef.kCandidateKeys, defaultValue: kDefaultKeys)
|
||||
@UserDefault(key: UserDef.kCandidateKeys.rawValue, defaultValue: kDefaultKeys)
|
||||
static var candidateKeys: String
|
||||
|
||||
static var defaultCandidateKeys: String {
|
||||
|
@ -566,46 +596,46 @@ public enum mgrPrefs {
|
|||
}
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kPhraseReplacementEnabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kPhraseReplacementEnabled.rawValue, defaultValue: false)
|
||||
static var phraseReplacementEnabled: Bool
|
||||
|
||||
static func togglePhraseReplacementEnabled() -> Bool {
|
||||
phraseReplacementEnabled = !phraseReplacementEnabled
|
||||
mgrLangModel.setPhraseReplacementEnabled(phraseReplacementEnabled)
|
||||
UserDefaults.standard.set(phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled)
|
||||
UserDefaults.standard.set(phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled.rawValue)
|
||||
return phraseReplacementEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kAssociatedPhrasesEnabled, defaultValue: false)
|
||||
@UserDefault(key: UserDef.kAssociatedPhrasesEnabled.rawValue, defaultValue: false)
|
||||
static var associatedPhrasesEnabled: Bool
|
||||
|
||||
static func toggleAssociatedPhrasesEnabled() -> Bool {
|
||||
associatedPhrasesEnabled = !associatedPhrasesEnabled
|
||||
UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled)
|
||||
UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled.rawValue)
|
||||
return associatedPhrasesEnabled
|
||||
}
|
||||
|
||||
// MARK: - Keyboard HotKey Enable / Disable
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeySCPC, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUsingHotKeySCPC.rawValue, defaultValue: true)
|
||||
static var usingHotKeySCPC: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyAssociates, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUsingHotKeyAssociates.rawValue, defaultValue: true)
|
||||
static var usingHotKeyAssociates: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyCNS, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUsingHotKeyCNS.rawValue, defaultValue: true)
|
||||
static var usingHotKeyCNS: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyKangXi, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUsingHotKeyKangXi.rawValue, defaultValue: true)
|
||||
static var usingHotKeyKangXi: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyJIS, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUsingHotKeyJIS.rawValue, defaultValue: true)
|
||||
static var usingHotKeyJIS: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyHalfWidthASCII, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUsingHotKeyHalfWidthASCII.rawValue, defaultValue: true)
|
||||
static var usingHotKeyHalfWidthASCII: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyCurrencyNumerals, defaultValue: true)
|
||||
@UserDefault(key: UserDef.kUsingHotKeyCurrencyNumerals.rawValue, defaultValue: true)
|
||||
static var usingHotKeyCurrencyNumerals: Bool
|
||||
}
|
||||
|
||||
|
@ -614,45 +644,23 @@ public enum mgrPrefs {
|
|||
var snapshot: [String: Any]?
|
||||
|
||||
extension mgrPrefs {
|
||||
static var allKeys: [String] {
|
||||
[
|
||||
UserDef.kIsDebugModeEnabled, UserDef.kMostRecentInputMode, UserDef.kUserDataFolderSpecified,
|
||||
UserDef.kCheckUpdateAutomatically, UserDef.kMandarinParser, UserDef.kBasicKeyboardLayout,
|
||||
UserDef.kShowPageButtonsInCandidateWindow, UserDef.kCandidateListTextSize, UserDef.kAppleLanguages,
|
||||
UserDef.kShouldAutoReloadUserDataFiles, UserDef.kuseRearCursorMode, UserDef.kUseHorizontalCandidateList,
|
||||
UserDef.kComposingBufferSize, UserDef.kChooseCandidateUsingSpace, UserDef.kCNS11643Enabled,
|
||||
UserDef.kSymbolInputEnabled, UserDef.kChineseConversionEnabled, UserDef.kShiftJISShinjitaiOutputEnabled,
|
||||
UserDef.kHalfWidthPunctuationEnabled, UserDef.kMoveCursorAfterSelectingCandidate, UserDef.kEscToCleanInputBuffer,
|
||||
UserDef.kSpecifyShiftTabKeyBehavior, UserDef.kSpecifyShiftSpaceKeyBehavior,
|
||||
UserDef.kAllowBoostingSingleKanjiAsUserPhrase, UserDef.kUseSCPCTypingMode, UserDef.kMaxCandidateLength,
|
||||
UserDef.kShouldNotFartInLieuOfBeep, UserDef.kShowHanyuPinyinInCompositionBuffer,
|
||||
UserDef.kInlineDumpPinyinInLieuOfZhuyin, UserDef.kFetchSuggestionsFromUserOverrideModel,
|
||||
UserDef.kCandidateTextFontName, UserDef.kCandidateKeyLabelFontName, UserDef.kCandidateKeys,
|
||||
UserDef.kAssociatedPhrasesEnabled, UserDef.kPhraseReplacementEnabled, UserDef.kUsingHotKeySCPC,
|
||||
UserDef.kUsingHotKeyAssociates, UserDef.kUsingHotKeyCNS, UserDef.kUsingHotKeyKangXi, UserDef.kUsingHotKeyJIS,
|
||||
UserDef.kUsingHotKeyHalfWidthASCII, UserDef.kUseFixecCandidateOrderOnSelection,
|
||||
UserDef.kAutoCorrectReadingCombination, UserDef.kAlsoConfirmAssociatedCandidatesByEnter,
|
||||
UserDef.kCurrencyNumeralsEnabled, UserDef.kUsingHotKeyCurrencyNumerals,
|
||||
]
|
||||
}
|
||||
|
||||
func reset() {
|
||||
mgrPrefs.allKeys.forEach {
|
||||
UserDefaults.standard.removeObject(forKey: $0)
|
||||
UserDef.allCases.forEach {
|
||||
UserDefaults.standard.removeObject(forKey: $0.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
func makeSnapshot() -> [String: Any] {
|
||||
var dict = [String: Any]()
|
||||
mgrPrefs.allKeys.forEach {
|
||||
dict[$0] = UserDefaults.standard.object(forKey: $0)
|
||||
UserDef.allCases.forEach {
|
||||
dict[$0.rawValue] = UserDefaults.standard.object(forKey: $0.rawValue)
|
||||
}
|
||||
return dict
|
||||
}
|
||||
|
||||
func restore(from snapshot: [String: Any]) {
|
||||
mgrPrefs.allKeys.forEach {
|
||||
UserDefaults.standard.set(snapshot[$0], forKey: $0)
|
||||
UserDef.allCases.forEach {
|
||||
UserDefaults.standard.set(snapshot[$0.rawValue], forKey: $0.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ extension vChewing {
|
|||
do {
|
||||
strData = try String(contentsOfFile: path, encoding: .utf8).replacingOccurrences(of: "\t", with: " ")
|
||||
strData = strData.replacingOccurrences(of: "\r", with: "\n")
|
||||
strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach {
|
||||
strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach {
|
||||
let neta = strData[$0].split(separator: " ")
|
||||
if neta.count >= 2 {
|
||||
let theKey = String(neta[0])
|
||||
|
|
|
@ -87,7 +87,7 @@ extension vChewing {
|
|||
do {
|
||||
strData = try String(contentsOfFile: path, encoding: .utf8).replacingOccurrences(of: "\t", with: " ")
|
||||
strData = strData.replacingOccurrences(of: "\r", with: "\n")
|
||||
strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach {
|
||||
strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach {
|
||||
let neta = strData[$0].split(separator: " ")
|
||||
if neta.count >= 2, String(neta[0]).first != "#" {
|
||||
if !neta[0].isEmpty, !neta[1].isEmpty {
|
||||
|
|
|
@ -53,7 +53,7 @@ extension vChewing {
|
|||
do {
|
||||
strData = try String(contentsOfFile: path, encoding: .utf8).replacingOccurrences(of: "\t", with: " ")
|
||||
strData = strData.replacingOccurrences(of: "\r", with: "\n")
|
||||
strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach {
|
||||
strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach {
|
||||
let neta = strData[$0].split(separator: " ")
|
||||
if neta.count >= 2 {
|
||||
let theKey = String(neta[0])
|
||||
|
|
|
@ -326,9 +326,8 @@ extension vChewing.LMUserOverride {
|
|||
public func saveData(toURL fileURL: URL) {
|
||||
let encoder = JSONEncoder()
|
||||
do {
|
||||
if let jsonData = try? encoder.encode(mutLRUMap) {
|
||||
try jsonData.write(to: fileURL, options: .atomic)
|
||||
}
|
||||
guard let jsonData = try? encoder.encode(mutLRUMap) else { return }
|
||||
try jsonData.write(to: fileURL, options: .atomic)
|
||||
} catch {
|
||||
IME.prtDebugIntel("UOM Error: Unable to save data, abort saving. Details: \(error)")
|
||||
return
|
||||
|
|
|
@ -67,4 +67,6 @@ else {
|
|||
exit(-1)
|
||||
}
|
||||
|
||||
public let theServer = server
|
||||
|
||||
NSApp.run()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"vChewing" = "vChewing";
|
||||
"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability.";
|
||||
"About vChewing…" = "About vChewing…";
|
||||
"vChewing Preferences…" = "vChewing Preferences…";
|
||||
"Uninstallation" = "Uninstallation";
|
||||
|
@ -37,7 +39,7 @@
|
|||
"Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys.";
|
||||
"Maximum 15 candidate keys allowed." = "Maximum 15 candidate keys allowed.";
|
||||
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ Phrase replacement mode enabled, interfering user phrase entry.";
|
||||
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.";
|
||||
"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ Beware: Chars and Readings in buffer doesn't match.";
|
||||
"Per-Char Select Mode" = "Per-Char Select Mode";
|
||||
"CNS11643 Mode" = "CNS11643 Mode";
|
||||
"JIS Shinjitai Output" = "JIS Shinjitai Output";
|
||||
|
@ -85,6 +87,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)Space:";
|
||||
"Allow backspace-editing miscomposed readings" = "Allow backspace-editing miscomposed readings";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "Allow boosting / excluding a candidate of single kanji";
|
||||
"Allow using Enter key to confirm associated candidate selection" = "Allow using Enter key to confirm associated candidate selection";
|
||||
"Always use fixed listing order in candidate window" = "Always use fixed listing order in candidate window";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"vChewing" = "vChewing";
|
||||
"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability.";
|
||||
"About vChewing…" = "About vChewing…";
|
||||
"vChewing Preferences…" = "vChewing Preferences…";
|
||||
"Uninstallation" = "Uninstallation";
|
||||
|
@ -37,7 +39,7 @@
|
|||
"Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys.";
|
||||
"Maximum 15 candidate keys allowed." = "Maximum 15 candidate keys allowed.";
|
||||
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ Phrase replacement mode enabled, interfering user phrase entry.";
|
||||
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.";
|
||||
"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ Beware: Chars and Readings in buffer doesn't match.";
|
||||
"Per-Char Select Mode" = "Per-Char Select Mode";
|
||||
"CNS11643 Mode" = "CNS11643 Mode";
|
||||
"JIS Shinjitai Output" = "JIS Shinjitai Output";
|
||||
|
@ -85,6 +87,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)Space:";
|
||||
"Allow backspace-editing miscomposed readings" = "Allow backspace-editing miscomposed readings";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "Allow boosting / excluding a candidate of single kanji";
|
||||
"Allow using Enter key to confirm associated candidate selection" = "Allow using Enter key to confirm associated candidate selection";
|
||||
"Always use fixed listing order in candidate window" = "Always use fixed listing order in candidate window";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"vChewing" = "威注音入力アプリ";
|
||||
"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "臨時記憶モジュールの観測行為による威注音入力アプリの意外中止は発生した。威注音入力アプリの無事利用のために、既存臨時記憶データは全てお消しした。";
|
||||
"About vChewing…" = "威注音について…";
|
||||
"vChewing Preferences…" = "入力機能設定…";
|
||||
"Uninstallation" = "入力アプリの卸除(おろしのぞき)";
|
||||
|
@ -37,7 +39,7 @@
|
|||
"Please specify at least 4 candidate keys." = "言選り用キー陣列に少なくとも4つのキーをご登録ください。";
|
||||
"Maximum 15 candidate keys allowed." = "言選り用キー陣列には最多15つキー登録できます。";
|
||||
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 言葉置換機能稼働中、新添付言葉にも影響。";
|
||||
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 緩衝列の字数は読みの数と不同等のため、対処不可。";
|
||||
"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ 注意:緩衝列の字数は読みの数と不同等。";
|
||||
"Per-Char Select Mode" = "全候補入力モード";
|
||||
"CNS11643 Mode" = "全字庫モード";
|
||||
"JIS Shinjitai Output" = "JIS 新字体モード";
|
||||
|
@ -85,6 +87,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)Space:";
|
||||
"Allow backspace-editing miscomposed readings" = "効かぬ音読みを BackSpace で再編集";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "即排除/即最優先にできる候補の文字数の最低限は1字とする";
|
||||
"Allow using Enter key to confirm associated candidate selection" = "Enter キーを連想語彙候補の確認のために使う";
|
||||
"Always use fixed listing order in candidate window" = "候補文字を固定順番で陳列する";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"vChewing" = "威注音输入法";
|
||||
"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "威注音输入法的使用者半衰记忆模组在观测时崩溃,相关半衰记忆资料档案内容已全部清空。";
|
||||
"About vChewing…" = "关于威注音…";
|
||||
"vChewing Preferences…" = "威注音偏好设定…";
|
||||
"Uninstallation" = "卸除输入法";
|
||||
|
@ -37,7 +39,7 @@
|
|||
"Please specify at least 4 candidate keys." = "请至少指定四个选字键。";
|
||||
"Maximum 15 candidate keys allowed." = "选字键最多只能指定十五个。";
|
||||
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 语汇置换功能已启用,会波及语汇自订。";
|
||||
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 无法处理组字区字数与读音数不对应的情形。";
|
||||
"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ 注意:组字区字数与读音数不对应。";
|
||||
"Per-Char Select Mode" = "仿真逐字选字输入";
|
||||
"CNS11643 Mode" = "全字库模式";
|
||||
"JIS Shinjitai Output" = "JIS 新字体模式";
|
||||
|
@ -85,6 +87,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)空格键:";
|
||||
"Allow backspace-editing miscomposed readings" = "允许对无效的读音使用 BackSpace 编辑";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "将可以就地升权/排除的候选字词的最短词长设为单个汉字";
|
||||
"Allow using Enter key to confirm associated candidate selection" = "允许使用 Enter 确认当前选中的联想词";
|
||||
"Always use fixed listing order in candidate window" = "以固定顺序来陈列选字窗内的候选字";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"vChewing" = "威注音輸入法";
|
||||
"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "威注音輸入法的使用者半衰記憶模組在觀測時崩潰,相關半衰記憶資料檔案內容已全部清空。";
|
||||
"About vChewing…" = "關於威注音…";
|
||||
"vChewing Preferences…" = "威注音偏好設定…";
|
||||
"Uninstallation" = "卸除輸入法";
|
||||
|
@ -37,7 +39,7 @@
|
|||
"Please specify at least 4 candidate keys." = "請至少指定四個選字鍵。";
|
||||
"Maximum 15 candidate keys allowed." = "選字鍵最多只能指定十五個。";
|
||||
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 語彙置換功能已啟用,會波及語彙自訂。";
|
||||
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 無法處理組字區字數與讀音數不對應的情形。";
|
||||
"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ 注意:組字區字數與讀音數不對應。";
|
||||
"Per-Char Select Mode" = "模擬逐字選字輸入";
|
||||
"CNS11643 Mode" = "全字庫模式";
|
||||
"JIS Shinjitai Output" = "JIS 新字體模式";
|
||||
|
@ -85,6 +87,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)空格鍵:";
|
||||
"Allow backspace-editing miscomposed readings" = "允許對無效的讀音使用 BackSpace 編輯";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "將可以就地升權/排除的候選字詞的最短詞長設為單個漢字";
|
||||
"Allow using Enter key to confirm associated candidate selection" = "允許使用 Enter 確認當前選中的聯想詞";
|
||||
"Always use fixed listing order in candidate window" = "以固定順序來陳列選字窗內的候選字";
|
||||
|
|
|
@ -26,6 +26,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
import Cocoa
|
||||
|
||||
public enum CandidateLayout {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
public class CandidateKeyLabel: NSObject {
|
||||
public private(set) var key: String
|
||||
public private(set) var displayedText: String
|
||||
|
@ -38,21 +43,39 @@ public class CandidateKeyLabel: NSObject {
|
|||
}
|
||||
|
||||
public protocol ctlCandidateDelegate: AnyObject {
|
||||
func candidateCountForController(_ controller: ctlCandidate) -> Int
|
||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
|
||||
func handleDelegateEvent(_ event: NSEvent!) -> Bool
|
||||
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int
|
||||
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)]
|
||||
func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)
|
||||
-> (String, String)
|
||||
func ctlCandidate(
|
||||
_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int
|
||||
_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int
|
||||
)
|
||||
}
|
||||
|
||||
public class ctlCandidate: NSWindowController {
|
||||
public enum Layout {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
public protocol ctlCandidateProtocol {
|
||||
var currentLayout: CandidateLayout { get set }
|
||||
var delegate: ctlCandidateDelegate? { get set }
|
||||
var selectedCandidateIndex: Int { get set }
|
||||
var visible: Bool { get set }
|
||||
var windowTopLeftPoint: NSPoint { get set }
|
||||
var keyLabels: [CandidateKeyLabel] { get set }
|
||||
var keyLabelFont: NSFont { get set }
|
||||
var candidateFont: NSFont { get set }
|
||||
var tooltip: String { get set }
|
||||
|
||||
public var currentLayout: Layout = .horizontal
|
||||
init(_ layout: CandidateLayout)
|
||||
func reloadData()
|
||||
func showNextPage() -> Bool
|
||||
func showPreviousPage() -> Bool
|
||||
func highlightNextCandidate() -> Bool
|
||||
func highlightPreviousCandidate() -> Bool
|
||||
func candidateIndexAtKeyLabelIndex(_: Int) -> Int
|
||||
func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat)
|
||||
}
|
||||
|
||||
public class ctlCandidate: NSWindowController, ctlCandidateProtocol {
|
||||
public var currentLayout: CandidateLayout = .horizontal
|
||||
public weak var delegate: ctlCandidateDelegate? {
|
||||
didSet {
|
||||
reloadData()
|
||||
|
@ -85,6 +108,16 @@ public class ctlCandidate: NSWindowController {
|
|||
}
|
||||
}
|
||||
|
||||
public required init(_: CandidateLayout = .horizontal) {
|
||||
super.init(window: .init())
|
||||
visible = false
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||
.map {
|
||||
CandidateKeyLabel(key: $0, displayedText: $0)
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
1. The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
2. 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 above.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import InputMethodKit
|
||||
|
||||
public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
|
||||
public var currentLayout: CandidateLayout = .horizontal
|
||||
|
||||
public weak var delegate: ctlCandidateDelegate? {
|
||||
didSet {
|
||||
reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedCandidateIndex: Int = .max
|
||||
|
||||
public var visible: Bool = false {
|
||||
didSet {
|
||||
if visible {
|
||||
show()
|
||||
} else {
|
||||
hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var windowTopLeftPoint: NSPoint = .init(x: 0, y: 0) {
|
||||
didSet {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
|
||||
self.set(windowTopLeftPoint: self.windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||
.map {
|
||||
CandidateKeyLabel(key: $0, displayedText: $0)
|
||||
}
|
||||
|
||||
public var keyLabelFont: NSFont = NSFont.monospacedDigitSystemFont(
|
||||
ofSize: 14, weight: .medium
|
||||
)
|
||||
public var candidateFont: NSFont = NSFont.systemFont(ofSize: 18)
|
||||
public var tooltip: String = ""
|
||||
|
||||
var keyCount = 0
|
||||
var displayedCandidates = [String]()
|
||||
|
||||
public func specifyLayout(_ layout: CandidateLayout = .horizontal) {
|
||||
currentLayout = layout
|
||||
switch currentLayout {
|
||||
case .horizontal:
|
||||
setPanelType(kIMKScrollingGridCandidatePanel)
|
||||
case .vertical:
|
||||
setPanelType(kIMKSingleColumnScrollingCandidatePanel)
|
||||
}
|
||||
setAttributes([IMKCandidatesSendServerKeyEventFirst: false])
|
||||
}
|
||||
|
||||
public required init(_ layout: CandidateLayout = .horizontal) {
|
||||
super.init(server: theServer, panelType: kIMKScrollingGridCandidatePanel)
|
||||
specifyLayout(layout)
|
||||
visible = false
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func reloadData() {
|
||||
guard let delegate = delegate else { return }
|
||||
let candidates = delegate.candidatesForController(self).map { theCandidate -> String in
|
||||
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
|
||||
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
|
||||
}
|
||||
setCandidateData(candidates)
|
||||
keyCount = selectionKeys().count
|
||||
selectedCandidateIndex = 0
|
||||
update()
|
||||
}
|
||||
|
||||
public func showNextPage() -> Bool {
|
||||
if selectedCandidateIndex == candidates(self).count - 1 { return false }
|
||||
selectedCandidateIndex = min(selectedCandidateIndex + keyCount, candidates(self).count - 1)
|
||||
return selectCandidate(withIdentifier: selectedCandidateIndex)
|
||||
}
|
||||
|
||||
public func showPreviousPage() -> Bool {
|
||||
if selectedCandidateIndex == 0 { return true }
|
||||
selectedCandidateIndex = max(selectedCandidateIndex - keyCount, 0)
|
||||
return selectCandidate(withIdentifier: selectedCandidateIndex)
|
||||
}
|
||||
|
||||
public func highlightNextCandidate() -> Bool {
|
||||
if selectedCandidateIndex == candidates(self).count - 1 { return false }
|
||||
selectedCandidateIndex = min(selectedCandidateIndex + 1, candidates(self).count - 1)
|
||||
return selectCandidate(withIdentifier: selectedCandidateIndex)
|
||||
}
|
||||
|
||||
public func highlightPreviousCandidate() -> Bool {
|
||||
if selectedCandidateIndex == 0 { return true }
|
||||
selectedCandidateIndex = max(selectedCandidateIndex - 1, 0)
|
||||
return selectCandidate(withIdentifier: selectedCandidateIndex)
|
||||
}
|
||||
|
||||
public func candidateIndexAtKeyLabelIndex(_: Int) -> Int {
|
||||
selectedCandidateIndex
|
||||
}
|
||||
|
||||
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight _: CGFloat = 0) {
|
||||
setCandidateFrameTopLeft(windowTopLeftPoint)
|
||||
}
|
||||
|
||||
override public func handle(_ event: NSEvent!, client _: Any!) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
return delegate.handleDelegateEvent(event)
|
||||
}
|
||||
}
|
|
@ -370,7 +370,7 @@ public class ctlCandidateUniversal: ctlCandidate {
|
|||
private var nextPageButton: NSButton
|
||||
private var pageCounterLabel: NSTextField
|
||||
private var currentPageIndex: Int = 0
|
||||
override public var currentLayout: Layout {
|
||||
override public var currentLayout: CandidateLayout {
|
||||
get { candidateView.isVerticalLayout ? .vertical : .horizontal }
|
||||
set {
|
||||
switch newValue {
|
||||
|
@ -380,7 +380,7 @@ public class ctlCandidateUniversal: ctlCandidate {
|
|||
}
|
||||
}
|
||||
|
||||
public init(_ layout: Layout = .horizontal) {
|
||||
public required init(_ layout: CandidateLayout = .horizontal) {
|
||||
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
|
||||
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
|
||||
let panel = NSPanel(
|
||||
|
@ -448,7 +448,8 @@ public class ctlCandidateUniversal: ctlCandidate {
|
|||
|
||||
// MARK: Post-Init()
|
||||
|
||||
super.init(window: panel)
|
||||
super.init(layout)
|
||||
window = panel
|
||||
currentLayout = layout
|
||||
|
||||
candidateView.target = self
|
||||
|
|
|
@ -28,19 +28,19 @@ import SwiftUI
|
|||
struct suiPrefPaneDictionary: View {
|
||||
private var fdrDefault = mgrLangModel.dataFolderPath(isDefaultFolder: true)
|
||||
@State private var tbxUserDataPathSpecified: String =
|
||||
UserDefaults.standard.string(forKey: UserDef.kUserDataFolderSpecified)
|
||||
UserDefaults.standard.string(forKey: UserDef.kUserDataFolderSpecified.rawValue)
|
||||
?? mgrLangModel.dataFolderPath(isDefaultFolder: true)
|
||||
@State private var selAutoReloadUserData: Bool = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kShouldAutoReloadUserDataFiles)
|
||||
@State private var selEnableCNS11643: Bool = UserDefaults.standard.bool(forKey: UserDef.kCNS11643Enabled)
|
||||
forKey: UserDef.kShouldAutoReloadUserDataFiles.rawValue)
|
||||
@State private var selEnableCNS11643: Bool = UserDefaults.standard.bool(forKey: UserDef.kCNS11643Enabled.rawValue)
|
||||
@State private var selEnableSymbolInputSupport: Bool = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kSymbolInputEnabled)
|
||||
forKey: UserDef.kSymbolInputEnabled.rawValue)
|
||||
@State private var selAllowBoostingSingleKanjiAsUserPhrase: Bool = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase)
|
||||
forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue)
|
||||
@State private var selFetchSuggestionsFromUserOverrideModel: Bool = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kFetchSuggestionsFromUserOverrideModel)
|
||||
forKey: UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue)
|
||||
@State private var selUseFixecCandidateOrderOnSelection: Bool = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kUseFixecCandidateOrderOnSelection)
|
||||
forKey: UserDef.kUseFixecCandidateOrderOnSelection.rawValue)
|
||||
private let contentWidth: Double = {
|
||||
switch mgrPrefs.appleLanguages[0] {
|
||||
case "ja":
|
||||
|
|
|
@ -29,27 +29,30 @@ import SwiftUI
|
|||
struct suiPrefPaneExperience: View {
|
||||
@State private var selSelectionKeysList = mgrPrefs.suggestedCandidateKeys
|
||||
@State private var selSelectionKeys =
|
||||
(UserDefaults.standard.string(forKey: UserDef.kCandidateKeys) ?? mgrPrefs.defaultCandidateKeys) as String
|
||||
(UserDefaults.standard.string(forKey: UserDef.kCandidateKeys.rawValue) ?? mgrPrefs.defaultCandidateKeys) as String
|
||||
@State private var selCursorPosition =
|
||||
UserDefaults.standard.bool(
|
||||
forKey: UserDef.kuseRearCursorMode) ? 1 : 0
|
||||
forKey: UserDef.kUseRearCursorMode.rawValue) ? 1 : 0
|
||||
@State private var selPushCursorAfterSelection = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kMoveCursorAfterSelectingCandidate)
|
||||
forKey: UserDef.kMoveCursorAfterSelectingCandidate.rawValue)
|
||||
@State private var selKeyBehaviorShiftTab =
|
||||
UserDefaults.standard.bool(forKey: UserDef.kSpecifyShiftTabKeyBehavior) ? 1 : 0
|
||||
UserDefaults.standard.bool(forKey: UserDef.kSpecifyShiftTabKeyBehavior.rawValue) ? 1 : 0
|
||||
@State private var selKeyBehaviorShiftSpace =
|
||||
UserDefaults.standard.bool(
|
||||
forKey: UserDef.kSpecifyShiftSpaceKeyBehavior) ? 1 : 0
|
||||
forKey: UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue) ? 1 : 0
|
||||
@State private var selKeyBehaviorSpaceForCallingCandidate = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kChooseCandidateUsingSpace)
|
||||
forKey: UserDef.kChooseCandidateUsingSpace.rawValue)
|
||||
@State private var selKeyBehaviorESCForClearingTheBuffer = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kEscToCleanInputBuffer)
|
||||
@State private var selEnableSCPCTypingMode = UserDefaults.standard.bool(forKey: UserDef.kUseSCPCTypingMode)
|
||||
@State private var selComposingBufferSize = UserDefaults.standard.integer(forKey: UserDef.kComposingBufferSize)
|
||||
forKey: UserDef.kEscToCleanInputBuffer.rawValue)
|
||||
@State private var selEnableSCPCTypingMode = UserDefaults.standard.bool(forKey: UserDef.kUseSCPCTypingMode.rawValue)
|
||||
@State private var selComposingBufferSize = UserDefaults.standard.integer(
|
||||
forKey: UserDef.kComposingBufferSize.rawValue)
|
||||
@State private var selAutoCorrectReadingCombination = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kAutoCorrectReadingCombination)
|
||||
forKey: UserDef.kAutoCorrectReadingCombination.rawValue)
|
||||
@State private var selAlsoConfirmAssociatedCandidatesByEnter = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter)
|
||||
forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue)
|
||||
@State private var selKeepReadingUponCompositionError = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kKeepReadingUponCompositionError.rawValue)
|
||||
private let contentWidth: Double = {
|
||||
switch mgrPrefs.appleLanguages[0] {
|
||||
case "ja":
|
||||
|
@ -188,6 +191,12 @@ struct suiPrefPaneExperience: View {
|
|||
).onChange(of: selAlsoConfirmAssociatedCandidatesByEnter) { value in
|
||||
mgrPrefs.alsoConfirmAssociatedCandidatesByEnter = value
|
||||
}
|
||||
Toggle(
|
||||
LocalizedStringKey("Allow backspace-editing miscomposed readings"),
|
||||
isOn: $selKeepReadingUponCompositionError
|
||||
).onChange(of: selKeepReadingUponCompositionError) { value in
|
||||
mgrPrefs.keepReadingUponCompositionError = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,29 +27,32 @@ import SwiftUI
|
|||
|
||||
@available(macOS 11.0, *)
|
||||
struct suiPrefPaneGeneral: View {
|
||||
@State private var selCandidateUIFontSize = UserDefaults.standard.integer(forKey: UserDef.kCandidateListTextSize)
|
||||
@State private var selCandidateUIFontSize = UserDefaults.standard.integer(
|
||||
forKey: UserDef.kCandidateListTextSize.rawValue)
|
||||
@State private var selUILanguage: [String] =
|
||||
IME.arrSupportedLocales.contains(
|
||||
((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages) == nil)
|
||||
? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages) as? [String] ?? ["auto"])[0])
|
||||
? ((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages) == nil)
|
||||
? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages) as? [String] ?? ["auto"])
|
||||
((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil)
|
||||
? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages.rawValue) as? [String] ?? ["auto"])[0])
|
||||
? ((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil)
|
||||
? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages.rawValue) as? [String] ?? ["auto"])
|
||||
: ["auto"]
|
||||
@State private var selEnableHorizontalCandidateLayout = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kUseHorizontalCandidateList)
|
||||
forKey: UserDef.kUseHorizontalCandidateList.rawValue)
|
||||
@State private var selShowPageButtonsInCandidateUI = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kShowPageButtonsInCandidateWindow)
|
||||
forKey: UserDef.kShowPageButtonsInCandidateWindow.rawValue)
|
||||
@State private var selEnableKanjiConvToKangXi = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kChineseConversionEnabled)
|
||||
forKey: UserDef.kChineseConversionEnabled.rawValue)
|
||||
@State private var selEnableKanjiConvToJIS = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kShiftJISShinjitaiOutputEnabled)
|
||||
forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue)
|
||||
@State private var selShowHanyuPinyinInCompositionBuffer = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kShowHanyuPinyinInCompositionBuffer)
|
||||
forKey: UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue)
|
||||
@State private var selInlineDumpPinyinInLieuOfZhuyin = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin)
|
||||
@State private var selEnableFartSuppressor = UserDefaults.standard.bool(forKey: UserDef.kShouldNotFartInLieuOfBeep)
|
||||
@State private var selEnableAutoUpdateCheck = UserDefaults.standard.bool(forKey: UserDef.kCheckUpdateAutomatically)
|
||||
@State private var selEnableDebugMode = UserDefaults.standard.bool(forKey: UserDef.kIsDebugModeEnabled)
|
||||
forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue)
|
||||
@State private var selEnableFartSuppressor = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kShouldNotFartInLieuOfBeep.rawValue)
|
||||
@State private var selEnableAutoUpdateCheck = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kCheckUpdateAutomatically.rawValue)
|
||||
@State private var selEnableDebugMode = UserDefaults.standard.bool(forKey: UserDef.kIsDebugModeEnabled.rawValue)
|
||||
private let contentWidth: Double = {
|
||||
switch mgrPrefs.appleLanguages[0] {
|
||||
case "ja":
|
||||
|
@ -94,14 +97,14 @@ struct suiPrefPaneGeneral: View {
|
|||
IME.prtDebugIntel(value[0])
|
||||
if selUILanguage == mgrPrefs.appleLanguages
|
||||
|| (selUILanguage[0] == "auto"
|
||||
&& UserDefaults.standard.object(forKey: UserDef.kAppleLanguages) == nil)
|
||||
&& UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil)
|
||||
{
|
||||
return
|
||||
}
|
||||
if selUILanguage[0] != "auto" {
|
||||
mgrPrefs.appleLanguages = value
|
||||
} else {
|
||||
UserDefaults.standard.removeObject(forKey: UserDef.kAppleLanguages)
|
||||
UserDefaults.standard.removeObject(forKey: UserDef.kAppleLanguages.rawValue)
|
||||
}
|
||||
NSLog("vChewing App self-terminated due to UI language change.")
|
||||
NSApplication.shared.terminate(nil)
|
||||
|
|
|
@ -26,19 +26,20 @@ import SwiftUI
|
|||
|
||||
@available(macOS 11.0, *)
|
||||
struct suiPrefPaneKeyboard: View {
|
||||
@State private var selMandarinParser = UserDefaults.standard.integer(forKey: UserDef.kMandarinParser)
|
||||
@State private var selMandarinParser = UserDefaults.standard.integer(forKey: UserDef.kMandarinParser.rawValue)
|
||||
@State private var selBasicKeyboardLayout: String =
|
||||
UserDefaults.standard.string(forKey: UserDef.kBasicKeyboardLayout) ?? mgrPrefs.basicKeyboardLayout
|
||||
UserDefaults.standard.string(forKey: UserDef.kBasicKeyboardLayout.rawValue) ?? mgrPrefs.basicKeyboardLayout
|
||||
|
||||
@State private var selUsingHotKeySCPC = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeySCPC)
|
||||
@State private var selUsingHotKeyAssociates = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyAssociates)
|
||||
@State private var selUsingHotKeyCNS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyCNS)
|
||||
@State private var selUsingHotKeyKangXi = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyKangXi)
|
||||
@State private var selUsingHotKeyJIS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyJIS)
|
||||
@State private var selUsingHotKeySCPC = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeySCPC.rawValue)
|
||||
@State private var selUsingHotKeyAssociates = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kUsingHotKeyAssociates.rawValue)
|
||||
@State private var selUsingHotKeyCNS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyCNS.rawValue)
|
||||
@State private var selUsingHotKeyKangXi = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyKangXi.rawValue)
|
||||
@State private var selUsingHotKeyJIS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyJIS.rawValue)
|
||||
@State private var selUsingHotKeyHalfWidthASCII = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kUsingHotKeyHalfWidthASCII)
|
||||
forKey: UserDef.kUsingHotKeyHalfWidthASCII.rawValue)
|
||||
@State private var selUsingHotKeyCurrencyNumerals = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kUsingHotKeyCurrencyNumerals)
|
||||
forKey: UserDef.kUsingHotKeyCurrencyNumerals.rawValue)
|
||||
|
||||
private let contentWidth: Double = {
|
||||
switch mgrPrefs.appleLanguages[0] {
|
||||
|
|
|
@ -57,13 +57,13 @@ class ctlAboutWindow: NSWindowController {
|
|||
)
|
||||
}
|
||||
|
||||
@IBAction func btnBugReport(_ sender: NSButton) {
|
||||
@IBAction func btnBugReport(_: NSButton) {
|
||||
if let url = URL(string: "https://vchewing.github.io/BUGREPORT.html") {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func btnWebsite(_ sender: NSButton) {
|
||||
@IBAction func btnWebsite(_: NSButton) {
|
||||
if let url = URL(string: "https://vchewing.github.io/") {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
|
|
|
@ -388,7 +388,8 @@ extension ctlPrefWindow: NSToolbarDelegate {
|
|||
item.label = title
|
||||
if #available(macOS 11.0, *) {
|
||||
item.image = NSImage(
|
||||
systemSymbolName: "wrench.and.screwdriver.fill", accessibilityDescription: "General Preferences")
|
||||
systemSymbolName: "wrench.and.screwdriver.fill", accessibilityDescription: "General Preferences"
|
||||
)
|
||||
} else {
|
||||
item.image = NSImage(named: NSImage.homeTemplateName)
|
||||
}
|
||||
|
@ -399,7 +400,8 @@ extension ctlPrefWindow: NSToolbarDelegate {
|
|||
item.label = title
|
||||
if #available(macOS 11.0, *) {
|
||||
item.image = NSImage(
|
||||
systemSymbolName: "person.fill.questionmark", accessibilityDescription: "Experiences Preferences")
|
||||
systemSymbolName: "person.fill.questionmark", accessibilityDescription: "Experiences Preferences"
|
||||
)
|
||||
} else {
|
||||
item.image = NSImage(named: NSImage.flowViewTemplateName)
|
||||
}
|
||||
|
@ -410,7 +412,8 @@ extension ctlPrefWindow: NSToolbarDelegate {
|
|||
item.label = title
|
||||
if #available(macOS 11.0, *) {
|
||||
item.image = NSImage(
|
||||
systemSymbolName: "character.book.closed.fill", accessibilityDescription: "Dictionary Preferences")
|
||||
systemSymbolName: "character.book.closed.fill", accessibilityDescription: "Dictionary Preferences"
|
||||
)
|
||||
} else {
|
||||
item.image = NSImage(named: NSImage.bookmarksTemplateName)
|
||||
}
|
||||
|
|
|
@ -591,12 +591,23 @@
|
|||
<binding destination="32" name="value" keyPath="values.AlsoConfirmAssociatedCandidatesByEnter" id="P1C-j9-N88"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HaB-rc-AcW">
|
||||
<rect key="frame" x="19" y="40.5" width="388" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="Allow backspace-editing miscomposed readings" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="chkKeepReadingUponCompositionError">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.KeepReadingUponCompositionError" id="ddF-qg-jes"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="n7q-ew-DYu" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="33" id="2gT-9Z-6F4"/>
|
||||
<constraint firstItem="109" firstAttribute="top" secondItem="YkJ-lr-EP6" secondAttribute="bottom" constant="9" id="3tl-Zc-C5n"/>
|
||||
<constraint firstItem="13" firstAttribute="top" secondItem="d5f-bq-dRQ" secondAttribute="bottom" constant="8" symbolic="YES" id="4pp-NX-pCc"/>
|
||||
<constraint firstItem="YkJ-lr-EP6" firstAttribute="top" secondItem="J0f-Aw-dxC" secondAttribute="bottom" constant="7" id="5fd-qi-hJ6"/>
|
||||
<constraint firstItem="HaB-rc-AcW" firstAttribute="leading" secondItem="6MM-WC-Mpd" secondAttribute="leading" id="7Fs-9l-g66"/>
|
||||
<constraint firstItem="d5f-bq-dRQ" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="20" id="9wo-7x-RKb"/>
|
||||
<constraint firstItem="TMn-LX-3Ub" firstAttribute="top" secondItem="7z2-DD-c58" secondAttribute="bottom" constant="9" id="AXY-LV-HMX"/>
|
||||
<constraint firstItem="7z2-DD-c58" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="34" id="BUo-Us-u2B"/>
|
||||
|
@ -618,6 +629,7 @@
|
|||
<constraint firstItem="IpX-f7-rTL" firstAttribute="top" secondItem="XWo-36-xGi" secondAttribute="top" constant="20" id="YaG-ab-LJH"/>
|
||||
<constraint firstItem="uHU-aL-du7" firstAttribute="top" secondItem="IpX-f7-rTL" secondAttribute="bottom" constant="8" symbolic="YES" id="aet-Zq-v6x"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="TMn-LX-3Ub" secondAttribute="trailing" constant="20" symbolic="YES" id="atz-4L-U9s"/>
|
||||
<constraint firstItem="HaB-rc-AcW" firstAttribute="top" secondItem="6MM-WC-Mpd" secondAttribute="bottom" constant="6" symbolic="YES" id="cBa-57-gWH"/>
|
||||
<constraint firstItem="ETa-09-qWI" firstAttribute="top" secondItem="IpX-f7-rTL" secondAttribute="bottom" constant="11" id="cx2-US-uOU"/>
|
||||
<constraint firstItem="6MM-WC-Mpd" firstAttribute="top" secondItem="j8R-Hj-3dj" secondAttribute="bottom" constant="6" id="dmy-Au-peI"/>
|
||||
<constraint firstItem="YkJ-lr-EP6" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="33" id="e3v-m2-co7"/>
|
||||
|
@ -630,6 +642,7 @@
|
|||
<constraint firstItem="6MM-WC-Mpd" firstAttribute="leading" secondItem="j8R-Hj-3dj" secondAttribute="leading" id="mbl-sV-kc8"/>
|
||||
<constraint firstItem="mzw-F2-aAQ" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="20" id="pHT-DE-qdx"/>
|
||||
<constraint firstItem="ETa-09-qWI" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="33" id="qFL-i6-eUT"/>
|
||||
<constraint firstItem="HaB-rc-AcW" firstAttribute="trailing" secondItem="6MM-WC-Mpd" secondAttribute="trailing" id="rA7-0f-akd"/>
|
||||
<constraint firstItem="JG6-RM-ZdJ" firstAttribute="top" secondItem="uHU-aL-du7" secondAttribute="bottom" constant="8" id="shS-7h-z7u"/>
|
||||
<constraint firstItem="TMn-LX-3Ub" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="20" id="tFX-Eg-X70"/>
|
||||
<constraint firstItem="J0f-Aw-dxC" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="20" id="v75-cS-TsO"/>
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"chkAlsoConfirmAssociatedCandidatesByEnter.title" = "Allow using Enter key to confirm associated candidate selection";
|
||||
"chkAutoCorrectReadingCombination.title" = "Automatically correct reading combinations when typing";
|
||||
"chkFetchSuggestionsFromUserOverrideModel.title" = "Applying typing suggestions from half-life user override model";
|
||||
"chkKeepReadingUponCompositionError.title" = "Allow backspace-editing miscomposed readings";
|
||||
"chkUseFixecCandidateOrderOnSelection.title" = "Always use fixed listing order in candidate window";
|
||||
"DbW-eq-ZdB.title" = "Starlight";
|
||||
"dIN-TZ-67g.title" = "Space to +cycle candidates, Shift+Space to +cycle pages";
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"chkAlsoConfirmAssociatedCandidatesByEnter.title" = "Enter キーを連想語彙候補の確認のために使う";
|
||||
"chkAutoCorrectReadingCombination.title" = "入力中で打ち間違った発音組み合わせを自動的に訂正する";
|
||||
"chkFetchSuggestionsFromUserOverrideModel.title" = "入力中で臨時記憶モジュールからお薦めの候補を自動的に選ぶ";
|
||||
"chkKeepReadingUponCompositionError.title" = "効かぬ音読みを BackSpace で再編集";
|
||||
"chkUseFixecCandidateOrderOnSelection.title" = "候補文字を固定順番で陳列する";
|
||||
"DbW-eq-ZdB.title" = "星光";
|
||||
"dIN-TZ-67g.title" = "Shift+Space で次のページ、Space で次の候補文字を";
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"chkAlsoConfirmAssociatedCandidatesByEnter.title" = "允许使用 Enter 确认当前选中的联想词";
|
||||
"chkAutoCorrectReadingCombination.title" = "敲字时自动纠正读音组合";
|
||||
"chkFetchSuggestionsFromUserOverrideModel.title" = "在敲字时自动套用来自半衰记忆模组的建议";
|
||||
"chkKeepReadingUponCompositionError.title" = "允许对无效的读音使用 BackSpace 编辑";
|
||||
"chkUseFixecCandidateOrderOnSelection.title" = "以固定顺序来陈列选字窗内的候选字";
|
||||
"DbW-eq-ZdB.title" = "星光";
|
||||
"dIN-TZ-67g.title" = "Shift+Space 换下一页,Space 换选下一个候选字。";
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"chkAlsoConfirmAssociatedCandidatesByEnter.title" = "允許使用 Enter 確認當前選中的聯想詞";
|
||||
"chkAutoCorrectReadingCombination.title" = "敲字時自動糾正讀音組合";
|
||||
"chkFetchSuggestionsFromUserOverrideModel.title" = "在敲字時自動套用來自半衰記憶模組的建議";
|
||||
"chkKeepReadingUponCompositionError.title" = "允許對無效的讀音使用 BackSpace 編輯";
|
||||
"chkUseFixecCandidateOrderOnSelection.title" = "以固定順序來陳列選字窗內的候選字";
|
||||
"DbW-eq-ZdB.title" = "星光";
|
||||
"dIN-TZ-67g.title" = "Shift+Space 換下一頁,Space 換選下一個候選字";
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.8.6</string>
|
||||
<string>1.8.7</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1986</string>
|
||||
<string>1987</string>
|
||||
<key>UpdateInfoEndpoint</key>
|
||||
<string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string>
|
||||
<key>UpdateInfoSite</key>
|
||||
|
|
|
@ -57,7 +57,7 @@ import Cocoa
|
|||
)
|
||||
}
|
||||
|
||||
@IBAction func btnWebsite(_ sender: NSButton) {
|
||||
@IBAction func btnWebsite(_: NSButton) {
|
||||
if let url = URL(string: "https://vchewing.github.io/") {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
|
|
|
@ -726,7 +726,7 @@
|
|||
<key>USE_HFS+_COMPRESSION</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.8.6</string>
|
||||
<string>1.8.7</string>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
5BF9DA2A28840E6200DBD48E /* template-replacements.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2528840E6200DBD48E /* template-replacements.txt */; };
|
||||
5BF9DA2B28840E6200DBD48E /* template-userphrases.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2628840E6200DBD48E /* template-userphrases.txt */; };
|
||||
5BF9DA2D288427E000DBD48E /* template-associatedPhrases-cht.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2C2884247800DBD48E /* template-associatedPhrases-cht.txt */; };
|
||||
5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */; };
|
||||
6A187E2616004C5900466B2E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A187E2816004C5900466B2E /* MainMenu.xib */; };
|
||||
6A225A1F23679F2600F685C6 /* NotarizedArchives in Resources */ = {isa = PBXBuildFile; fileRef = 6A225A1E23679F2600F685C6 /* NotarizedArchives */; };
|
||||
6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; };
|
||||
|
@ -320,6 +321,7 @@
|
|||
5BF9DA2528840E6200DBD48E /* template-replacements.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-replacements.txt"; sourceTree = "<group>"; usesTabs = 0; };
|
||||
5BF9DA2628840E6200DBD48E /* template-userphrases.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-userphrases.txt"; sourceTree = "<group>"; usesTabs = 0; };
|
||||
5BF9DA2C2884247800DBD48E /* template-associatedPhrases-cht.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; name = "template-associatedPhrases-cht.txt"; path = "../Data/components/cht/template-associatedPhrases-cht.txt"; sourceTree = "<group>"; };
|
||||
5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateIMK.swift; sourceTree = "<group>"; };
|
||||
5BFDF48C27B51867009523B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = "<group>"; };
|
||||
6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewing.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; };
|
||||
|
@ -570,6 +572,7 @@
|
|||
children = (
|
||||
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */,
|
||||
5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */,
|
||||
5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */,
|
||||
);
|
||||
path = CandidateUI;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1203,6 +1206,7 @@
|
|||
5BA9FD4727FEF3C9002DE248 /* PreferencesStyleController.swift in Sources */,
|
||||
5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */,
|
||||
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */,
|
||||
5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */,
|
||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
|
||||
5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */,
|
||||
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
|
||||
|
@ -1393,7 +1397,7 @@
|
|||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
|
@ -1403,7 +1407,7 @@
|
|||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
|
||||
|
@ -1432,13 +1436,13 @@
|
|||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
|
||||
|
@ -1469,7 +1473,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
|
@ -1490,7 +1494,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1519,7 +1523,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
@ -1536,7 +1540,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1650,7 +1654,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
@ -1678,7 +1682,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1705,7 +1709,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
@ -1727,7 +1731,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -1749,7 +1753,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1769,7 +1773,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1791,7 +1795,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1986;
|
||||
CURRENT_PROJECT_VERSION = 1987;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1805,7 +1809,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.6;
|
||||
MARKETING_VERSION = 1.8.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
@ -357,7 +357,7 @@ extension String {
|
|||
|
||||
extension vChewing.LMAssociates {
|
||||
public mutating func forceOpenStringInstead(_ strData: String) {
|
||||
strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach {
|
||||
strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach {
|
||||
let neta = strData[$0].split(separator: " ")
|
||||
if neta.count >= 2 {
|
||||
let theKey = String(neta[0])
|
||||
|
|
Loading…
Reference in New Issue