UserDef // + dodgeInvalidEdgeCandidateCursorPosition.

This commit is contained in:
ShikiSuen 2024-02-11 00:08:44 +08:00
parent 095a0a34c5
commit 28e53c27ad
12 changed files with 73 additions and 9 deletions

View File

@ -31,7 +31,7 @@ public protocol InputHandlerProtocol {
func clearComposerAndCalligrapher()
func ensureKeyboardParser()
func triageInput(event input: InputSignalProtocol) -> Bool
func generateStateOfCandidates() -> IMEStateProtocol
func generateStateOfCandidates(dodge: Bool) -> IMEStateProtocol
func generateStateOfInputting(sansReading: Bool, guarded: Bool) -> IMEStateProtocol
func generateStateOfAssociates(withPair pair: Megrez.KeyValuePaired) -> IMEStateProtocol
func consolidateNode(
@ -46,6 +46,10 @@ extension InputHandlerProtocol {
generateStateOfInputting(sansReading: sansReading, guarded: guarded)
}
func generateStateOfCandidates() -> IMEStateProtocol {
generateStateOfCandidates(dodge: true)
}
func consolidateNode(candidate: (keyArray: [String], value: String), respectCursorPushing: Bool, preConsolidate: Bool) {
consolidateNode(
candidate: candidate, respectCursorPushing: respectCursorPushing,
@ -83,6 +87,9 @@ public class InputHandler: InputHandlerProtocol {
public var delegate: InputHandlerDelegate?
public var prefs: PrefMgrProtocol
///
var backupCursor: Int?
///
let kEpsilon: Double = 0.000_001
@ -115,6 +122,7 @@ public class InputHandler: InputHandlerProtocol {
compositor.clear()
isCodePointInputMode = false
isHaninKeyboardSymbolMode = false
backupCursor = nil
}
/// /
@ -155,6 +163,43 @@ public class InputHandler: InputHandlerProtocol {
public var isCompositorEmpty: Bool { compositor.isEmpty }
func isInvalidEdgeCursorSituation(givenCursor: Int? = nil) -> Bool {
let cursorToCheck = givenCursor ?? compositor.cursor
// prefs.useRearCursorMode 0 (false) macOS
// prefs.useRearCursorMode 1 (true)
switch prefs.useRearCursorMode {
case false where cursorToCheck == 0: return true
case true where cursorToCheck == compositor.length: return true
default: return false
}
}
public func removeBackupCursor() {
backupCursor = nil
}
public func dodgeInvalidEdgeCursorForCandidateState() {
guard !prefs.useSCPCTypingMode else { return }
guard prefs.dodgeInvalidEdgeCandidateCursorPosition else { return }
guard isInvalidEdgeCursorSituation() else { return }
backupCursor = compositor.cursor
switch prefs.useRearCursorMode {
case false where compositor.cursor < compositor.length:
compositor.cursor += 1
if isCursorCuttingChar() { compositor.jumpCursorBySpan(to: .front) }
case true where compositor.cursor > 0:
compositor.cursor -= 1
if isCursorCuttingChar() { compositor.jumpCursorBySpan(to: .rear) }
default: break
}
}
public func restoreBackupCursor() {
guard let theBackupCursor = backupCursor else { return }
compositor.cursor = Swift.max(Swift.min(theBackupCursor, compositor.length), 0)
backupCursor = nil
}
///
/// - Returns:
func currentMarkedRange() -> Range<Int> {

View File

@ -25,6 +25,7 @@ extension InputHandler {
/// - Returns:
public func generateStateOfInputting(sansReading: Bool = false, guarded: Bool = false) -> IMEStateProtocol {
if isConsideredEmptyForNow, !guarded { return IMEState.ofAbortion() }
restoreBackupCursor() // Inputting
var segHighlightedAt: Int?
let cpInput = isCodePointInputMode && !sansReading
/// (Update the composing buffer)
@ -134,11 +135,15 @@ extension InputHandler {
///
/// - Returns:
public func generateStateOfCandidates() -> IMEStateProtocol {
public func generateStateOfCandidates(dodge: Bool = true) -> IMEStateProtocol {
guard let delegate = delegate else { return IMEState.ofAbortion() }
if dodge, delegate.state.type == .ofInputting {
dodgeInvalidEdgeCursorForCandidateState()
}
var result = IMEState.ofCandidates(
candidates: generateArrayOfCandidates(fixOrder: prefs.useFixedCandidateOrderOnSelection),
displayTextSegments: compositor.walkedNodes.values,
cursor: delegate?.state.cursor ?? generateStateOfInputting().cursor
cursor: compositor.cursor
)
if !prefs.useRearCursorMode {
let markerBackup = compositor.marker
@ -1001,7 +1006,7 @@ extension InputHandler {
inputting.textToCommit = textToCommit
delegate.switchState(inputting)
//
let newState = generateStateOfCandidates()
let newState = generateStateOfCandidates(dodge: false)
_ = newState.candidates.isEmpty ? delegate.callError("B5127D8A") : delegate.switchState(newState)
} else { //
delegate.callError("17446655")

View File

@ -106,6 +106,9 @@ import SwiftExtension
@AppProperty(key: UserDef.kMoveCursorAfterSelectingCandidate.rawValue, defaultValue: true)
public dynamic var moveCursorAfterSelectingCandidate: Bool
@AppProperty(key: UserDef.kDodgeInvalidEdgeCandidateCursorPosition.rawValue, defaultValue: true)
public dynamic var dodgeInvalidEdgeCandidateCursorPosition: Bool
@AppProperty(key: UserDef.kUseDynamicCandidateWindowOrigin.rawValue, defaultValue: true)
public dynamic var useDynamicCandidateWindowOrigin: Bool

View File

@ -281,7 +281,7 @@ extension SessionCtl: CtlCandidateDelegate {
//
var newState: IMEStateProtocol = updateResult
? inputHandler.generateStateOfCandidates()
? inputHandler.generateStateOfCandidates(dodge: false)
: IMEState.ofCommitting(textToCommit: state.displayedText)
newState.tooltipDuration = 1.85
var tooltipMessage = ""

View File

@ -51,6 +51,7 @@ public extension SettingsPanesCocoa {
UserDef.kUseRearCursorMode.render(fixWidth: contentWidth)
UserDef.kMoveCursorAfterSelectingCandidate.render(fixWidth: contentWidth)
UserDef.kUseDynamicCandidateWindowOrigin.render(fixWidth: contentWidth)
UserDef.kDodgeInvalidEdgeCandidateCursorPosition.render(fixWidth: contentWidth)
}?.boxed()
NSStackView.buildSection(width: contentWidth) {
UserDef.kShowReverseLookupInCandidateUI.render(fixWidth: contentWidth)

View File

@ -41,6 +41,9 @@ public struct VwrSettingsPaneCandidates: View {
@AppStorage(wrappedValue: true, UserDef.kUseDynamicCandidateWindowOrigin.rawValue)
private var useDynamicCandidateWindowOrigin: Bool
@AppStorage(wrappedValue: true, UserDef.kDodgeInvalidEdgeCandidateCursorPosition.rawValue)
private var dodgeInvalidEdgeCandidateCursorPosition: Bool
@AppStorage(wrappedValue: false, UserDef.kUseFixedCandidateOrderOnSelection.rawValue)
private var useFixedCandidateOrderOnSelection: Bool
@ -62,6 +65,7 @@ public struct VwrSettingsPaneCandidates: View {
UserDef.kUseDynamicCandidateWindowOrigin.bind($useDynamicCandidateWindowOrigin).render()
.disabled(useRearCursorMode)
}
UserDef.kDodgeInvalidEdgeCandidateCursorPosition.bind($dodgeInvalidEdgeCandidateCursorPosition).render()
}
Section {
VwrSettingsPaneCandidates_SelectionKeys()

View File

@ -31,6 +31,7 @@ public protocol PrefMgrProtocol {
var shouldAutoReloadUserDataFiles: Bool { get set }
var useRearCursorMode: Bool { get set }
var moveCursorAfterSelectingCandidate: Bool { get set }
var dodgeInvalidEdgeCandidateCursorPosition: Bool { get set }
var useDynamicCandidateWindowOrigin: Bool { get set }
var useHorizontalCandidateList: Bool { get set }
var chooseCandidateUsingSpace: Bool { get set }

View File

@ -63,6 +63,7 @@ public enum UserDef: String, CaseIterable, Identifiable {
case kCurrencyNumeralsEnabled = "CurrencyNumeralsEnabled"
case kHalfWidthPunctuationEnabled = "HalfWidthPunctuationEnable"
case kMoveCursorAfterSelectingCandidate = "MoveCursorAfterSelectingCandidate"
case kDodgeInvalidEdgeCandidateCursorPosition = "DodgeInvalidEdgeCandidateCursorPosition"
case kEscToCleanInputBuffer = "EscToCleanInputBuffer"
case kAcceptLeadingIntonations = "AcceptLeadingIntonations"
case kSpecifyIntonationKeyBehavior = "SpecifyIntonationKeyBehavior"
@ -183,6 +184,7 @@ public extension UserDef {
case .kCurrencyNumeralsEnabled: return .bool
case .kHalfWidthPunctuationEnabled: return .bool
case .kMoveCursorAfterSelectingCandidate: return .bool
case .kDodgeInvalidEdgeCandidateCursorPosition: return .bool
case .kEscToCleanInputBuffer: return .bool
case .kAcceptLeadingIntonations: return .bool
case .kSpecifyIntonationKeyBehavior: return .integer
@ -357,6 +359,9 @@ public extension UserDef {
case .kMoveCursorAfterSelectingCandidate: return .init(
userDef: self, shortTitle: "Push the cursor in front of the phrase after selection"
)
case .kDodgeInvalidEdgeCandidateCursorPosition: return .init(
userDef: self, shortTitle: "i18n:UserDef.kDodgeInvalidEdgeCandidateCursorPosition.shortTitle"
)
case .kEscToCleanInputBuffer: return .init(
userDef: self, shortTitle: "Use ESC key to clear the entire input buffer",
description: "If unchecked, the ESC key will try cleaning the unfinished readings / strokes first, and will commit the current composition buffer if there's no unfinished readings / strokes."

View File

@ -1,4 +1,3 @@
"! Succeeded in filtering a candidate." = "! Succeeded in filtering a candidate.";
"! Succeeded in filtering a user phrase." = "! Succeeded in filtering a user phrase.";
"%@-Stroke" = "%@-Stroke";
@ -191,6 +190,7 @@
"i18n:UserDef.kBypassNonAppleCapsLockHandling.shortTitle" = "Bypass the vChewing built-in Caps Lock handling";
"i18n:userdef.kCheckAbusersOfSecureEventInputAPI.description" = "Such abuse of SecureEventInput API in the background can hinder all 3rd-party input methods from being able to switch to. It is fine to use SecureEventInput for sensitive input fields. However, an app calling EnableSecureEventInput() is responsible to call DisableSecureEventInput() immediately right after the input field loses focus. This situation may also happen if an app is hanging in the background (or working as a helper application in the background) with its SecureEventInput left enabled.";
"i18n:UserDef.kCheckAbusersOfSecureEventInputAPI.shortTitle" = "Actively check those processes abusing the SecureEventInput API";
"i18n:UserDef.kDodgeInvalidEdgeCandidateCursorPosition.shortTitle" = "Dodge Invalid Edge Candidate Cursor Position";
"i18n:UserDef.kNumPadCharInputBehavior.description" = "Choose the behavior of numeric pad inputs.";
"i18n:UserDef.kNumPadCharInputBehavior.option.0" = "Always directly commit half-width chars";
"i18n:UserDef.kNumPadCharInputBehavior.option.1" = "Always directly commit full-width chars";

View File

@ -1,4 +1,3 @@
"! Succeeded in filtering a candidate." = "! 指定された文字候補は排除リストに登録しました。";
"! Succeeded in filtering a user phrase." = "! この語彙は排除リストに登録完了。";
"%@-Stroke" = "%@画";
@ -191,6 +190,7 @@
"i18n:UserDef.kBypassNonAppleCapsLockHandling.shortTitle" = "威注音入力アプリの内蔵CapsLock処理を不使用";
"i18n:userdef.kCheckAbusersOfSecureEventInputAPI.description" = "このような不正利用は「システム内蔵入力以外の全ての入力アプリがメニューで灰色状態で選べなくて使えない」の元凶である。センシティブな資料の記入どころでSecureEventInputをEnableSecureEventInput()で使うのは当然であるが、「入力中」状態が終わった後必ずDisableSecureEventInput()で状態解消すべきだと義務である。いくつかヘルパーアプリも、あるいはSecureEventInputを呼び起こしてからすぐ固まったアプリも、この状態になりやすい。特に、他のアプリの画面へ切り替えたとしても、固まったアプリのSecureEventInput状態は自動的に解消できぬ。";
"i18n:UserDef.kCheckAbusersOfSecureEventInputAPI.shortTitle" = "SecureEventInput API を不正利用しているバクグラウンド・プロセスを自動検知";
"i18n:UserDef.kDodgeInvalidEdgeCandidateCursorPosition.shortTitle" = "端末での不合理的な候補選択用カーソル位置を是正";
"i18n:UserDef.kNumPadCharInputBehavior.description" = "テンキー文字の入力行為をご指定ください。";
"i18n:UserDef.kNumPadCharInputBehavior.option.0" = "いつでも半角で直接出力";
"i18n:UserDef.kNumPadCharInputBehavior.option.1" = "いつでも全角で直接出力";

View File

@ -1,4 +1,3 @@
"! Succeeded in filtering a candidate." = "! 候选字词滤除成功。";
"! Succeeded in filtering a user phrase." = "! 成功滤除该词音配对。";
"%@-Stroke" = "%@画";
@ -191,6 +190,7 @@
"i18n:UserDef.kBypassNonAppleCapsLockHandling.shortTitle" = "不使用威注音输入法内建的 Caps Lock 处理";
"i18n:userdef.kCheckAbusersOfSecureEventInputAPI.description" = "这种滥用会导致系统内的所有第三方输入法全都无法正常使用(在输入法选单内会变成灰色)。针对需要填写敏感数据的场合,使用 SecureEventInput 无可厚非。但是,用 EnableSecureEventInput() 开启该模式之后,就有义务在输入窗格失焦的那一刻呼叫 DisableSecureEventInput() 来结束这种状态。这种状态还常见于后台辅助 App 当中、或者某个 App 在叫出该模式之后失去响应这样的话哪怕被切换到后台SecureEventInput 也不会自动解除)。";
"i18n:UserDef.kCheckAbusersOfSecureEventInputAPI.shortTitle" = "主动检测正在滥用 SecureEventInput API 的后台进程";
"i18n:UserDef.kDodgeInvalidEdgeCandidateCursorPosition.shortTitle" = "糾正不合理的端点选字游标位置";
"i18n:UserDef.kNumPadCharInputBehavior.description" = "指定数字小键盘的输入行为。";
"i18n:UserDef.kNumPadCharInputBehavior.option.0" = "始终以半形文字直接递交";
"i18n:UserDef.kNumPadCharInputBehavior.option.1" = "始终以全形文字直接递交";

View File

@ -1,4 +1,3 @@
"! Succeeded in filtering a candidate." = "! 候選字詞濾除成功。";
"! Succeeded in filtering a user phrase." = "! 成功濾除該詞音配對。";
"%@-Stroke" = "%@畫";
@ -191,6 +190,7 @@
"i18n:UserDef.kBypassNonAppleCapsLockHandling.shortTitle" = "不使用威注音輸入法內建的 Caps Lock 處理";
"i18n:userdef.kCheckAbusersOfSecureEventInputAPI.description" = "這種濫用會導致系統內的所有第三方輸入法全都無法正常使用(在輸入法選單內會變成灰色)。針對需要填寫敏感資料的場合,使用 SecureEventInput 無可厚非。但是,用 EnableSecureEventInput() 開啟該模式之後,就有義務在輸入窗格失焦的那一刻呼叫 DisableSecureEventInput() 來結束這種狀態。這種狀態還常見於後檯輔助 App 當中、或者某個 App 在叫出該模式之後失去回應這樣的話哪怕被切換到後檯SecureEventInput 也不會自動解除)。";
"i18n:UserDef.kCheckAbusersOfSecureEventInputAPI.shortTitle" = "主動偵測正在濫用 SecureEventInput API 的後檯執行緒";
"i18n:UserDef.kDodgeInvalidEdgeCandidateCursorPosition.shortTitle" = "糾正不合理的端點選字游標位置";
"i18n:UserDef.kNumPadCharInputBehavior.description" = "指定數字小鍵盤的輸入行為。";
"i18n:UserDef.kNumPadCharInputBehavior.option.0" = "始終以半形文字直接遞交";
"i18n:UserDef.kNumPadCharInputBehavior.option.1" = "始終以全形文字直接遞交";