UserDef // + dodgeInvalidEdgeCandidateCursorPosition.
This commit is contained in:
parent
095a0a34c5
commit
28e53c27ad
|
@ -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> {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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" = "いつでも全角で直接出力";
|
||||
|
|
|
@ -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" = "始终以全形文字直接递交";
|
||||
|
|
|
@ -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" = "始終以全形文字直接遞交";
|
||||
|
|
Loading…
Reference in New Issue