diff --git a/macSKK/KeyBinding.swift b/macSKK/KeyBinding.swift index 10abf16..729bc76 100644 --- a/macSKK/KeyBinding.swift +++ b/macSKK/KeyBinding.swift @@ -31,6 +31,8 @@ struct KeyBinding: Identifiable, Hashable { case enter /// デフォルトはSpaceキー case space + /// 前の変換候補に移動する。デフォルトはxキー。 + case backwardCandidate /// 補完候補の確定用。デフォルトはTabキー case tab /// デフォルトはBackspaceキーとCtrl-hキー @@ -316,6 +318,8 @@ struct KeyBinding: Identifiable, Hashable { return KeyBinding(action, [Input(key: .code(0x24), modifierFlags: [], optionalModifierFlags: [.shift, .option])]) case .space: return KeyBinding(action, [Input(key: .code(0x31), modifierFlags: [])]) + case .backwardCandidate: + return KeyBinding(action, [Input(key: .character("x"), modifierFlags: [])]) case .tab: return KeyBinding(action, [Input(key: .code(0x30), modifierFlags: [], optionalModifierFlags: .shift)]) case .backspace: diff --git a/macSKK/KeyBindingSet.swift b/macSKK/KeyBindingSet.swift index ad069ad..223fb24 100644 --- a/macSKK/KeyBindingSet.swift +++ b/macSKK/KeyBindingSet.swift @@ -52,11 +52,11 @@ struct KeyBindingSet: Identifiable, Hashable { guard let id = dict["id"] as? String, let keyBindings = dict["keyBindings"] as? [[String: Any]], let version = dict["version"] as? Int else { return nil } + var values: [KeyBinding] = [] if version != Self.serializeVersion { - logger.error("シリアライズバージョンが合わないためキーバインド \(id, privacy: .public) が読み込めません。(現在: \(Self.serializeVersion), 環境設定: \(version)") + logger.error("シリアライズバージョンが合わないためキーバインド \(id, privacy: .public) が読み込めません。(現在: \(Self.serializeVersion), 環境設定: \(version))") return nil } - var values: [KeyBinding] = [] for dict in keyBindings { guard let keyBinding = KeyBinding(dict: dict) else { logger.warning("キーバインド \(id, privacy: .public) の読み込みに失敗しました") @@ -64,6 +64,12 @@ struct KeyBindingSet: Identifiable, Hashable { } values.append(keyBinding) } + // 不足しているキーバインドがあればデフォルト値を設定する + KeyBinding.defaultKeyBindingSettings.forEach { keyBinding in + if values.allSatisfy({ $0.action != keyBinding.action }) { + values.append(keyBinding) + } + } self.init(id: id, values: values) } diff --git a/macSKK/StateMachine.swift b/macSKK/StateMachine.swift index f26ea4b..d641e8a 100644 --- a/macSKK/StateMachine.swift +++ b/macSKK/StateMachine.swift @@ -340,7 +340,7 @@ final class StateMachine { case .eisu: // 何もしない (OSがIMEの切り替えはしてくれる) return true - case .unregister, nil: + case .unregister, .backwardCandidate, nil: break } @@ -738,7 +738,7 @@ final class StateMachine { return true case .abbrev, .up, .down, .registerPaste, .eisu, .kana: return true - case .unregister, .none: + case .unregister, .backwardCandidate, .none: break } @@ -1063,6 +1063,8 @@ final class StateMachine { } updateMarkedText() return true + case .backwardCandidate: + return handleSelectingPrevious(diff: -1, selecting: selecting) case .tab: return true case .stickyShift, .hiragana, .hankakuKana: @@ -1139,9 +1141,7 @@ final class StateMachine { } if let input = action.event.charactersIgnoringModifiers { - if input == "x" { - return handleSelectingPrevious(diff: -1, selecting: selecting) - } else if input == "." && action.shiftIsPressed() { + if input == "." && action.shiftIsPressed() { // 選択中候補で確定し、接尾辞入力に移行。 // カーソル位置より右に文字列がある場合は接頭辞入力として扱う (無視してもいいかも) addWordToUserDict(yomi: selecting.yomi, okuri: selecting.okuri, candidate: selecting.candidates[selecting.candidateIndex]) diff --git a/macSKK/en.lproj/Localizable.strings b/macSKK/en.lproj/Localizable.strings index c3b6dfa..c8a8198 100644 --- a/macSKK/en.lproj/Localizable.strings +++ b/macSKK/en.lproj/Localizable.strings @@ -97,6 +97,7 @@ "KeyBindingActionStickyshift" = "Sticky Shift"; "KeyBindingActionEnter" = "Enter"; "KeyBindingActionSpace" = "Space"; +"KeyBindingActionBackwardcandidate" = "Select backward candidate"; "KeyBindingActionTab" = "Tab"; "KeyBindingActionBackspace" = "Backspace"; "KeyBindingActionDelete" = "Delete"; diff --git a/macSKK/ja.lproj/Localizable.strings b/macSKK/ja.lproj/Localizable.strings index 4d120c4..8acf6ea 100644 --- a/macSKK/ja.lproj/Localizable.strings +++ b/macSKK/ja.lproj/Localizable.strings @@ -97,6 +97,7 @@ "KeyBindingActionStickyshift" = "Sticky Shift"; "KeyBindingActionEnter" = "Enter"; "KeyBindingActionSpace" = "Space"; +"KeyBindingActionBackwardcandidate" = "前の変換候補を選択"; "KeyBindingActionTab" = "Tab"; "KeyBindingActionBackspace" = "Backspace"; "KeyBindingActionDelete" = "Delete"; diff --git a/macSKKTests/StateMachineTests.swift b/macSKKTests/StateMachineTests.swift index a6fe43a..1c1be3e 100644 --- a/macSKKTests/StateMachineTests.swift +++ b/macSKKTests/StateMachineTests.swift @@ -234,16 +234,19 @@ final class StateMachineTests: XCTestCase { @MainActor func testHandleNormalPrintable() { let stateMachine = StateMachine(initialState: IMEState(inputMode: .direct)) let expectation = XCTestExpectation() - stateMachine.inputMethodEvent.collect(3).sink { events in + stateMachine.inputMethodEvent.collect(4).sink { events in XCTAssertEqual(events[0], .fixedText("c")) XCTAssertEqual(events[1], .fixedText("C")) XCTAssertEqual(events[2], .fixedText("X")) + XCTAssertEqual(events[3], .fixedText("x")) expectation.fulfill() }.store(in: &cancellables) XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "c"))) XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "c", withShift: true))) // 変換候補選択画面で登録解除へ遷移するキー。Normalではなにも起きない XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "x", withShift: true))) + // 変換候補選択画面で前の候補へ遷移するキー。Normalではなにも起きない + XCTAssertTrue(stateMachine.handle(printableKeyEventAction(character: "x"))) wait(for: [expectation], timeout: 1.0) } @@ -2979,7 +2982,7 @@ final class StateMachineTests: XCTestCase { case "q": return withShift ? .japanese : .toggleKana case "x": - return withShift ? .unregister : nil + return withShift ? .unregister : .backwardCandidate case ";": return withShift ? nil : .stickyShift case "/":