1.8.5 // Currency numerals, etc. Merge Gitee PR!69 from /upd/1.8.5
This commit is contained in:
commit
5ba2d51894
|
@ -27,6 +27,34 @@ import Foundation
|
|||
public class ChineseConverter {
|
||||
public static let shared = HotenkaChineseConverter.init(plistDir: mgrLangModel.getBundleDataPath("convdict"))
|
||||
|
||||
/// 漢字數字大寫轉換專用辭典,順序為:康熙、當代繁體中文、日文、簡體中文。
|
||||
private static let currencyNumeralDictTable: [String: (String, String, String, String)] = [
|
||||
"一": ("壹", "壹", "壹", "壹"), "二": ("貳", "貳", "弐", "贰"), "三": ("叄", "參", "参", "叁"),
|
||||
"四": ("肆", "肆", "肆", "肆"), "五": ("伍", "伍", "伍", "伍"), "六": ("陸", "陸", "陸", "陆"),
|
||||
"七": ("柒", "柒", "柒", "柒"), "八": ("捌", "捌", "捌", "捌"), "九": ("玖", "玖", "玖", "玖"),
|
||||
"十": ("拾", "拾", "拾", "拾"), "百": ("佰", "佰", "佰", "佰"), "千": ("仟", "仟", "仟", "仟"),
|
||||
"万": ("萬", "萬", "萬", "万"), "〇": ("零", "零", "零", "零"),
|
||||
]
|
||||
|
||||
/// 將指定字串內的小寫漢字數字轉換為大寫,會對轉換對象進行直接修改操作。
|
||||
/// - Parameter target: 轉換對象。
|
||||
public static func ensureCurrencyNumerals(target: inout String) {
|
||||
if !mgrPrefs.currencyNumeralsEnabled { return }
|
||||
for key in currencyNumeralDictTable.keys {
|
||||
guard let result = currencyNumeralDictTable[key] else { continue }
|
||||
if IME.currentInputMode == InputMode.imeModeCHS {
|
||||
target = target.replacingOccurrences(of: key, with: result.3) // Simplified Chinese
|
||||
continue
|
||||
}
|
||||
switch (mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled) {
|
||||
case (false, true), (true, true): target = target.replacingOccurrences(of: key, with: result.2) // JIS
|
||||
case (true, false): target = target.replacingOccurrences(of: key, with: result.0) // KangXi
|
||||
default: target = target.replacingOccurrences(of: key, with: result.1) // Contemporary
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/// CrossConvert.
|
||||
///
|
||||
/// - Parameter string: Text in Original Script.
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 94b215d444e1c43e81ad173e9c28e6769f74c330
|
||||
Subproject commit 2cbca5af7f281b7932878cda5e63f2dfed6b48b0
|
|
@ -115,6 +115,9 @@ enum KeyCodeBlackListed: UInt16 {
|
|||
/// 注意:第 95 號 Key Code(逗號)為 JIS 佈局特有的數字小鍵盤按鍵。
|
||||
let arrNumpadKeyCodes: [UInt16] = [65, 67, 69, 71, 75, 78, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91, 92, 95]
|
||||
|
||||
/// 主鍵盤區域的數字鍵的 KeyCode。
|
||||
let arrMainAreaNumKey: [UInt16] = [18, 19, 20, 21, 22, 23, 25, 26, 28, 29]
|
||||
|
||||
// CharCodes: https://theasciicode.com.ar/ascii-control-characters/horizontal-tab-ascii-code-9.html
|
||||
enum CharCode: UInt16 {
|
||||
case yajuusenpaiA = 114
|
||||
|
@ -128,27 +131,25 @@ enum CharCode: UInt16 {
|
|||
|
||||
struct InputSignal: CustomStringConvertible {
|
||||
private(set) var isTypingVertical: Bool
|
||||
private(set) var inputText: String?
|
||||
private(set) var inputText: String
|
||||
private(set) var inputTextIgnoringModifiers: String?
|
||||
private(set) var charCode: UInt16
|
||||
private(set) var keyCode: UInt16
|
||||
private var isFlagChanged: Bool
|
||||
private var flags: NSEvent.ModifierFlags
|
||||
private var cursorForwardKey: KeyCode = .kNone
|
||||
private var cursorBackwardKey: KeyCode = .kNone
|
||||
private var extraChooseCandidateKey: KeyCode = .kNone
|
||||
private var extraChooseCandidateKeyReverse: KeyCode = .kNone
|
||||
private var absorbedArrowKey: KeyCode = .kNone
|
||||
private var verticalTypingOnlyChooseCandidateKey: KeyCode = .kNone
|
||||
private var cursorForwardKey: KeyCode = .kNone // 12 o'clock
|
||||
private var cursorBackwardKey: KeyCode = .kNone // 6 o'clock
|
||||
private var cursorKeyClockRight: KeyCode = .kNone // 3 o'clock
|
||||
private var cursorKeyClockLeft: KeyCode = .kNone // 9 o'clock
|
||||
private(set) var emacsKey: EmacsKey
|
||||
|
||||
public init(
|
||||
inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
|
||||
inputText: String = "", keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
|
||||
isVerticalTyping: Bool = false, inputTextIgnoringModifiers: String? = nil
|
||||
) {
|
||||
self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "")
|
||||
self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText)
|
||||
self.inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
|
||||
inputTextIgnoringModifiers ?? inputText ?? "")
|
||||
inputTextIgnoringModifiers ?? inputText)
|
||||
self.flags = flags
|
||||
isFlagChanged = false
|
||||
isTypingVertical = isVerticalTyping
|
||||
|
@ -164,7 +165,7 @@ struct InputSignal: CustomStringConvertible {
|
|||
public init(event: NSEvent, isVerticalTyping: Bool = false) {
|
||||
inputText = AppleKeyboardConverter.cnvStringApple2ABC(event.characters ?? "")
|
||||
inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
|
||||
event.charactersIgnoringModifiers ?? "")
|
||||
event.charactersIgnoringModifiers ?? inputText)
|
||||
keyCode = event.keyCode
|
||||
flags = event.modifierFlags
|
||||
isFlagChanged = (event.type == .flagsChanged)
|
||||
|
@ -188,173 +189,83 @@ struct InputSignal: CustomStringConvertible {
|
|||
mutating func defineArrowKeys() {
|
||||
cursorForwardKey = isTypingVertical ? .kDownArrow : .kRightArrow
|
||||
cursorBackwardKey = isTypingVertical ? .kUpArrow : .kLeftArrow
|
||||
extraChooseCandidateKey = isTypingVertical ? .kLeftArrow : .kDownArrow
|
||||
extraChooseCandidateKeyReverse = isTypingVertical ? .kRightArrow : .kUpArrow
|
||||
absorbedArrowKey = isTypingVertical ? .kRightArrow : .kUpArrow
|
||||
verticalTypingOnlyChooseCandidateKey = isTypingVertical ? absorbedArrowKey : .kNone
|
||||
cursorKeyClockLeft = isTypingVertical ? .kRightArrow : .kUpArrow
|
||||
cursorKeyClockRight = isTypingVertical ? .kLeftArrow : .kDownArrow
|
||||
}
|
||||
|
||||
var description: String {
|
||||
"<inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), extraChooseCandidateKeyReverse:\(extraChooseCandidateKeyReverse), absorbedArrowKey:\(absorbedArrowKey), verticalTypingOnlyChooseCandidateKey:\(verticalTypingOnlyChooseCandidateKey), emacsKey:\(emacsKey), isTypingVertical:\(isTypingVertical)>"
|
||||
var result = "<[InputSignal] "
|
||||
result += "inputText:\(String(describing: inputText)), "
|
||||
result += "inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)), "
|
||||
result += "charCode:\(charCode), "
|
||||
result += "keyCode:\(keyCode), "
|
||||
result += "flags:\(flags), "
|
||||
result += "cursorForwardKey:\(cursorForwardKey), "
|
||||
result += "cursorBackwardKey:\(cursorBackwardKey), "
|
||||
result += "cursorKeyClockRight:\(cursorKeyClockRight), "
|
||||
result += "cursorKeyClockLeft:\(cursorKeyClockLeft), "
|
||||
result += "emacsKey:\(emacsKey), "
|
||||
result += "isTypingVertical:\(isTypingVertical)"
|
||||
result += ">"
|
||||
return result
|
||||
}
|
||||
|
||||
// 除了 ANSI charCode 以外,其餘一律過濾掉,免得純 Swift 版 KeyHandler 被餵屎。
|
||||
var isInvalidInput: Bool {
|
||||
switch charCode {
|
||||
case 0x20...0xFF: // ANSI charCode 範圍
|
||||
return false
|
||||
default:
|
||||
if isReservedKey, !isKeyCodeBlacklisted {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
var isInvalid: Bool { (0x20...0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted) }
|
||||
|
||||
var isKeyCodeBlacklisted: Bool {
|
||||
guard let code = KeyCodeBlackListed(rawValue: keyCode) else {
|
||||
return false
|
||||
}
|
||||
guard let code = KeyCodeBlackListed(rawValue: keyCode) else { return false }
|
||||
return code.rawValue != KeyCode.kNone.rawValue
|
||||
}
|
||||
|
||||
var isShiftHold: Bool {
|
||||
flags.contains([.shift])
|
||||
}
|
||||
|
||||
var isCommandHold: Bool {
|
||||
flags.contains([.command])
|
||||
}
|
||||
|
||||
var isControlHold: Bool {
|
||||
flags.contains([.control])
|
||||
}
|
||||
|
||||
var isControlHotKey: Bool {
|
||||
flags.contains([.control]) && inputText?.first?.isLetter ?? false
|
||||
}
|
||||
|
||||
var isOptionHold: Bool {
|
||||
flags.contains([.option])
|
||||
}
|
||||
|
||||
var isOptionHotKey: Bool {
|
||||
flags.contains([.option]) && inputText?.first?.isLetter ?? false
|
||||
}
|
||||
|
||||
var isCapsLockOn: Bool {
|
||||
flags.contains([.capsLock])
|
||||
}
|
||||
|
||||
var isNumericPad: Bool {
|
||||
flags.contains([.numericPad])
|
||||
}
|
||||
|
||||
var isFunctionKeyHold: Bool {
|
||||
flags.contains([.function])
|
||||
}
|
||||
|
||||
var isReservedKey: Bool {
|
||||
guard let code = KeyCode(rawValue: keyCode) else {
|
||||
return false
|
||||
}
|
||||
guard let code = KeyCode(rawValue: keyCode) else { return false }
|
||||
return code.rawValue != KeyCode.kNone.rawValue
|
||||
}
|
||||
|
||||
// 摁 Alt+Shift+主鍵盤區域數字鍵 的話,根據不同的 macOS 鍵盤佈局種類,會出現不同的符號結果。
|
||||
// 然而呢,KeyCode 卻是一致的。於是這裡直接準備一個換算表來用。
|
||||
let mapMainAreaNumKey: [UInt16: String] = [
|
||||
18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6", 26: "7", 28: "8", 25: "9", 29: "0",
|
||||
]
|
||||
|
||||
/// 單獨用 flags 來判定數字小鍵盤輸入的方法已經失效了,所以必須再增補用 KeyCode 判定的方法。
|
||||
var isNumericPadAreaKey: Bool {
|
||||
arrNumpadKeyCodes.contains(keyCode)
|
||||
}
|
||||
var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) }
|
||||
var isMainAreaNumKey: Bool { arrMainAreaNumKey.contains(keyCode) }
|
||||
var isShiftHold: Bool { flags.contains([.shift]) }
|
||||
var isCommandHold: Bool { flags.contains([.command]) }
|
||||
var isControlHold: Bool { flags.contains([.control]) }
|
||||
var isControlHotKey: Bool { flags.contains([.control]) && inputText.first?.isLetter ?? false }
|
||||
var isOptionHold: Bool { flags.contains([.option]) }
|
||||
var isOptionHotKey: Bool { flags.contains([.option]) && inputText.first?.isLetter ?? false }
|
||||
var isCapsLockOn: Bool { flags.contains([.capsLock]) }
|
||||
var isFunctionKeyHold: Bool { flags.contains([.function]) }
|
||||
var isNonLaptopFunctionKey: Bool { flags.contains([.numericPad]) && !isNumericPadKey }
|
||||
var isEnter: Bool { [KeyCode.kCarriageReturn, KeyCode.kLineFeed].contains(KeyCode(rawValue: keyCode)) }
|
||||
var isTab: Bool { KeyCode(rawValue: keyCode) == KeyCode.kTab }
|
||||
var isUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kUpArrow }
|
||||
var isDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kDownArrow }
|
||||
var isLeft: Bool { KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow }
|
||||
var isRight: Bool { KeyCode(rawValue: keyCode) == KeyCode.kRightArrow }
|
||||
var isPageUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageUp }
|
||||
var isPageDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageDown }
|
||||
var isSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kSpace }
|
||||
var isBackSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kBackSpace }
|
||||
var isEsc: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEscape }
|
||||
var isHome: Bool { KeyCode(rawValue: keyCode) == KeyCode.kHome }
|
||||
var isEnd: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEnd }
|
||||
var isDelete: Bool { KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete }
|
||||
var isCursorBackward: Bool { KeyCode(rawValue: keyCode) == cursorBackwardKey }
|
||||
var isCursorForward: Bool { KeyCode(rawValue: keyCode) == cursorForwardKey }
|
||||
var isCursorClockRight: Bool { KeyCode(rawValue: keyCode) == cursorKeyClockRight }
|
||||
var isCursorClockLeft: Bool { KeyCode(rawValue: keyCode) == cursorKeyClockLeft }
|
||||
|
||||
var isTab: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kTab
|
||||
}
|
||||
|
||||
var isEnter: Bool {
|
||||
(KeyCode(rawValue: keyCode) == KeyCode.kCarriageReturn)
|
||||
|| (KeyCode(rawValue: keyCode) == KeyCode.kLineFeed)
|
||||
}
|
||||
|
||||
var isUp: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kUpArrow
|
||||
}
|
||||
|
||||
var isDown: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kDownArrow
|
||||
}
|
||||
|
||||
var isLeft: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow
|
||||
}
|
||||
|
||||
var isRight: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kRightArrow
|
||||
}
|
||||
|
||||
var isPageUp: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kPageUp
|
||||
}
|
||||
|
||||
var isPageDown: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kPageDown
|
||||
}
|
||||
|
||||
var isSpace: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kSpace
|
||||
}
|
||||
|
||||
var isBackSpace: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kBackSpace
|
||||
}
|
||||
|
||||
var isEsc: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kEscape
|
||||
}
|
||||
|
||||
var isHome: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kHome
|
||||
}
|
||||
|
||||
var isEnd: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kEnd
|
||||
}
|
||||
|
||||
var isDelete: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete
|
||||
}
|
||||
|
||||
var isCursorBackward: Bool {
|
||||
KeyCode(rawValue: keyCode) == cursorBackwardKey
|
||||
}
|
||||
|
||||
var isCursorForward: Bool {
|
||||
KeyCode(rawValue: keyCode) == cursorForwardKey
|
||||
}
|
||||
|
||||
var isAbsorbedArrowKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == absorbedArrowKey
|
||||
}
|
||||
|
||||
var isExtraChooseCandidateKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == extraChooseCandidateKey
|
||||
}
|
||||
|
||||
var isExtraChooseCandidateKeyReverse: Bool {
|
||||
KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse
|
||||
}
|
||||
|
||||
var isVerticalTypingOnlyChooseCandidateKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == verticalTypingOnlyChooseCandidateKey
|
||||
}
|
||||
|
||||
var isUpperCaseASCIILetterKey: Bool {
|
||||
// 這裡必須加上「flags == .shift」,否則會出現某些情況下輸入法「誤判當前鍵入的非 Shift 字符為大寫」的問題。
|
||||
(65...90).contains(charCode) && flags == .shift
|
||||
}
|
||||
// 這裡必須加上「flags == .shift」,否則會出現某些情況下輸入法「誤判當前鍵入的非 Shift 字符為大寫」的問題。
|
||||
var isUpperCaseASCIILetterKey: Bool { (65...90).contains(charCode) && flags == .shift }
|
||||
|
||||
// 這裡必須用 KeyCode,這樣才不會受隨 macOS 版本更動的 Apple 動態注音鍵盤排列內容的影響。
|
||||
// 只是必須得與 ![input isShift] 搭配使用才可以(也就是僅判定 Shift 沒被摁下的情形)。
|
||||
var isSymbolMenuPhysicalKey: Bool {
|
||||
// 這裡必須用 KeyCode,這樣才不會受隨 macOS 版本更動的 Apple 動態注音鍵盤排列內容的影響。
|
||||
// 只是必須得與 ![input isShift] 搭配使用才可以(也就是僅判定 Shift 沒被摁下的情形)。
|
||||
[KeyCode.kSymbolMenuPhysicalKeyIntl, KeyCode.kSymbolMenuPhysicalKeyJIS].contains(KeyCode(rawValue: keyCode))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ enum StateType {
|
|||
case ofDeactivated
|
||||
case ofAssociatedPhrases
|
||||
case ofEmpty
|
||||
case ofEmptyIgnorePreviousState
|
||||
case ofEmptyIgnoringPreviousState
|
||||
case ofCommitting
|
||||
case ofNotEmpty
|
||||
case ofInputting
|
||||
|
@ -70,7 +70,7 @@ protocol InputStateProtocol {
|
|||
/// 組字區內存入任何東西,所以該狀態不受 .NotEmpty 的管轄。
|
||||
/// - .Empty: 使用者剛剛切換至該輸入法、卻還沒有任何輸入行為。抑或是剛剛敲字遞交給
|
||||
/// 客體應用、準備新的輸入行為。
|
||||
/// - .EmptyIgnorePreviousState: 與 Empty 類似,但會扔掉上一個狀態的內容、不將這些
|
||||
/// - .EmptyIgnoringPreviousState: 與 Empty 類似,但會扔掉上一個狀態的內容、不將這些
|
||||
/// 內容遞交給客體應用。該狀態在處理完畢之後會被立刻切換至 .Empty()。
|
||||
/// - .Committing: 該狀態會承載要遞交出去的內容,讓輸入法控制器處理時代為遞交。
|
||||
/// - .NotEmpty: 非空狀態,是一種狀態大類、用以派生且代表下述諸狀態。
|
||||
|
@ -106,11 +106,11 @@ enum InputState {
|
|||
|
||||
// MARK: -
|
||||
|
||||
/// .EmptyIgnorePreviousState: 與 Empty 類似,
|
||||
/// .EmptyIgnoringPreviousState: 與 Empty 類似,
|
||||
/// 但會扔掉上一個狀態的內容、不將這些內容遞交給客體應用。
|
||||
/// 該狀態在處理完畢之後會被立刻切換至 .Empty()。
|
||||
class EmptyIgnoringPreviousState: Empty {
|
||||
override public var type: StateType { .ofEmptyIgnorePreviousState }
|
||||
override public var type: StateType { .ofEmptyIgnoringPreviousState }
|
||||
override var description: String {
|
||||
"<InputState.EmptyIgnoringPreviousState>"
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ enum InputState {
|
|||
convenience init(textToCommit: String) {
|
||||
self.init()
|
||||
self.textToCommit = textToCommit
|
||||
ChineseConverter.ensureCurrencyNumerals(target: &self.textToCommit)
|
||||
}
|
||||
|
||||
var description: String {
|
||||
|
@ -146,6 +147,17 @@ enum InputState {
|
|||
self.isTypingVertical = isTypingVertical
|
||||
}
|
||||
|
||||
var attributedString: NSMutableAttributedString {
|
||||
let attributedString = NSMutableAttributedString(
|
||||
string: " ",
|
||||
attributes: [
|
||||
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||
.markedClauseSegment: 0,
|
||||
]
|
||||
)
|
||||
return attributedString
|
||||
}
|
||||
|
||||
var description: String {
|
||||
"<InputState.AssociatedPhrases, candidates:\(candidates), isTypingVertical:\(isTypingVertical)>"
|
||||
}
|
||||
|
@ -428,8 +440,11 @@ enum InputState {
|
|||
override public var type: StateType { .ofSymbolTable }
|
||||
var node: SymbolNode
|
||||
|
||||
init(node: SymbolNode, isTypingVertical: Bool) {
|
||||
init(node: SymbolNode, previous: SymbolNode? = nil, isTypingVertical: Bool) {
|
||||
self.node = node
|
||||
if let previous = previous {
|
||||
self.node.previous = previous
|
||||
}
|
||||
let candidates = node.children?.map(\.title) ?? [String]()
|
||||
super.init(
|
||||
composingBuffer: "", cursorIndex: 0, candidates: candidates.map { ("", $0) },
|
||||
|
|
|
@ -59,7 +59,7 @@ extension KeyHandler {
|
|||
|| ((input.isCursorBackward || input.isCursorForward) && input.isShiftHold)
|
||||
|
||||
if cancelCandidateKey {
|
||||
if (state is InputState.AssociatedPhrases)
|
||||
if state is InputState.AssociatedPhrases
|
||||
|| mgrPrefs.useSCPCTypingMode
|
||||
|| compositor.isEmpty
|
||||
{
|
||||
|
@ -72,6 +72,9 @@ extension KeyHandler {
|
|||
} else {
|
||||
stateCallback(buildInputtingState)
|
||||
}
|
||||
if let state = state as? InputState.SymbolTable, let nodePrevious = state.node.previous {
|
||||
stateCallback(InputState.SymbolTable(node: nodePrevious, isTypingVertical: state.isTypingVertical))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -288,7 +291,7 @@ extension KeyHandler {
|
|||
|
||||
var index: Int = NSNotFound
|
||||
let match: String =
|
||||
(state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText ?? ""
|
||||
(state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText
|
||||
|
||||
var j = 0
|
||||
while j < ctlCandidateCurrent.keyLabels.count {
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||
// Refactored from the ObjCpp-version of this class by:
|
||||
// (c) 2011 and onwards The OpenVanilla Project (MIT 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.
|
||||
*/
|
||||
|
||||
/// 該檔案用來處理 KeyHandler.HandleInput() 當中的與組字有關的行為。
|
||||
|
||||
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
|
||||
let skipPhoneticHandling =
|
||||
input.isReservedKey || input.isNumericPadKey || input.isNonLaptopFunctionKey
|
||||
|| input.isControlHold || input.isOptionHold || input.isShiftHold || input.isCommandHold
|
||||
|
||||
// 這裡 inputValidityCheck() 是讓注拼槽檢查 charCode 這個 UniChar 是否是合法的注音輸入。
|
||||
// 如果是的話,就將這次傳入的這個按鍵訊號塞入注拼槽內且標記為「keyConsumedByReading」。
|
||||
// 函式 composer.receiveKey() 可以既接收 String 又接收 UniChar。
|
||||
if !skipPhoneticHandling && composer.inputValidityCheck(key: input.charCode) {
|
||||
composer.receiveKey(fromCharCode: input.charCode)
|
||||
keyConsumedByReading = true
|
||||
|
||||
// 沒有調號的話,只需要 updateClientComposingBuffer() 且終止處理(return true)即可。
|
||||
// 有調號的話,則不需要這樣,而是轉而繼續在此之後的處理。
|
||||
if !composer.hasToneMarker() {
|
||||
stateCallback(buildInputtingState)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
var composeReading = composer.hasToneMarker() // 這裡不需要做排他性判斷。
|
||||
|
||||
// 如果當前的按鍵是 Enter 或 Space 的話,這時就可以取出 _composer 內的注音來做檢查了。
|
||||
// 來看看詞庫內到底有沒有對應的讀音索引。這裡用了類似「|=」的判斷處理方式。
|
||||
composeReading = composeReading || (!composer.isEmpty && (input.isSpace || input.isEnter))
|
||||
if composeReading {
|
||||
if input.isSpace, !composer.hasToneMarker() {
|
||||
// 補上空格,否則倚天忘形與許氏排列某些音無法響應不了陰平聲調。
|
||||
// 小麥注音因為使用 OVMandarin,所以不需要這樣補。但鐵恨引擎對所有聲調一視同仁。
|
||||
composer.receiveKey(fromString: " ")
|
||||
}
|
||||
let readingKey = composer.getComposition() // 拿取用來進行索引檢索用的注音。
|
||||
// 如果輸入法的辭典索引是漢語拼音的話,要注意上一行拿到的內容得是漢語拼音。
|
||||
|
||||
// 向語言模型詢問是否有對應的記錄。
|
||||
if !currentLM.hasUnigramsFor(key: readingKey) {
|
||||
IME.prtDebugIntel("B49C0979:語彙庫內無「\(readingKey)」的匹配記錄。")
|
||||
errorCallback()
|
||||
composer.clear()
|
||||
// 根據「組字器是否為空」來判定回呼哪一種狀態。
|
||||
stateCallback((compositor.isEmpty) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
||||
return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。
|
||||
}
|
||||
|
||||
// 將該讀音插入至組字器內的軌格當中。
|
||||
compositor.insertReading(readingKey)
|
||||
|
||||
// 讓組字器反爬軌格。
|
||||
let textToCommit = commitOverflownCompositionAndWalk
|
||||
|
||||
// 看看半衰記憶模組是否會對目前的狀態給出自動選字建議。
|
||||
fetchAndApplySuggestionsFromUserOverrideModel()
|
||||
|
||||
// 將組字器內超出最大動態爬軌範圍的節錨都標記為「已經手動選字過」,減少之後的爬軌運算負擔。
|
||||
markNodesFixedIfNecessary()
|
||||
|
||||
// 之後就是更新組字區了。先清空注拼槽的內容。
|
||||
composer.clear()
|
||||
|
||||
// 再以回呼組字狀態的方式來執行 updateClientComposingBuffer()。
|
||||
let inputting = buildInputtingState
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
|
||||
/// 逐字選字模式的處理。
|
||||
if mgrPrefs.useSCPCTypingMode {
|
||||
let choosingCandidates: InputState.ChoosingCandidate = buildCandidate(
|
||||
state: inputting,
|
||||
isTypingVertical: input.isTypingVertical
|
||||
)
|
||||
if choosingCandidates.candidates.count == 1 {
|
||||
clear()
|
||||
let reading: String = choosingCandidates.candidates.first?.0 ?? ""
|
||||
let text: String = choosingCandidates.candidates.first?.1 ?? ""
|
||||
stateCallback(InputState.Committing(textToCommit: text))
|
||||
|
||||
if !mgrPrefs.associatedPhrasesEnabled {
|
||||
stateCallback(InputState.Empty())
|
||||
} else {
|
||||
if let associatedPhrases =
|
||||
buildAssociatePhraseState(
|
||||
withPair: .init(key: reading, value: text),
|
||||
isTypingVertical: input.isTypingVertical
|
||||
), !associatedPhrases.candidates.isEmpty
|
||||
{
|
||||
stateCallback(associatedPhrases)
|
||||
} else {
|
||||
stateCallback(InputState.Empty())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stateCallback(choosingCandidates)
|
||||
}
|
||||
}
|
||||
// 將「這個按鍵訊號已經被輸入法攔截處理了」的結果藉由 ctlInputMethod 回報給 IMK。
|
||||
return true
|
||||
}
|
||||
|
||||
/// 如果此時這個選項是 true 的話,可知當前注拼槽輸入了聲調、且上一次按鍵不是聲調按鍵。
|
||||
/// 比方說大千傳統佈局敲「6j」會出現「ˊㄨ」但並不會被認為是「ㄨˊ」,因為先輸入的調號
|
||||
/// 並非用來確認這個注音的調號。除非是:「ㄨˊ」「ˊㄨˊ」「ˊㄨˇ」「ˊㄨ 」等。
|
||||
if keyConsumedByReading {
|
||||
// 以回呼組字狀態的方式來執行 updateClientComposingBuffer()。
|
||||
stateCallback(buildInputtingState)
|
||||
return true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -45,18 +45,17 @@ extension KeyHandler {
|
|||
stateCallback: @escaping (InputStateProtocol) -> Void,
|
||||
errorCallback: @escaping () -> Void
|
||||
) -> Bool {
|
||||
// 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。
|
||||
guard !input.inputText.isEmpty else { return false }
|
||||
|
||||
let charCode: UniChar = input.charCode
|
||||
let inputText: String = input.inputText
|
||||
var state = state // 常數轉變數。
|
||||
|
||||
// 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。
|
||||
guard let inputText: String = input.inputText, !inputText.isEmpty else {
|
||||
return false
|
||||
}
|
||||
|
||||
// 提前過濾掉一些不合規的按鍵訊號輸入,免得相關按鍵訊號被送給 Megrez 引發輸入法崩潰。
|
||||
if input.isInvalidInput {
|
||||
if input.isInvalid {
|
||||
// 在「.Empty(IgnoringPreviousState) 與 .Deactivated」狀態下的首次不合規按鍵輸入可以直接放行。
|
||||
// 因為「.EmptyIgnorePreviousState」會在處理之後被自動轉為「.Empty」,所以不需要單獨判斷。
|
||||
// 因為「.EmptyIgnoringPreviousState」會在處理之後被自動轉為「.Empty」,所以不需要單獨判斷。
|
||||
if state is InputState.Empty || state is InputState.Deactivated {
|
||||
return false
|
||||
}
|
||||
|
@ -68,7 +67,7 @@ extension KeyHandler {
|
|||
|
||||
// 如果當前組字器為空的話,就不再攔截某些修飾鍵,畢竟這些鍵可能會會用來觸發某些功能。
|
||||
let isFunctionKey: Bool =
|
||||
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNumericPad)
|
||||
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey)
|
||||
if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey {
|
||||
return false
|
||||
}
|
||||
|
@ -79,8 +78,9 @@ extension KeyHandler {
|
|||
/// 這裡的處理原先是給威注音曾經有過的 Shift 切換英數模式來用的,但因為採 Chromium 核
|
||||
/// 心的瀏覽器會讓 IMK 無法徹底攔截對 Shift 鍵的單擊行為、導致這個模式的使用體驗非常糟
|
||||
/// 糕,故僅保留以 Caps Lock 驅動的英數模式。
|
||||
if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey
|
||||
|| input.isExtraChooseCandidateKeyReverse || input.isCursorForward || input.isCursorBackward
|
||||
if input.isBackSpace || input.isEnter
|
||||
|| input.isCursorClockLeft || input.isCursorClockRight
|
||||
|| input.isCursorForward || input.isCursorBackward
|
||||
{
|
||||
// 略過對 BackSpace 的處理。
|
||||
} else if input.isCapsLockOn {
|
||||
|
@ -108,10 +108,10 @@ extension KeyHandler {
|
|||
|
||||
// MARK: 處理數字小鍵盤 (Numeric Pad Processing)
|
||||
|
||||
// 這裡必須用「isNumericPadAreaKey」這個以 KeyCode 判定數字鍵區輸入的方法來鎖定按鍵範圍。
|
||||
// 不然的話,會誤傷到在主鍵盤區域的功能鍵。
|
||||
// 這裡的「isNumericPadKey」處理邏輯已經改成用 KeyCode 判定數字鍵區輸入、以鎖定按鍵範圍。
|
||||
// 不然、使用 Cocoa 內建的 flags 的話,會誤傷到在主鍵盤區域的功能鍵。
|
||||
// 我們先規定允許小鍵盤區域操縱選字窗,其餘場合一律直接放行。
|
||||
if input.isNumericPadAreaKey {
|
||||
if input.isNumericPadKey {
|
||||
if !(state is InputState.ChoosingCandidate || state is InputState.AssociatedPhrases
|
||||
|| state is InputState.SymbolTable)
|
||||
{
|
||||
|
@ -158,117 +158,17 @@ extension KeyHandler {
|
|||
|
||||
// MARK: 注音按鍵輸入處理 (Handle BPMF Keys)
|
||||
|
||||
var keyConsumedByReading = false
|
||||
let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold
|
||||
|
||||
// 這裡 inputValidityCheck() 是讓注拼槽檢查 charCode 這個 UniChar 是否是合法的注音輸入。
|
||||
// 如果是的話,就將這次傳入的這個按鍵訊號塞入注拼槽內且標記為「keyConsumedByReading」。
|
||||
// 函式 composer.receiveKey() 可以既接收 String 又接收 UniChar。
|
||||
if !skipPhoneticHandling && composer.inputValidityCheck(key: charCode) {
|
||||
composer.receiveKey(fromCharCode: charCode)
|
||||
keyConsumedByReading = true
|
||||
|
||||
// 沒有調號的話,只需要 updateClientComposingBuffer() 且終止處理(return true)即可。
|
||||
// 有調號的話,則不需要這樣,而是轉而繼續在此之後的處理。
|
||||
if !composer.hasToneMarker() {
|
||||
stateCallback(buildInputtingState)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
var composeReading = composer.hasToneMarker() // 這裡不需要做排他性判斷。
|
||||
|
||||
// 如果當前的按鍵是 Enter 或 Space 的話,這時就可以取出 _composer 內的注音來做檢查了。
|
||||
// 來看看詞庫內到底有沒有對應的讀音索引。這裡用了類似「|=」的判斷處理方式。
|
||||
composeReading = composeReading || (!composer.isEmpty && (input.isSpace || input.isEnter))
|
||||
if composeReading {
|
||||
if input.isSpace, !composer.hasToneMarker() {
|
||||
// 補上空格,否則倚天忘形與許氏排列某些音無法響應不了陰平聲調。
|
||||
// 小麥注音因為使用 OVMandarin,所以不需要這樣補。但鐵恨引擎對所有聲調一視同仁。
|
||||
composer.receiveKey(fromString: " ")
|
||||
}
|
||||
let readingKey = composer.getComposition() // 拿取用來進行索引檢索用的注音。
|
||||
// 如果輸入法的辭典索引是漢語拼音的話,要注意上一行拿到的內容得是漢語拼音。
|
||||
|
||||
// 向語言模型詢問是否有對應的記錄。
|
||||
if !currentLM.hasUnigramsFor(key: readingKey) {
|
||||
IME.prtDebugIntel("B49C0979:語彙庫內無「\(readingKey)」的匹配記錄。")
|
||||
errorCallback()
|
||||
composer.clear()
|
||||
// 根據「組字器是否為空」來判定回呼哪一種狀態。
|
||||
stateCallback((compositor.isEmpty) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
|
||||
return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。
|
||||
}
|
||||
|
||||
// 將該讀音插入至組字器內的軌格當中。
|
||||
compositor.insertReading(readingKey)
|
||||
|
||||
// 讓組字器反爬軌格。
|
||||
let textToCommit = commitOverflownCompositionAndWalk
|
||||
|
||||
// 看看半衰記憶模組是否會對目前的狀態給出自動選字建議。
|
||||
fetchAndApplySuggestionsFromUserOverrideModel()
|
||||
|
||||
// 將組字器內超出最大動態爬軌範圍的節錨都標記為「已經手動選字過」,減少之後的爬軌運算負擔。
|
||||
markNodesFixedIfNecessary()
|
||||
|
||||
// 之後就是更新組字區了。先清空注拼槽的內容。
|
||||
composer.clear()
|
||||
|
||||
// 再以回呼組字狀態的方式來執行 updateClientComposingBuffer()。
|
||||
let inputting = buildInputtingState
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
|
||||
/// 逐字選字模式的處理。
|
||||
if mgrPrefs.useSCPCTypingMode {
|
||||
let choosingCandidates: InputState.ChoosingCandidate = buildCandidate(
|
||||
state: inputting,
|
||||
isTypingVertical: input.isTypingVertical
|
||||
)
|
||||
if choosingCandidates.candidates.count == 1 {
|
||||
clear()
|
||||
let reading: String = choosingCandidates.candidates.first?.0 ?? ""
|
||||
let text: String = choosingCandidates.candidates.first?.1 ?? ""
|
||||
stateCallback(InputState.Committing(textToCommit: text))
|
||||
|
||||
if !mgrPrefs.associatedPhrasesEnabled {
|
||||
stateCallback(InputState.Empty())
|
||||
} else {
|
||||
if let associatedPhrases =
|
||||
buildAssociatePhraseState(
|
||||
withPair: .init(key: reading, value: text),
|
||||
isTypingVertical: input.isTypingVertical
|
||||
), !associatedPhrases.candidates.isEmpty
|
||||
{
|
||||
stateCallback(associatedPhrases)
|
||||
} else {
|
||||
stateCallback(InputState.Empty())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stateCallback(choosingCandidates)
|
||||
}
|
||||
}
|
||||
// 將「這個按鍵訊號已經被輸入法攔截處理了」的結果藉由 ctlInputMethod 回報給 IMK。
|
||||
return true
|
||||
}
|
||||
|
||||
/// 如果此時這個選項是 true 的話,可知當前注拼槽輸入了聲調、且上一次按鍵不是聲調按鍵。
|
||||
/// 比方說大千傳統佈局敲「6j」會出現「ˊㄨ」但並不會被認為是「ㄨˊ」,因為先輸入的調號
|
||||
/// 並非用來確認這個注音的調號。除非是:「ㄨˊ」「ˊㄨˊ」「ˊㄨˇ」「ˊㄨ 」等。
|
||||
if keyConsumedByReading {
|
||||
// 以回呼組字狀態的方式來執行 updateClientComposingBuffer()。
|
||||
stateCallback(buildInputtingState)
|
||||
return true
|
||||
if let compositionHandled = handleComposition(
|
||||
input: input, state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
{
|
||||
return compositionHandled
|
||||
}
|
||||
|
||||
// MARK: 用上下左右鍵呼叫選字窗 (Calling candidate window using Up / Down or PageUp / PageDn.)
|
||||
|
||||
if let currentState = state as? InputState.NotEmpty, composer.isEmpty, !input.isOptionHold,
|
||||
input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace
|
||||
input.isCursorClockLeft || input.isCursorClockRight || input.isSpace
|
||||
|| input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
|
||||
|| (input.isTypingVertical && (input.isVerticalTypingOnlyChooseCandidateKey))
|
||||
{
|
||||
if input.isSpace {
|
||||
/// 倘若沒有在偏好設定內將 Space 空格鍵設為選字窗呼叫用鍵的話………
|
||||
|
@ -354,22 +254,22 @@ extension KeyHandler {
|
|||
return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
}
|
||||
|
||||
// MARK: AbsorbedArrowKey
|
||||
// MARK: Clock-Left & Clock-Right
|
||||
|
||||
if input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse {
|
||||
if input.isCursorClockLeft || input.isCursorClockRight {
|
||||
if input.isOptionHold, state is InputState.Inputting {
|
||||
if input.isExtraChooseCandidateKey {
|
||||
if input.isCursorClockRight {
|
||||
return handleInlineCandidateRotation(
|
||||
state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
if input.isExtraChooseCandidateKeyReverse {
|
||||
if input.isCursorClockLeft {
|
||||
return handleInlineCandidateRotation(
|
||||
state: state, reverseModifier: true, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
return handleAbsorbedArrowKey(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleClockKey(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
}
|
||||
|
||||
// MARK: Backspace
|
||||
|
@ -424,6 +324,23 @@ extension KeyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: 全形/半形阿拉伯數字輸入 (FW / HW Arabic Numbers Input)
|
||||
|
||||
if state is InputState.Empty {
|
||||
if input.isMainAreaNumKey, input.isShiftHold, input.isOptionHold, !input.isControlHold, !input.isCommandHold {
|
||||
// NOTE: 將來棄用 macOS 10.11 El Capitan 支援的時候,把這裡由 CFStringTransform 改為 StringTransform:
|
||||
// https://developer.apple.com/documentation/foundation/stringtransform
|
||||
guard let stringRAW = input.mapMainAreaNumKey[input.keyCode] else { return false }
|
||||
let string = NSMutableString(string: stringRAW)
|
||||
CFStringTransform(string, nil, kCFStringTransformFullwidthHalfwidth, true)
|
||||
stateCallback(
|
||||
InputState.Committing(textToCommit: mgrPrefs.halfWidthPunctuationEnabled ? stringRAW : string as String)
|
||||
)
|
||||
stateCallback(InputState.Empty())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Punctuation
|
||||
|
||||
/// 如果仍無匹配結果的話,先看一下:
|
||||
|
@ -479,11 +396,11 @@ extension KeyHandler {
|
|||
|
||||
/// 該功能僅可在當前組字區沒有任何內容的時候使用。
|
||||
if state is InputState.Empty {
|
||||
if input.isSpace, !input.isOptionHold, !input.isFunctionKeyHold, !input.isControlHold, !input.isCommandHold {
|
||||
if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold {
|
||||
stateCallback(InputState.Committing(textToCommit: input.isShiftHold ? " " : " "))
|
||||
stateCallback(InputState.Empty())
|
||||
return true
|
||||
}
|
||||
stateCallback(InputState.Empty())
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: - 終末處理 (Still Nothing)
|
||||
|
|
|
@ -509,7 +509,7 @@ extension KeyHandler {
|
|||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 ctlInputMethod 回報給 IMK。
|
||||
func handleAbsorbedArrowKey(
|
||||
func handleClockKey(
|
||||
state: InputStateProtocol,
|
||||
stateCallback: @escaping (InputStateProtocol) -> Void,
|
||||
errorCallback: @escaping () -> Void
|
||||
|
|
|
@ -101,7 +101,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
if let children = node.children, !children.isEmpty {
|
||||
handle(state: InputState.Empty()) // 防止縱橫排選字窗同時出現
|
||||
handle(
|
||||
state: InputState.SymbolTable(node: node, isTypingVertical: state.isTypingVertical)
|
||||
state: InputState.SymbolTable(node: node, previous: state.node, isTypingVertical: state.isTypingVertical)
|
||||
)
|
||||
} else {
|
||||
handle(state: InputState.Committing(textToCommit: node.title))
|
||||
|
@ -120,6 +120,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
keyHandler.clear()
|
||||
let composingBuffer = inputting.composingBuffer
|
||||
handle(state: InputState.Committing(textToCommit: composingBuffer))
|
||||
// 此時是逐字選字模式,所以「selectedValue.1」是單個字、不用追加處理。
|
||||
if mgrPrefs.associatedPhrasesEnabled,
|
||||
let associatePhrases = keyHandler.buildAssociatePhraseState(
|
||||
withPair: .init(key: selectedValue.0, value: selectedValue.1),
|
||||
|
@ -139,16 +140,22 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
if let state = state as? InputState.AssociatedPhrases {
|
||||
let selectedValue = state.candidates[index]
|
||||
handle(state: InputState.Committing(textToCommit: selectedValue.1))
|
||||
// 此時是聯想詞選字模式,所以「selectedValue.1」必須只保留最後一個字。
|
||||
// 不然的話,一旦你選中了由多個字組成的聯想候選詞,則連續聯想會被打斷。
|
||||
guard let valueKept = selectedValue.1.last else {
|
||||
handle(state: InputState.Empty())
|
||||
return
|
||||
}
|
||||
if mgrPrefs.associatedPhrasesEnabled,
|
||||
let associatePhrases = keyHandler.buildAssociatePhraseState(
|
||||
withPair: .init(key: selectedValue.0, value: selectedValue.1),
|
||||
withPair: .init(key: selectedValue.0, value: String(valueKept)),
|
||||
isTypingVertical: state.isTypingVertical
|
||||
), !associatePhrases.candidates.isEmpty
|
||||
{
|
||||
handle(state: associatePhrases)
|
||||
} else {
|
||||
handle(state: InputState.Empty())
|
||||
return
|
||||
}
|
||||
handle(state: InputState.Empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,14 @@ extension ctlInputMethod {
|
|||
|
||||
/// 針對受 .NotEmpty() 管轄的非空狀態,在組字區內顯示游標。
|
||||
private func setInlineDisplayWithCursor() {
|
||||
if let state = state as? InputState.AssociatedPhrases {
|
||||
client().setMarkedText(
|
||||
state.attributedString, selectionRange: NSRange(location: 0, length: 0),
|
||||
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
guard let state = state as? InputState.NotEmpty else {
|
||||
clearInlineDisplay()
|
||||
return
|
||||
|
@ -225,7 +233,7 @@ extension ctlInputMethod {
|
|||
private func handle(state: InputState.AssociatedPhrases, previous: InputStateProtocol) {
|
||||
_ = previous // 防止格式整理工具毀掉與此對應的參數。
|
||||
ctlInputMethod.tooltipController.hide()
|
||||
clearInlineDisplay()
|
||||
setInlineDisplayWithCursor()
|
||||
show(candidateWindowWith: state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,13 @@ extension ctlInputMethod {
|
|||
shiftJISConversionItem.state = mgrPrefs.shiftJISShinjitaiOutputEnabled.state
|
||||
}
|
||||
|
||||
let currencyNumeralsItem = menu.addItem(
|
||||
withTitle: NSLocalizedString("Currency Numeral Output", comment: ""),
|
||||
action: #selector(toggleCurrencyNumerals(_:)), keyEquivalent: mgrPrefs.usingHotKeyCurrencyNumerals ? "M" : ""
|
||||
)
|
||||
currencyNumeralsItem.keyEquivalentModifierMask = [.command, .control]
|
||||
currencyNumeralsItem.state = mgrPrefs.currencyNumeralsEnabled.state
|
||||
|
||||
let halfWidthPunctuationItem = menu.addItem(
|
||||
withTitle: NSLocalizedString("Half-Width Punctuation Mode", comment: ""),
|
||||
action: #selector(toggleHalfWidthPunctuation(_:)), keyEquivalent: mgrPrefs.usingHotKeyHalfWidthASCII ? "H" : ""
|
||||
|
@ -245,6 +252,17 @@ extension ctlInputMethod {
|
|||
))
|
||||
}
|
||||
|
||||
@objc func toggleCurrencyNumerals(_: Any?) {
|
||||
resetKeyHandler()
|
||||
NotifierController.notify(
|
||||
message: String(
|
||||
format: "%@%@%@", NSLocalizedString("Currency Numeral Output", comment: ""), "\n",
|
||||
mgrPrefs.toggleCurrencyNumeralsEnabled()
|
||||
? NSLocalizedString("NotificationSwitchON", comment: "")
|
||||
: NSLocalizedString("NotificationSwitchOFF", comment: "")
|
||||
))
|
||||
}
|
||||
|
||||
@objc func toggleHalfWidthPunctuation(_: Any?) {
|
||||
resetKeyHandler()
|
||||
NotifierController.notify(
|
||||
|
|
|
@ -450,6 +450,8 @@ extension Sequence {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Shell Extension
|
||||
|
||||
extension NSApplication {
|
||||
public static func shell(_ command: String) throws -> String {
|
||||
let task = Process()
|
||||
|
|
|
@ -45,6 +45,7 @@ struct UserDef {
|
|||
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"
|
||||
|
@ -74,6 +75,7 @@ struct UserDef {
|
|||
static let kUsingHotKeyKangXi = "UsingHotKeyKangXi"
|
||||
static let kUsingHotKeyJIS = "UsingHotKeyJIS"
|
||||
static let kUsingHotKeyHalfWidthASCII = "UsingHotKeyHalfWidthASCII"
|
||||
static let kUsingHotKeyCurrencyNumerals = "UsingHotKeyCurrencyNumerals"
|
||||
}
|
||||
|
||||
private let kDefaultCandidateListTextSize: CGFloat = 18
|
||||
|
@ -286,6 +288,9 @@ public enum mgrPrefs {
|
|||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.alsoConfirmAssociatedCandidatesByEnter, forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.currencyNumeralsEnabled, forKey: UserDef.kCurrencyNumeralsEnabled
|
||||
)
|
||||
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates)
|
||||
|
@ -293,6 +298,7 @@ public enum mgrPrefs {
|
|||
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.synchronize()
|
||||
}
|
||||
|
@ -456,6 +462,14 @@ public enum mgrPrefs {
|
|||
return shiftJISShinjitaiOutputEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kCurrencyNumeralsEnabled, defaultValue: false)
|
||||
static var currencyNumeralsEnabled: Bool
|
||||
|
||||
static func toggleCurrencyNumeralsEnabled() -> Bool {
|
||||
currencyNumeralsEnabled = !currencyNumeralsEnabled
|
||||
return currencyNumeralsEnabled
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kHalfWidthPunctuationEnabled, defaultValue: false)
|
||||
static var halfWidthPunctuationEnabled: Bool
|
||||
|
||||
|
@ -584,6 +598,9 @@ public enum mgrPrefs {
|
|||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyHalfWidthASCII, defaultValue: true)
|
||||
static var usingHotKeyHalfWidthASCII: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kUsingHotKeyCurrencyNumerals, defaultValue: true)
|
||||
static var usingHotKeyCurrencyNumerals: Bool
|
||||
}
|
||||
|
||||
// MARK: Snapshot Extension
|
||||
|
@ -609,6 +626,7 @@ extension mgrPrefs {
|
|||
UserDef.kUsingHotKeyAssociates, UserDef.kUsingHotKeyCNS, UserDef.kUsingHotKeyKangXi, UserDef.kUsingHotKeyJIS,
|
||||
UserDef.kUsingHotKeyHalfWidthASCII, UserDef.kUseFixecCandidateOrderOnSelection,
|
||||
UserDef.kAutoCorrectReadingCombination, UserDef.kAlsoConfirmAssociatedCandidatesByEnter,
|
||||
UserDef.kCurrencyNumeralsEnabled, UserDef.kUsingHotKeyCurrencyNumerals,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import Foundation
|
|||
class SymbolNode {
|
||||
var title: String
|
||||
var children: [SymbolNode]?
|
||||
var previous: SymbolNode?
|
||||
|
||||
init(_ title: String, _ children: [SymbolNode]? = nil) {
|
||||
self.title = title
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"Core Dict loading complete." = "Core Dict loading complete.";
|
||||
"Optimize Memorized Phrases" = "Optimize Memorized Phrases";
|
||||
"Clear Memorized Phrases" = "Clear Memorized Phrases";
|
||||
"Currency Numeral Output" = "Currency Numeral Output";
|
||||
|
||||
// The followings are the category names used in the Symbol menu.
|
||||
"catCommonSymbols" = "CommonSymbols";
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"Core Dict loading complete." = "Core Dict loading complete.";
|
||||
"Optimize Memorized Phrases" = "Optimize Memorized Phrases";
|
||||
"Clear Memorized Phrases" = "Clear Memorized Phrases";
|
||||
"Currency Numeral Output" = "Currency Numeral Output";
|
||||
|
||||
// The followings are the category names used in the Symbol menu.
|
||||
"catCommonSymbols" = "CommonSymbols";
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"Core Dict loading complete." = "核心辞書読込完了";
|
||||
"Optimize Memorized Phrases" = "臨時記憶資料を整う";
|
||||
"Clear Memorized Phrases" = "臨時記憶資料を削除";
|
||||
"Currency Numeral Output" = "数字大字変換";
|
||||
|
||||
// The followings are the category names used in the Symbol menu.
|
||||
"catCommonSymbols" = "常用";
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"Core Dict loading complete." = "核心辞典载入完毕";
|
||||
"Optimize Memorized Phrases" = "精简临时记忆语汇资料";
|
||||
"Clear Memorized Phrases" = "清除临时记忆语汇资料";
|
||||
"Currency Numeral Output" = "大写汉字数字输出";
|
||||
|
||||
// The followings are the category names used in the Symbol menu.
|
||||
"catCommonSymbols" = "常用";
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"Core Dict loading complete." = "核心辭典載入完畢";
|
||||
"Optimize Memorized Phrases" = "精簡臨時記憶語彙資料";
|
||||
"Clear Memorized Phrases" = "清除臨時記憶語彙資料";
|
||||
"Currency Numeral Output" = "大寫漢字數字輸出";
|
||||
|
||||
// The followings are the category names used in the Symbol menu.
|
||||
"catCommonSymbols" = "常用";
|
||||
|
|
|
@ -37,6 +37,8 @@ struct suiPrefPaneKeyboard: View {
|
|||
@State private var selUsingHotKeyJIS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyJIS)
|
||||
@State private var selUsingHotKeyHalfWidthASCII = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kUsingHotKeyHalfWidthASCII)
|
||||
@State private var selUsingHotKeyCurrencyNumerals = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kUsingHotKeyCurrencyNumerals)
|
||||
|
||||
private let contentWidth: Double = {
|
||||
switch mgrPrefs.appleLanguages[0] {
|
||||
|
@ -174,6 +176,13 @@ struct suiPrefPaneKeyboard: View {
|
|||
mgrPrefs.usingHotKeyHalfWidthASCII = value
|
||||
selUsingHotKeyHalfWidthASCII = value
|
||||
}
|
||||
Toggle(
|
||||
LocalizedStringKey("Currency Numeral Output"),
|
||||
isOn: $selUsingHotKeyCurrencyNumerals
|
||||
).onChange(of: selUsingHotKeyCurrencyNumerals) { value in
|
||||
mgrPrefs.usingHotKeyCurrencyNumerals = value
|
||||
selUsingHotKeyCurrencyNumerals = value
|
||||
}
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
|
|
|
@ -184,7 +184,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vwf-Kq-s8M" userLabel="chkShowHanyuPinyinInCompositionBuffer">
|
||||
<rect key="frame" x="19" y="122.5" width="401" height="16"/>
|
||||
<rect key="frame" x="19" y="121.5" width="401" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="Show Hanyu-Pinyin in the inline composition buffer & tooltip" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="wFR-zX-M8H">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
|
@ -195,7 +195,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gZ0-OK-r7a" userLabel="InlineDumpPinyinInLieuOfZhuyin">
|
||||
<rect key="frame" x="19" y="101.5" width="401" height="16"/>
|
||||
<rect key="frame" x="19" y="100.5" width="401" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="iWy-Nw-QKB">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
|
@ -281,7 +281,7 @@
|
|||
<constraint firstItem="Ldp-U1-36g" firstAttribute="leading" secondItem="5Rz-c8-hp9" secondAttribute="leading" id="AJV-6w-zzb"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Ldp-U1-36g" secondAttribute="trailing" constant="20" symbolic="YES" id="CGs-3y-UKf"/>
|
||||
<constraint firstItem="gZ0-OK-r7a" firstAttribute="leading" secondItem="E66-0o-wJZ" secondAttribute="leading" id="COA-Gy-V6W"/>
|
||||
<constraint firstItem="E66-0o-wJZ" firstAttribute="top" secondItem="gZ0-OK-r7a" secondAttribute="bottom" constant="20" id="FEO-KC-ere"/>
|
||||
<constraint firstItem="E66-0o-wJZ" firstAttribute="top" secondItem="gZ0-OK-r7a" secondAttribute="bottom" constant="19" id="FEO-KC-ere"/>
|
||||
<constraint firstItem="da1-7e-els" firstAttribute="top" relation="lessThanOrEqual" secondItem="233" secondAttribute="bottom" constant="6" id="GPh-Pz-Koa"/>
|
||||
<constraint firstItem="19" firstAttribute="top" secondItem="Yl4-Ar-L6r" secondAttribute="bottom" constant="6" id="HLS-hW-sXe"/>
|
||||
<constraint firstItem="233" firstAttribute="top" secondItem="19" secondAttribute="bottom" constant="5" id="IRf-SV-AoA"/>
|
||||
|
@ -303,7 +303,7 @@
|
|||
<constraint firstItem="h4r-Sp-LBI" firstAttribute="trailing" secondItem="pYB-E5-4Nv" secondAttribute="trailing" id="eyF-ME-nzz"/>
|
||||
<constraint firstItem="23" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="BUt-lg-GPp" secondAttribute="leading" constant="20" symbolic="YES" id="fVo-zM-hxe"/>
|
||||
<constraint firstItem="90" firstAttribute="leading" secondItem="oS6-u5-7dP" secondAttribute="leading" id="h1Q-UC-WWd"/>
|
||||
<constraint firstItem="vwf-Kq-s8M" firstAttribute="top" secondItem="pYB-E5-4Nv" secondAttribute="bottom" constant="5" id="iDF-SS-g0P"/>
|
||||
<constraint firstItem="vwf-Kq-s8M" firstAttribute="top" secondItem="pYB-E5-4Nv" secondAttribute="bottom" constant="6" id="iDF-SS-g0P"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="da1-7e-els" secondAttribute="trailing" constant="20" symbolic="YES" id="iM3-gf-fp7"/>
|
||||
<constraint firstItem="L5M-VG-W1X" firstAttribute="top" secondItem="5Rz-c8-hp9" secondAttribute="bottom" constant="9" id="iZF-3e-q0g"/>
|
||||
<constraint firstItem="L5M-VG-W1X" firstAttribute="baseline" secondItem="oS6-u5-7dP" secondAttribute="baseline" constant="-1" id="inK-mb-fEf"/>
|
||||
|
@ -813,11 +813,11 @@
|
|||
<point key="canvasLocation" x="-896.5" y="-214"/>
|
||||
</visualEffectView>
|
||||
<visualEffectView blendingMode="behindWindow" material="sidebar" state="followsWindowActiveState" id="U4q-xw-mc0" userLabel="vwrKeyboard">
|
||||
<rect key="frame" x="0.0" y="0.0" width="445" height="278"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="445" height="289"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="onD-QP-KPf">
|
||||
<rect key="frame" x="18" y="243" width="345" height="15"/>
|
||||
<rect key="frame" x="18" y="254" width="345" height="15"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="242" id="7Fg-39-CRo"/>
|
||||
</constraints>
|
||||
|
@ -828,7 +828,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<textField wantsLayer="YES" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="hab-1o-1kS">
|
||||
<rect key="frame" x="18" y="135" width="409" height="37"/>
|
||||
<rect key="frame" x="18" y="146" width="409" height="37"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="37" id="DuE-Bj-q43"/>
|
||||
</constraints>
|
||||
|
@ -840,7 +840,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="249" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="11">
|
||||
<rect key="frame" x="18" y="216" width="86" height="17"/>
|
||||
<rect key="frame" x="18" y="227" width="86" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="17" id="3Lz-Gj-jiD"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="82" id="Vfj-gd-B0r"/>
|
||||
|
@ -852,7 +852,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3">
|
||||
<rect key="frame" x="107" y="210" width="277" height="26"/>
|
||||
<rect key="frame" x="107" y="221" width="277" height="26"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="270" id="s83-aB-x7j"/>
|
||||
</constraints>
|
||||
|
@ -883,7 +883,7 @@
|
|||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="124">
|
||||
<rect key="frame" x="107" y="182" width="277" height="26"/>
|
||||
<rect key="frame" x="107" y="193" width="277" height="26"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="MHr-9M-m65"/>
|
||||
</constraints>
|
||||
|
@ -897,7 +897,7 @@
|
|||
</connections>
|
||||
</popUpButton>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="125">
|
||||
<rect key="frame" x="18" y="190" width="86" height="15"/>
|
||||
<rect key="frame" x="18" y="201" width="86" height="15"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Basic Layout:" id="126">
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -905,7 +905,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Md1-t7-hU4">
|
||||
<rect key="frame" x="18" y="112" width="246" height="15"/>
|
||||
<rect key="frame" x="18" y="123" width="246" height="15"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="242" id="ap2-EM-Teq"/>
|
||||
</constraints>
|
||||
|
@ -915,111 +915,127 @@
|
|||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qAe-7E-PPO" userLabel="UsingHotKeySCPC">
|
||||
<rect key="frame" x="19" y="80.5" width="201" height="16"/>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8fz-X4-HVS" userLabel="UsingHotKeySCPC">
|
||||
<rect key="frame" x="19" y="92.5" width="201" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="Per-Char Select Mode" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="xibUsingHotKeySCPC">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeySCPC" id="Bda-2Q-cWF"/>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeySCPC" id="fGT-OM-Lsm"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LeH-0u-c97" userLabel="UsingHotKeyAssociates">
|
||||
<rect key="frame" x="19" y="59.5" width="201" height="16"/>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZWc-3p-TII" userLabel="UsingHotKeyAssociates">
|
||||
<rect key="frame" x="19" y="71.5" width="201" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="Per-Char Associated Phrases" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="xibUsingHotKeyAssociates">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyAssociates" id="knj-hU-irv"/>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyAssociates" id="rtd-1k-lXs"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OwI-YY-9pZ" userLabel="UsingHotKeyCNS">
|
||||
<rect key="frame" x="19" y="38.5" width="201" height="16"/>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MVw-Gx-ueg" userLabel="UsingHotKeyCNS">
|
||||
<rect key="frame" x="19" y="50.5" width="201" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="iq0-sB-5kH"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="gce-s6-ooU"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="CNS11643 Mode" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="xibUsingHotKeyCNS">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyCNS" id="TDq-V9-Ojp"/>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyCNS" id="wR2-4R-4Fm"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eNN-a4-bj8" userLabel="UsingHotKeyKangXi">
|
||||
<rect key="frame" x="224" y="80.5" width="201" height="16"/>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ajS-Ja-Vxr" userLabel="UsingHotKeyKangXi">
|
||||
<rect key="frame" x="224" y="92.5" width="201" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="Force KangXi Writing" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="xibUsingHotKeyKangXi">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyKangXi" id="gy4-ge-8R1"/>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyKangXi" id="qXu-X7-Jbp"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button horizontalHuggingPriority="249" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AMy-dJ-qCK" userLabel="UsingHotKeyJIS">
|
||||
<rect key="frame" x="224" y="59.5" width="201" height="16"/>
|
||||
<button horizontalHuggingPriority="249" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vbm-7v-Dtb" userLabel="UsingHotKeyJIS">
|
||||
<rect key="frame" x="224" y="71.5" width="201" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="JIS Shinjitai Output" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="xibUsingHotKeyJIS">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyJIS" id="RBH-UU-T8y"/>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyJIS" id="ys3-4B-YOe"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Dsq-el-pj8" userLabel="UsingHotKeyHalfWidthASCII">
|
||||
<rect key="frame" x="224" y="38.5" width="201" height="16"/>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aBy-qb-ZL5" userLabel="UsingHotKeyHalfWidthASCII">
|
||||
<rect key="frame" x="224" y="50.5" width="201" height="16"/>
|
||||
<buttonCell key="cell" type="check" title="Half-Width Punctuation Mode" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="xibUsingHotKeyHalfWidthASCII">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyHalfWidthASCII" id="Bwk-BZ-dX5"/>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyHalfWidthASCII" id="Sb7-46-oZ3"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JvA-GK-VEW" userLabel="UsingHotKeyCNS">
|
||||
<rect key="frame" x="19" y="29.5" width="201" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="zON-Mt-JWB"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Currency Numeral Output" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="xibUsingCurrencyNumerals">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.UsingHotKeyCurrencyNumerals" id="FuM-xj-9qa"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="trailing" secondItem="LeH-0u-c97" secondAttribute="trailing" id="4hI-Ii-l7H"/>
|
||||
<constraint firstItem="aBy-qb-ZL5" firstAttribute="leading" secondItem="vbm-7v-Dtb" secondAttribute="leading" id="10e-oj-2e4"/>
|
||||
<constraint firstItem="3" firstAttribute="trailing" secondItem="124" secondAttribute="trailing" id="76H-Ss-XPj"/>
|
||||
<constraint firstItem="LeH-0u-c97" firstAttribute="top" secondItem="qAe-7E-PPO" secondAttribute="bottom" constant="6" symbolic="YES" id="7wy-ZX-Nzd"/>
|
||||
<constraint firstItem="JvA-GK-VEW" firstAttribute="top" secondItem="MVw-Gx-ueg" secondAttribute="bottom" constant="6" symbolic="YES" id="7nv-rS-Ba0"/>
|
||||
<constraint firstItem="hab-1o-1kS" firstAttribute="leading" secondItem="onD-QP-KPf" secondAttribute="leading" id="8LA-jZ-1CI"/>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="top" secondItem="LeH-0u-c97" secondAttribute="bottom" constant="6" symbolic="YES" id="8hj-WH-Ii4"/>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="leading" secondItem="LeH-0u-c97" secondAttribute="leading" id="Aop-aE-IvI"/>
|
||||
<constraint firstItem="MVw-Gx-ueg" firstAttribute="trailing" secondItem="ZWc-3p-TII" secondAttribute="trailing" id="9Ya-y5-WoA"/>
|
||||
<constraint firstItem="MVw-Gx-ueg" firstAttribute="leading" secondItem="8fz-X4-HVS" secondAttribute="leading" id="A1b-jn-JXI"/>
|
||||
<constraint firstItem="MVw-Gx-ueg" firstAttribute="trailing" secondItem="8fz-X4-HVS" secondAttribute="trailing" id="Auf-V6-Qfk"/>
|
||||
<constraint firstItem="aBy-qb-ZL5" firstAttribute="leading" secondItem="ajS-Ja-Vxr" secondAttribute="leading" id="BB9-6d-GXd"/>
|
||||
<constraint firstItem="hab-1o-1kS" firstAttribute="top" secondItem="124" secondAttribute="bottom" constant="14" id="BLN-yz-HZ5"/>
|
||||
<constraint firstItem="aBy-qb-ZL5" firstAttribute="trailing" secondItem="ajS-Ja-Vxr" secondAttribute="trailing" id="DAs-y8-Y5H"/>
|
||||
<constraint firstItem="124" firstAttribute="leading" secondItem="125" secondAttribute="trailing" constant="8" symbolic="YES" id="HZF-Ed-Ic2"/>
|
||||
<constraint firstItem="11" firstAttribute="leading" secondItem="125" secondAttribute="leading" id="JDY-99-OY4"/>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="leading" secondItem="qAe-7E-PPO" secondAttribute="leading" id="JwA-yj-8xq"/>
|
||||
<constraint firstItem="Dsq-el-pj8" firstAttribute="leading" secondItem="OwI-YY-9pZ" secondAttribute="trailing" constant="5" id="NUn-89-kRo"/>
|
||||
<constraint firstItem="8fz-X4-HVS" firstAttribute="baseline" secondItem="ajS-Ja-Vxr" secondAttribute="baseline" id="M9z-zF-mvg"/>
|
||||
<constraint firstItem="aBy-qb-ZL5" firstAttribute="top" secondItem="vbm-7v-Dtb" secondAttribute="bottom" constant="6" symbolic="YES" id="Mur-SU-yKs"/>
|
||||
<constraint firstItem="8fz-X4-HVS" firstAttribute="leading" secondItem="hab-1o-1kS" secondAttribute="leading" id="OUA-UC-Zmg"/>
|
||||
<constraint firstItem="aBy-qb-ZL5" firstAttribute="leading" secondItem="MVw-Gx-ueg" secondAttribute="trailing" constant="5" id="OVY-ez-7jX"/>
|
||||
<constraint firstItem="8fz-X4-HVS" firstAttribute="top" secondItem="Md1-t7-hU4" secondAttribute="bottom" constant="15.5" id="R9J-Kf-2Tq"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Md1-t7-hU4" secondAttribute="trailing" constant="20" symbolic="YES" id="RJh-7D-9dJ"/>
|
||||
<constraint firstItem="onD-QP-KPf" firstAttribute="top" secondItem="U4q-xw-mc0" secondAttribute="top" constant="20" id="Sdp-nk-7ka"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="onD-QP-KPf" secondAttribute="trailing" constant="20" symbolic="YES" id="UY1-sM-Bqv"/>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="leading" secondItem="hab-1o-1kS" secondAttribute="leading" id="Ubg-W8-aui"/>
|
||||
<constraint firstItem="11" firstAttribute="leading" secondItem="U4q-xw-mc0" secondAttribute="leading" constant="20" id="VUP-BQ-kHu"/>
|
||||
<constraint firstItem="qAe-7E-PPO" firstAttribute="baseline" secondItem="eNN-a4-bj8" secondAttribute="baseline" id="XUv-ud-KBW"/>
|
||||
<constraint firstItem="MVw-Gx-ueg" firstAttribute="top" secondItem="ZWc-3p-TII" secondAttribute="bottom" constant="6" symbolic="YES" id="YQF-oT-uJD"/>
|
||||
<constraint firstItem="124" firstAttribute="top" secondItem="3" secondAttribute="bottom" constant="7" id="dSu-bx-mNd"/>
|
||||
<constraint firstItem="Dsq-el-pj8" firstAttribute="leading" secondItem="eNN-a4-bj8" secondAttribute="leading" id="ea8-gY-Vy8"/>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="trailing" secondItem="qAe-7E-PPO" secondAttribute="trailing" id="ehC-vA-sYn"/>
|
||||
<constraint firstItem="aBy-qb-ZL5" firstAttribute="trailing" secondItem="vbm-7v-Dtb" secondAttribute="trailing" id="dlm-mb-Hz9"/>
|
||||
<constraint firstItem="onD-QP-KPf" firstAttribute="leading" secondItem="U4q-xw-mc0" secondAttribute="leading" constant="20" symbolic="YES" id="ezn-D5-52H"/>
|
||||
<constraint firstAttribute="trailing" secondItem="3" secondAttribute="trailing" constant="65" id="f90-7x-Uam"/>
|
||||
<constraint firstItem="Dsq-el-pj8" firstAttribute="trailing" secondItem="eNN-a4-bj8" secondAttribute="trailing" id="fQC-qn-PWP"/>
|
||||
<constraint firstItem="Dsq-el-pj8" firstAttribute="trailing" secondItem="hab-1o-1kS" secondAttribute="trailing" id="gC2-eT-5wj"/>
|
||||
<constraint firstItem="3" firstAttribute="leading" secondItem="11" secondAttribute="trailing" constant="8" symbolic="YES" id="gCv-rd-URb"/>
|
||||
<constraint firstItem="Dsq-el-pj8" firstAttribute="leading" secondItem="AMy-dJ-qCK" secondAttribute="leading" id="gcV-P7-BB2"/>
|
||||
<constraint firstAttribute="bottom" secondItem="OwI-YY-9pZ" secondAttribute="bottom" constant="38.5" id="hKf-Bf-LEv"/>
|
||||
<constraint firstItem="MVw-Gx-ueg" firstAttribute="leading" secondItem="JvA-GK-VEW" secondAttribute="leading" id="h0C-T9-sc7"/>
|
||||
<constraint firstItem="MVw-Gx-ueg" firstAttribute="baseline" secondItem="aBy-qb-ZL5" secondAttribute="baseline" id="iRt-Cq-jNG"/>
|
||||
<constraint firstItem="ajS-Ja-Vxr" firstAttribute="trailing" secondItem="hab-1o-1kS" secondAttribute="trailing" id="jzH-4M-HMs"/>
|
||||
<constraint firstAttribute="trailing" secondItem="hab-1o-1kS" secondAttribute="trailing" constant="20" id="k10-rQ-2Jc"/>
|
||||
<constraint firstItem="3" firstAttribute="top" secondItem="onD-QP-KPf" secondAttribute="bottom" constant="8" id="kkD-wl-fMP"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="JvA-GK-VEW" secondAttribute="trailing" constant="20" symbolic="YES" id="mZf-K6-v3k"/>
|
||||
<constraint firstItem="125" firstAttribute="baseline" secondItem="124" secondAttribute="firstBaseline" id="nTd-7q-D7u"/>
|
||||
<constraint firstItem="11" firstAttribute="centerY" secondItem="3" secondAttribute="centerY" id="p8C-VN-s3f"/>
|
||||
<constraint firstItem="11" firstAttribute="firstBaseline" secondItem="3" secondAttribute="firstBaseline" id="rB9-ff-s2J"/>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="baseline" secondItem="Dsq-el-pj8" secondAttribute="baseline" id="rwB-0B-HbV"/>
|
||||
<constraint firstItem="ZWc-3p-TII" firstAttribute="top" secondItem="8fz-X4-HVS" secondAttribute="bottom" constant="6" symbolic="YES" id="rmF-ds-Epf"/>
|
||||
<constraint firstItem="8fz-X4-HVS" firstAttribute="leading" secondItem="Md1-t7-hU4" secondAttribute="leading" id="u3x-T2-w76"/>
|
||||
<constraint firstItem="3" firstAttribute="leading" secondItem="124" secondAttribute="leading" id="vEJ-6b-kNs"/>
|
||||
<constraint firstItem="OwI-YY-9pZ" firstAttribute="leading" secondItem="Md1-t7-hU4" secondAttribute="leading" id="xRy-Ap-bdu"/>
|
||||
<constraint firstItem="Dsq-el-pj8" firstAttribute="top" secondItem="AMy-dJ-qCK" secondAttribute="bottom" constant="6" symbolic="YES" id="xo9-tg-HMe"/>
|
||||
<constraint firstItem="Dsq-el-pj8" firstAttribute="trailing" secondItem="AMy-dJ-qCK" secondAttribute="trailing" id="ysU-b7-c1h"/>
|
||||
<constraint firstItem="MVw-Gx-ueg" firstAttribute="leading" secondItem="ZWc-3p-TII" secondAttribute="leading" id="wWr-35-Mkg"/>
|
||||
<constraint firstItem="Md1-t7-hU4" firstAttribute="top" secondItem="hab-1o-1kS" secondAttribute="bottom" constant="8" symbolic="YES" id="zZC-jv-TR3"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="-377" y="-200"/>
|
||||
<point key="canvasLocation" x="-377.5" y="-194.5"/>
|
||||
</visualEffectView>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"29.title" = "Candidate UI font size:";
|
||||
"2iG-Ic-gbl.label" = "Dictionary";
|
||||
"2pS-nv-te4.title" = "Choose which keys you prefer for selecting candidates.";
|
||||
"xibGeneralSettings.title" = "General Settings";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "Microsoft, Dachen, Wang, etc.";
|
||||
"62u-jY-BRh.title" = "Stop farting (when typed phonetic combination is invalid, etc.)";
|
||||
|
@ -74,15 +73,17 @@
|
|||
"sZx-18-8dO.title" = "Debug Mode";
|
||||
"TXr-FF-ehw.title" = "Traditional Chinese";
|
||||
"ueU-Rz-a1C.title" = "Choose the behavior of (Shift+)Tab key in the candidate window.";
|
||||
"xibOutputSettings.title" = "Output Settings";
|
||||
"W24-T4-cg0.title" = "Enable CNS11643 Support (2022-07-20)";
|
||||
"wFR-zX-M8H.title" = "Show Hanyu-Pinyin in the inline composition buffer & tooltip";
|
||||
"wN3-k3-b2a.title" = "Choose your desired user data folder path. Will be omitted if invalid.";
|
||||
"wQ9-px-b07.title" = "Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional.";
|
||||
"Wvt-HE-LOv.title" = "Keyboard Layout";
|
||||
"xC5-yV-1W1.title" = "Choose your preferred layout of the candidate window.";
|
||||
"xibGeneralSettings.title" = "General Settings";
|
||||
"xibKeyboardShortcuts.title" = "Keyboard Shortcuts";
|
||||
"xibLabelBufferLimit.title" = "Buffer Limit:";
|
||||
"xibOutputSettings.title" = "Output Settings";
|
||||
"xibUsingCurrencyNumerals.title" = "Currency Numeral Output";
|
||||
"xibUsingHotKeyAssociates.title" = "Per-Char Associated Phrases";
|
||||
"xibUsingHotKeyCNS.title" = "CNS11643 Mode";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "Half-Width Punctuation Mode";
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"29.title" = "候補文字の字号:";
|
||||
"2iG-Ic-gbl.label" = "辞書";
|
||||
"2pS-nv-te4.title" = "お好きなる言選り用キー陣列をお選びください。";
|
||||
"xibGeneralSettings.title" = "全般設定";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "大千配列(Microsoft 標準・王安など)";
|
||||
"62u-jY-BRh.title" = "マナーモード // 外すと入力間違の時に変な音が出る";
|
||||
|
@ -74,15 +73,17 @@
|
|||
"sZx-18-8dO.title" = "欠陥辿着モード";
|
||||
"TXr-FF-ehw.title" = "繁体中国語";
|
||||
"ueU-Rz-a1C.title" = "入力候補陳列での (Shift+)Tab キーの輪番切替対象をご指定ください。";
|
||||
"xibOutputSettings.title" = "出力設定";
|
||||
"W24-T4-cg0.title" = "全字庫モード // 入力可能の漢字数倍増 (2022-07-20)";
|
||||
"wFR-zX-M8H.title" = "弁音合併入力(入力緩衝列とヒントで音読みを漢語弁音に)";
|
||||
"wN3-k3-b2a.title" = "欲しがるユーザー辞書保存先をご指定ください。無効の保存先設定は効かぬ。";
|
||||
"wQ9-px-b07.title" = "Apple 動態注音キーボード (大千と倚天伝統) を使うには、共通語分析器の注音配列を大千と設定すべきである。";
|
||||
"Wvt-HE-LOv.title" = "キーボード";
|
||||
"xC5-yV-1W1.title" = "入力候補陳列の仕様をご指定ください。";
|
||||
"xibGeneralSettings.title" = "全般設定";
|
||||
"xibKeyboardShortcuts.title" = "ショートカット";
|
||||
"xibLabelBufferLimit.title" = "緩衝列の容量:";
|
||||
"xibOutputSettings.title" = "出力設定";
|
||||
"xibUsingCurrencyNumerals.title" = "数字大字変換";
|
||||
"xibUsingHotKeyAssociates.title" = "全候補入力で連想語彙";
|
||||
"xibUsingHotKeyCNS.title" = "全字庫モード";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "半角句読モード";
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"29.title" = "字型大小设定:";
|
||||
"2iG-Ic-gbl.label" = "辞典";
|
||||
"2pS-nv-te4.title" = "选择您所偏好的用来选字的按键组合。";
|
||||
"xibGeneralSettings.title" = "一般设定";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "微软/大千/王安/国乔/零壹/仲鼎";
|
||||
"62u-jY-BRh.title" = "廉耻模式 // 取消勾选的话,敲错字时会有异音";
|
||||
|
@ -74,15 +73,17 @@
|
|||
"sZx-18-8dO.title" = "侦错模式";
|
||||
"TXr-FF-ehw.title" = "繁体中文";
|
||||
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 热键在选字窗内的轮替操作对象。";
|
||||
"xibOutputSettings.title" = "输出设定";
|
||||
"W24-T4-cg0.title" = "启用 CNS11643 全字库支援 (2022-07-20)";
|
||||
"wFR-zX-M8H.title" = "拼音并击(组字区与工具提示内显示汉语拼音)";
|
||||
"wN3-k3-b2a.title" = "请在此指定您想指定的使用者语汇档案目录。无效值会被忽略。";
|
||||
"wQ9-px-b07.title" = "Apple 动态注音键盘布局(大千与倚天)要求普通话/国音分析器的注音排列得配置为大千排列。";
|
||||
"Wvt-HE-LOv.title" = "键盘布局";
|
||||
"xC5-yV-1W1.title" = "选择您所偏好的候选字窗布局。";
|
||||
"xibGeneralSettings.title" = "一般设定";
|
||||
"xibKeyboardShortcuts.title" = "键盘快捷键";
|
||||
"xibLabelBufferLimit.title" = "组字区容量:";
|
||||
"xibOutputSettings.title" = "输出设定";
|
||||
"xibUsingCurrencyNumerals.title" = "大写汉字数字输出";
|
||||
"xibUsingHotKeyAssociates.title" = "逐字选字联想模式";
|
||||
"xibUsingHotKeyCNS.title" = "全字库模式";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "半形标点模式";
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"29.title" = "字型大小設定:";
|
||||
"2iG-Ic-gbl.label" = "辭典";
|
||||
"2pS-nv-te4.title" = "選擇您所偏好的用來選字的按鍵組合。";
|
||||
"xibGeneralSettings.title" = "一般設定";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "微軟/大千/王安/國喬/零壹/仲鼎";
|
||||
"62u-jY-BRh.title" = "廉恥模式 // 取消勾選的話,敲錯字時會有異音";
|
||||
|
@ -74,15 +73,17 @@
|
|||
"sZx-18-8dO.title" = "偵錯模式";
|
||||
"TXr-FF-ehw.title" = "繁體中文";
|
||||
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 熱鍵在選字窗內的輪替操作對象。";
|
||||
"xibOutputSettings.title" = "輸出設定";
|
||||
"W24-T4-cg0.title" = "啟用 CNS11643 全字庫支援 (2022-07-20)";
|
||||
"wFR-zX-M8H.title" = "拼音並擊(組字區與工具提示內顯示漢語拼音)";
|
||||
"wN3-k3-b2a.title" = "請在此指定您想指定的使用者語彙檔案目錄。無效值會被忽略。";
|
||||
"wQ9-px-b07.title" = "Apple 動態注音鍵盤佈局(大千與倚天)要求普通話/國音分析器的注音排列得配置為大千排列。";
|
||||
"Wvt-HE-LOv.title" = "鍵盤佈局";
|
||||
"xC5-yV-1W1.title" = "選擇您所偏好的候選字窗佈局。";
|
||||
"xibGeneralSettings.title" = "一般設定";
|
||||
"xibKeyboardShortcuts.title" = "鍵盤快速鍵";
|
||||
"xibLabelBufferLimit.title" = "組字區容量:";
|
||||
"xibOutputSettings.title" = "輸出設定";
|
||||
"xibUsingCurrencyNumerals.title" = "大寫漢字數字輸出";
|
||||
"xibUsingHotKeyAssociates.title" = "逐字選字聯想模式";
|
||||
"xibUsingHotKeyCNS.title" = "全字庫模式";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "半形標點模式";
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.8.4</string>
|
||||
<string>1.8.5</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1984</string>
|
||||
<string>1985</string>
|
||||
<key>UpdateInfoEndpoint</key>
|
||||
<string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string>
|
||||
<key>UpdateInfoSite</key>
|
||||
|
|
|
@ -726,7 +726,7 @@
|
|||
<key>USE_HFS+_COMPRESSION</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.8.4</string>
|
||||
<string>1.8.5</string>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
5BD05C6A27B2BBEF004C4F1D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD05C6527B2BBEF004C4F1D /* ViewController.swift */; };
|
||||
5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */; };
|
||||
5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */; };
|
||||
5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */; };
|
||||
5BE78BD927B3775B005EA1BE /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */; };
|
||||
5BE78BDD27B3776D005EA1BE /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE78BDA27B37764005EA1BE /* frmAboutWindow.xib */; };
|
||||
5BEDB721283B4C250078EB25 /* data-cns.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71D283B4AEA0078EB25 /* data-cns.plist */; };
|
||||
|
@ -303,6 +304,7 @@
|
|||
5BDCBB4A27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
5BDCBB4B27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmAboutWindow.strings"; sourceTree = "<group>"; };
|
||||
5BDCBB4D27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_HandleComposition.swift; sourceTree = "<group>"; };
|
||||
5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5BE78BDB27B37764005EA1BE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; };
|
||||
5BE78BDF27B37968005EA1BE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = "<group>"; };
|
||||
|
@ -465,6 +467,7 @@
|
|||
5BD0113C2818543900609769 /* KeyHandler_Core.swift */,
|
||||
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
|
||||
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */,
|
||||
5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */,
|
||||
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
|
||||
5B62A33727AE79CD00A19448 /* StringUtils.swift */,
|
||||
5BAA8FBD282CAF380066C406 /* SyllableComposer.swift */,
|
||||
|
@ -1204,6 +1207,7 @@
|
|||
5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */,
|
||||
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
|
||||
5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */,
|
||||
5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */,
|
||||
5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1389,7 +1393,7 @@
|
|||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
|
@ -1399,7 +1403,7 @@
|
|||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.8.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
|
||||
|
@ -1428,13 +1432,13 @@
|
|||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
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.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests;
|
||||
|
@ -1465,7 +1469,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
|
@ -1486,7 +1490,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1515,7 +1519,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
@ -1532,7 +1536,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1646,7 +1650,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
@ -1674,7 +1678,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1701,7 +1705,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
@ -1723,7 +1727,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -1745,7 +1749,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1765,7 +1769,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1787,7 +1791,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1984;
|
||||
CURRENT_PROJECT_VERSION = 1985;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1801,7 +1805,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.8.4;
|
||||
MARKETING_VERSION = 1.8.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
@ -296,7 +296,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase {
|
|||
XCTAssertTrue(state is InputState.Empty, "\(state)")
|
||||
}
|
||||
|
||||
func testisNumericPad() {
|
||||
func testisNumericPadKey() {
|
||||
var input = InputSignal(inputText: "b", keyCode: 0, charCode: charCode("b"), flags: [], isVerticalTyping: false)
|
||||
var state: InputStateProtocol = InputState.Empty()
|
||||
_ = handler.handle(input: input, state: state) { newState in
|
||||
|
@ -304,7 +304,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase {
|
|||
} errorCallback: {
|
||||
}
|
||||
input = InputSignal(
|
||||
inputText: "1", keyCode: 0, charCode: charCode("1"), flags: .numericPad, isVerticalTyping: false
|
||||
inputText: "1", keyCode: 83, charCode: charCode("1"), flags: [], isVerticalTyping: false
|
||||
)
|
||||
var count = 0
|
||||
var empty: InputStateProtocol = InputState.Empty()
|
||||
|
|
Loading…
Reference in New Issue