InputHandler // Implementing Code Point Input support.

This commit is contained in:
ShikiSuen 2023-02-09 16:55:33 +08:00
parent 5245585b60
commit 8734ebd68f
9 changed files with 155 additions and 7 deletions

View File

@ -108,6 +108,29 @@ public class InputHandler: InputHandlerProtocol {
public func clear() {
clearComposerAndCalligrapher()
compositor.clear()
isCodePointInputMode = false
}
// MARK: - Codepoint Input Buffer.
var isCodePointInputMode = false {
willSet {
strCodePointBuffer.removeAll()
}
}
var strCodePointBuffer = ""
var tooltipCodePointInputMode: String {
let commonTerm = NSMutableString()
commonTerm.insert("Code Point Input Mode.".localized, at: 0)
switch IMEApp.currentInputMode {
case .imeModeCHS: commonTerm.insert("[GB] ", at: 0)
case .imeModeCHT: commonTerm.insert("[Big5] ", at: 0)
default: break
}
commonTerm.append("  ")
return commonTerm.description
}
// MARK: - Functions dealing with Megrez.
@ -366,7 +389,10 @@ public class InputHandler: InputHandlerProtocol {
// MARK: - Extracted methods and functions (Tekkon).
var isComposerOrCalligrapherEmpty: Bool { prefs.cassetteEnabled ? calligrapher.isEmpty : composer.isEmpty }
var isComposerOrCalligrapherEmpty: Bool {
if !strCodePointBuffer.isEmpty { return false }
return prefs.cassetteEnabled ? calligrapher.isEmpty : composer.isEmpty
}
/// _
var currentKeyboardParser: String { currentKeyboardParserType.name + "_" }
@ -400,6 +426,7 @@ public class InputHandler: InputHandlerProtocol {
public func clearComposerAndCalligrapher() {
calligrapher.removeAll()
composer.clear()
strCodePointBuffer.removeAll()
}
func letComposerAndCalligrapherDoBackSpace() {

View File

@ -17,7 +17,9 @@ extension InputHandler {
/// - input:
/// - Returns: IMK
func handleComposition(input: InputSignalProtocol) -> Bool? {
prefs.cassetteEnabled ? handleCassetteComposition(input: input) : handlePhonabetComposition(input: input)
if isCodePointInputMode { return handleCodePointComposition(input: input) }
if prefs.cassetteEnabled { return handleCassetteComposition(input: input) }
return handlePhonabetComposition(input: input)
}
// MARK: (Handle BPMF Keys)
@ -305,4 +307,61 @@ extension InputHandler {
}
return nil
}
// MARK: (Handle Code Point Input)
/// InputHandler.HandleInput()
/// - Parameters:
/// - input:
/// - Returns: IMK
private func handleCodePointComposition(input: InputSignalProtocol) -> Bool? {
guard !input.isReservedKey else { return nil }
guard let delegate = delegate, input.text.count == 1 else { return nil }
guard !input.text.compactMap(\.hexDigitValue).isEmpty else {
delegate.callError("05DD692C輸入的字元並非 ASCII 字元。。")
return true
}
switch strCodePointBuffer.count {
case 0 ..< 4:
if strCodePointBuffer.count < 3 {
strCodePointBuffer.append(input.text)
var updatedState = generateStateOfInputting()
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
delegate.switchState(updatedState)
return true
}
let encoding: CFStringEncodings? = {
switch IMEApp.currentInputMode {
case .imeModeCHS: return .GB_18030_2000
case .imeModeCHT: return .big5_HKSCS_1999
default: return nil
}
}()
guard
var char = "\(strCodePointBuffer)\(input.text)"
.parsedAsHexLiteral(encoding: encoding)?.first?.description
else {
delegate.callError("D220B880輸入的字碼沒有對應的字元。")
var updatedState = IMEState.ofAbortion()
updatedState.tooltipDuration = 0
updatedState.tooltip = "Invalid Code Point.".localized + "  "
delegate.switchState(updatedState)
isCodePointInputMode = true
return true
}
// macOS
if char.count > 1 { char = char.map(\.description)[0] }
var updatedState = IMEState.ofCommitting(textToCommit: char)
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
delegate.switchState(updatedState)
isCodePointInputMode = true
return true
default:
delegate.switchState(generateStateOfInputting())
isCodePointInputMode = true
return true
}
}
}

View File

@ -162,8 +162,10 @@ extension InputHandler {
switch input.modifierFlags {
case []:
return handlePunctuationList(alternative: false, isJIS: isJIS)
case [.option]:
case [.option, .shift]:
return handlePunctuationList(alternative: true, isJIS: isJIS)
case .option:
return handleCodePointInputToggle()
default: break
}
case .kSpace: // Space

View File

@ -19,11 +19,16 @@ extension InputHandler {
///
public func generateStateOfInputting(sansReading: Bool = false) -> IMEStateProtocol {
let cpInput = isCodePointInputMode && !sansReading
/// (Update the composing buffer)
/// IMEStateData NSAttributeString
var displayTextSegments: [String] = compositor.walkedNodes.values
var cursor = convertCursorForDisplay(compositor.cursor)
let reading: String = sansReading ? "" : readingForDisplay //
var displayTextSegments: [String] = cpInput
? [strCodePointBuffer]
: compositor.walkedNodes.values
var cursor = cpInput
? displayTextSegments.joined().count
: convertCursorForDisplay(compositor.cursor)
let reading: String = (sansReading || isCodePointInputMode) ? "" : readingForDisplay //
if !reading.isEmpty {
var newDisplayTextSegments = [String]()
var temporaryNode = ""
@ -287,6 +292,9 @@ extension InputHandler {
@discardableResult func handleEnter(input: InputSignalProtocol, readingOnly: Bool = false) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if isCodePointInputMode { return handleCodePointInputToggle() }
guard state.type == .ofInputting else { return false }
var displayedText = state.displayedText
@ -384,7 +392,24 @@ extension InputHandler {
func handleBackSpace(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else { return false }
guard state.type == .ofInputting else {
isCodePointInputMode = false
return false
}
if isCodePointInputMode {
if !strCodePointBuffer.isEmpty {
strCodePointBuffer = strCodePointBuffer.dropLast(1).description
if !strCodePointBuffer.isEmpty {
var updatedState = generateStateOfInputting()
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
delegate.switchState(updatedState)
return true
}
}
return handleCodePointInputToggle()
}
// macOS Shift+BackSpace
shiftBksp: switch prefs.specifyShiftBackSpaceKeyBehavior {
@ -448,6 +473,9 @@ extension InputHandler {
func handleDelete(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if isCodePointInputMode { return handleCodePointInputToggle() }
guard state.type == .ofInputting else { return false }
if input.isShiftHold {
@ -543,6 +571,9 @@ extension InputHandler {
func handleEsc() -> Bool {
guard let delegate = delegate else { return false }
let state = delegate.state
if isCodePointInputMode { return handleCodePointInputToggle() }
guard state.type == .ofInputting else { return false }
if prefs.escToCleanInputBuffer {
@ -763,6 +794,25 @@ extension InputHandler {
return true
}
// MARK: -
@discardableResult func handleCodePointInputToggle() -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
if isCodePointInputMode {
isCodePointInputMode = false
delegate.switchState(IMEState.ofAbortion())
return true
}
var updatedState = generateStateOfInputting(sansReading: true)
delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText))
updatedState = IMEState.ofEmpty()
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
delegate.switchState(updatedState)
isCodePointInputMode = true
return true
}
// MARK: -
///

View File

@ -1,4 +1,6 @@
"vChewing" = "vChewing";
"Invalid Code Point." = "Invalid Code Point.";
"Code Point Input Mode." = "Code Point Input Mode.";
"You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas." = "You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas.";
"Uncheck" = "Uncheck";
"Leave it checked" = "Leave it checked";

View File

@ -1,4 +1,6 @@
"vChewing" = "vChewing";
"Invalid Code Point." = "Invalid Code Point.";
"Code Point Input Mode." = "Code Point Input Mode.";
"You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas." = "You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas.";
"Uncheck" = "Uncheck";
"Leave it checked" = "Leave it checked";

View File

@ -1,4 +1,6 @@
"vChewing" = "威注音入力アプリ";
"Invalid Code Point." = "コードポイントは正しくない。";
"Code Point Input Mode." = "コードポイント入力。";
"You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas." = "マナーモードのチェックの外すことになります。周りの人は貴殿のパソコンから屁音を聞いてしまい、それについての結果は全部貴殿の自己責任でございます。公的なる場所でこのチェックを外すのはお勧めできません。";
"Uncheck" = "チェックの外す";
"Leave it checked" = "チェックをそのままに";

View File

@ -1,4 +1,6 @@
"vChewing" = "威注音输入法";
"Invalid Code Point." = "内码不正确。";
"Code Point Input Mode." = "内码输入模式。";
"You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas." = "您即将取消对廉耻模式的勾选。周围的人会听到您的电脑传出放屁的声音,且您对此负全部责任。我们强烈建议您不要在任何公共场合取消对该模式的勾选。";
"Uncheck" = "取消勾选";
"Leave it checked" = "保持勾选";

View File

@ -1,4 +1,6 @@
"vChewing" = "威注音輸入法";
"Invalid Code Point." = "內碼不正確。";
"Code Point Input Mode." = "內碼輸入模式。";
"You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas." = "您即將取消對廉恥模式的勾選。周圍的人會聽到您的電腦傳出放屁的聲音,且您對此負全部責任。我們強烈建議您不要在任何公共場合取消對該模式的勾選。";
"Uncheck" = "取消勾選";
"Leave it checked" = "保持勾選";