Refactors the input controller.
This commit is contained in:
parent
6fe2fc59f3
commit
1ad9e23918
|
@ -52,7 +52,7 @@
|
|||
D44FB74727919D35003C80A6 /* EmacsKeyHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */; };
|
||||
D44FB74A2791B829003C80A6 /* VXHanConvert in Frameworks */ = {isa = PBXBuildFile; productRef = D44FB7492791B829003C80A6 /* VXHanConvert */; };
|
||||
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */; };
|
||||
D456576E279E4F7B00DF6BC9 /* KeyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* KeyHandler.swift */; };
|
||||
D456576E279E4F7B00DF6BC9 /* KeyHandlerInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */; };
|
||||
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
|
||||
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
|
||||
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; };
|
||||
|
@ -206,7 +206,7 @@
|
|||
D44FB7482791B346003C80A6 /* VXHanConvert */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = VXHanConvert; path = Packages/VXHanConvert; sourceTree = "<group>"; };
|
||||
D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PhraseReplacementMap.cpp; sourceTree = "<group>"; };
|
||||
D44FB74C2792189A003C80A6 /* PhraseReplacementMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhraseReplacementMap.h; sourceTree = "<group>"; };
|
||||
D456576D279E4F7B00DF6BC9 /* KeyHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler.swift; sourceTree = "<group>"; };
|
||||
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerInput.swift; sourceTree = "<group>"; };
|
||||
D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.swift; sourceTree = "<group>"; };
|
||||
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||
|
@ -304,13 +304,13 @@
|
|||
D41355D6278D7409005E5CBD /* LanguageModelManager.h */,
|
||||
D41355D7278D7409005E5CBD /* LanguageModelManager.mm */,
|
||||
D461B791279DAC010070E734 /* InputState.swift */,
|
||||
D456576D279E4F7B00DF6BC9 /* KeyHandler.swift */,
|
||||
D47B92BF27972AC800458394 /* main.swift */,
|
||||
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */,
|
||||
D427F76B278CA1BA004A2160 /* AppDelegate.swift */,
|
||||
D44FB74427915555003C80A6 /* Preferences.swift */,
|
||||
D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */,
|
||||
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */,
|
||||
D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */,
|
||||
D47B92BF27972AC800458394 /* main.swift */,
|
||||
6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */,
|
||||
D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */,
|
||||
);
|
||||
|
@ -708,7 +708,7 @@
|
|||
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */,
|
||||
D44FB74527915565003C80A6 /* Preferences.swift in Sources */,
|
||||
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */,
|
||||
D456576E279E4F7B00DF6BC9 /* KeyHandler.swift in Sources */,
|
||||
D456576E279E4F7B00DF6BC9 /* KeyHandlerInput.swift in Sources */,
|
||||
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */,
|
||||
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */,
|
||||
D41355DB278E6D17005E5CBD /* McBopomofoLM.cpp in Sources */,
|
||||
|
|
|
@ -53,11 +53,6 @@ namespace Formosa {
|
|||
void setJoinSeparator(const string& separator);
|
||||
const string joinSeparator() const;
|
||||
|
||||
// TODO: Remove these later.
|
||||
size_t markerCursorIndex() const;
|
||||
// TODO: Remove these later.
|
||||
void setMarkerCursorIndex(size_t inNewIndex);
|
||||
|
||||
vector<string> readingsAtRange(size_t begin, size_t end) const;
|
||||
|
||||
Grid& grid();
|
||||
|
@ -71,7 +66,6 @@ namespace Formosa {
|
|||
static const size_t MaximumBuildSpanLength = 6;
|
||||
|
||||
size_t m_cursorIndex;
|
||||
size_t m_markerCursorIndex;
|
||||
vector<string> m_readings;
|
||||
|
||||
Grid m_grid;
|
||||
|
@ -82,14 +76,12 @@ namespace Formosa {
|
|||
inline BlockReadingBuilder::BlockReadingBuilder(LanguageModel *inLM)
|
||||
: m_LM(inLM)
|
||||
, m_cursorIndex(0)
|
||||
, m_markerCursorIndex(SIZE_MAX)
|
||||
{
|
||||
}
|
||||
|
||||
inline void BlockReadingBuilder::clear()
|
||||
{
|
||||
m_cursorIndex = 0;
|
||||
m_markerCursorIndex = SIZE_MAX;
|
||||
m_readings.clear();
|
||||
m_grid.clear();
|
||||
}
|
||||
|
@ -108,21 +100,6 @@ namespace Formosa {
|
|||
{
|
||||
m_cursorIndex = inNewIndex > m_readings.size() ? m_readings.size() : inNewIndex;
|
||||
}
|
||||
|
||||
inline size_t BlockReadingBuilder::markerCursorIndex() const
|
||||
{
|
||||
return m_markerCursorIndex;
|
||||
}
|
||||
|
||||
inline void BlockReadingBuilder::setMarkerCursorIndex(size_t inNewIndex)
|
||||
{
|
||||
if (inNewIndex == SIZE_MAX) {
|
||||
m_markerCursorIndex = SIZE_MAX;
|
||||
return;
|
||||
}
|
||||
|
||||
m_markerCursorIndex = inNewIndex > m_readings.size() ? m_readings.size() : inNewIndex;
|
||||
}
|
||||
|
||||
inline void BlockReadingBuilder::insertReadingAtCursor(const string& inReading)
|
||||
{
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
#import "Gramambular.h"
|
||||
#import "McBopomofoLM.h"
|
||||
#import "UserOverrideModel.h"
|
||||
#import "McBopomofo-Swift.h"
|
||||
|
||||
@interface McBopomofoInputMethodController : IMKInputController
|
||||
{
|
||||
@interface McBopomofoInputMethodController : IMKInputController {
|
||||
@private
|
||||
// the reading buffer that takes user input
|
||||
Formosa::Mandarin::BopomofoReadingBuffer* _bpmfReadingBuffer;
|
||||
Formosa::Mandarin::BopomofoReadingBuffer *_bpmfReadingBuffer;
|
||||
|
||||
// language model
|
||||
McBopomofo::McBopomofoLM *_languageModel;
|
||||
|
@ -41,26 +41,19 @@
|
|||
McBopomofo::UserOverrideModel *_userOverrideModel;
|
||||
|
||||
// the grid (lattice) builder for the unigrams (and bigrams)
|
||||
Formosa::Gramambular::BlockReadingBuilder* _builder;
|
||||
Formosa::Gramambular::BlockReadingBuilder *_builder;
|
||||
|
||||
// latest walked path (trellis) using the Viterbi algorithm
|
||||
std::vector<Formosa::Gramambular::NodeAnchor> _walkedNodes;
|
||||
|
||||
// the latest composing buffer that is updated to the foreground app
|
||||
NSMutableString *_composingBuffer;
|
||||
NSInteger _latestReadingCursor;
|
||||
|
||||
// the current text input client; we need to keep this when candidate panel is on
|
||||
id _currentCandidateClient;
|
||||
|
||||
// a special deferred client for Terminal.app fix
|
||||
id _currentDeferredClient;
|
||||
|
||||
// current available candidates
|
||||
NSMutableArray *_candidates;
|
||||
|
||||
// current input mode
|
||||
NSString *_inputMode;
|
||||
}
|
||||
|
||||
- (BOOL)handleInput:(KeyHandlerInput *)input
|
||||
state:(InputState *)state
|
||||
stateCallback:(void (^)(InputState *))stateCallback
|
||||
candidateSelectionCallback:(void (^)(void))candidateSelectionCallback
|
||||
errorCallback:(void (^)(void))errorCallback;
|
||||
|
||||
- (void)handleState:(InputState *)newState
|
||||
client:(id)client;
|
||||
|
||||
@end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,14 +1,18 @@
|
|||
import Cocoa
|
||||
|
||||
/// Represents the states for the input controller.
|
||||
class InputState: NSObject {
|
||||
}
|
||||
|
||||
/// Represents that the input controller is deactive.
|
||||
class InputStateDeactive: InputState {
|
||||
}
|
||||
|
||||
/// Represents that the composing buffer is empty.
|
||||
class InputStateEmpty: InputState {
|
||||
}
|
||||
|
||||
/// Represents that the input controller is committing text into client app.
|
||||
class InputStateCommitting: InputState {
|
||||
@objc private(set) var poppedText: String = ""
|
||||
|
||||
|
@ -18,17 +22,28 @@ class InputStateCommitting: InputState {
|
|||
}
|
||||
}
|
||||
|
||||
class InputStateInputting: InputState {
|
||||
/// Represents that the composing buffer is not empty.
|
||||
class InputStateNotEmpty: InputState {
|
||||
@objc private(set) var composingBuffer: String = ""
|
||||
@objc private(set) var cursorIndex: UInt = 0
|
||||
@objc var poppedText: String = ""
|
||||
|
||||
@objc init(composingBuffer: String, cursorIndex: UInt) {
|
||||
self.composingBuffer = composingBuffer
|
||||
self.cursorIndex = cursorIndex
|
||||
}
|
||||
}
|
||||
|
||||
@objc var attributedSting: NSAttributedString {
|
||||
/// Represents that the user is inputting text.
|
||||
class InputStateInputting: InputStateNotEmpty {
|
||||
@objc var bpmfReading: String = ""
|
||||
@objc var bpmfReadingCursotIndex: UInt8 = 0
|
||||
@objc var poppedText: String = ""
|
||||
|
||||
@objc override init(composingBuffer: String, cursorIndex: UInt) {
|
||||
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||
}
|
||||
|
||||
@objc var attributedString: NSAttributedString {
|
||||
let attrs: [NSAttributedString.Key : Any] = [
|
||||
.underlineStyle: NSUnderlineStyle.single,
|
||||
.markedClauseSegment: 0
|
||||
|
@ -38,13 +53,37 @@ class InputStateInputting: InputState {
|
|||
}
|
||||
}
|
||||
|
||||
class InputStateMarking: InputStateInputting {
|
||||
private let kMinMarkRangeLength = 2
|
||||
private let kMaxMarkRangeLength = 6
|
||||
|
||||
|
||||
/// Represents that the user is marking a range in the composing buffer.
|
||||
class InputStateMarking: InputStateNotEmpty {
|
||||
@objc private(set) var readings: [String] = []
|
||||
@objc private(set) var markerIndex: UInt = 0
|
||||
@objc private(set) var markedRange: NSRange = NSRange(location: 0, length: 0)
|
||||
@objc var tooltip: String {
|
||||
return ""
|
||||
|
||||
if Preferences.phraseReplacementEnabled {
|
||||
return NSLocalizedString("Phrase replacement mode is on. Not suggested to add phrase in the mode.", comment: "")
|
||||
}
|
||||
if Preferences.chineseConversionStyle == 1 && Preferences.chineseConversionEnabled {
|
||||
return NSLocalizedString("Model based Chinese conversion is on. Not suggested to add phrase in the mode.", comment: "")
|
||||
}
|
||||
if markedRange.length == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||
if markedRange.length < kMinMarkRangeLength {
|
||||
return String(format: NSLocalizedString("You are now selecting \"%@\". You can add a phrase with two or more characters.", comment: ""), text)
|
||||
} else if (markedRange.length > kMaxMarkRangeLength) {
|
||||
return String(format: NSLocalizedString("You are now selecting \"%@\". A phrase cannot be longer than %d characters.", comment: ""), text, kMaxMarkRangeLength)
|
||||
}
|
||||
return String(format: NSLocalizedString("You are now selecting \"%@\". Press enter to add a new phrase.", comment: ""), text)
|
||||
}
|
||||
|
||||
|
||||
@objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt) {
|
||||
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||
self.markerIndex = markerIndex
|
||||
|
@ -53,7 +92,7 @@ class InputStateMarking: InputStateInputting {
|
|||
self.markedRange = NSMakeRange(Int(begin), Int(end - begin))
|
||||
}
|
||||
|
||||
@objc override var attributedSting: NSAttributedString {
|
||||
@objc var attributedString: NSAttributedString {
|
||||
let attributedSting = NSMutableAttributedString(string: composingBuffer)
|
||||
attributedSting.setAttributes([
|
||||
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single,
|
||||
|
@ -72,7 +111,23 @@ class InputStateMarking: InputStateInputting {
|
|||
}
|
||||
}
|
||||
|
||||
class InputStateChoosingCandidate: InputStateInputting {
|
||||
var markingRang: NSRange = NSRange(location: 0, length: 0)
|
||||
var candidates: [String] = []
|
||||
/// Represents that the user is choosing in a candidates list.
|
||||
class InputStateChoosingCandidate: InputStateNotEmpty {
|
||||
@objc private(set) var candidates: [String] = []
|
||||
@objc private(set) var useVerticalMode: Bool = false
|
||||
|
||||
@objc init(composingBuffer: String, cursorIndex: UInt, candidates: [String], useVerticalMode: Bool) {
|
||||
self.candidates = candidates
|
||||
self.useVerticalMode = useVerticalMode
|
||||
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||
}
|
||||
|
||||
@objc var attributedString: NSAttributedString {
|
||||
let attrs: [NSAttributedString.Key : Any] = [
|
||||
.underlineStyle: NSUnderlineStyle.single,
|
||||
.markedClauseSegment: 0
|
||||
]
|
||||
let attributedSting = NSAttributedString(string: composingBuffer, attributes: attrs)
|
||||
return attributedSting
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
import Cocoa
|
||||
|
||||
@objc enum KeyCode: UInt16 {
|
||||
case none = 0
|
||||
case enter = 76
|
||||
case up = 126
|
||||
case down = 125
|
||||
case left = 123
|
||||
case right = 124
|
||||
case pageUp = 116
|
||||
case pageDown = 121
|
||||
case home = 115
|
||||
case end = 119
|
||||
case delete = 117
|
||||
}
|
||||
|
||||
class KeyHandlerInput: NSObject {
|
||||
private (set) var event: NSEvent
|
||||
private (set) var useVerticalMode: Bool
|
||||
|
||||
@objc var inputText: String? {
|
||||
event.characters
|
||||
}
|
||||
|
||||
@objc var charCode: UInt16 {
|
||||
guard let inputText = inputText, inputText.count > 0 else {
|
||||
return 0
|
||||
}
|
||||
let first = inputText[inputText.startIndex].utf16.first!
|
||||
return first
|
||||
}
|
||||
|
||||
@objc var keyCode: UInt16 {
|
||||
event.keyCode
|
||||
}
|
||||
|
||||
@objc var flags: NSEvent.ModifierFlags {
|
||||
event.modifierFlags
|
||||
}
|
||||
|
||||
@objc var cursorForwardKey: KeyCode {
|
||||
useVerticalMode ? .down : .right
|
||||
}
|
||||
|
||||
@objc var cursorBackwardKey: KeyCode {
|
||||
useVerticalMode ? .up : .left
|
||||
}
|
||||
|
||||
@objc var extraChooseCandidateKey: KeyCode {
|
||||
useVerticalMode ? .left : .down
|
||||
}
|
||||
|
||||
@objc var absorbedArrowKey: KeyCode {
|
||||
useVerticalMode ? .right : .up
|
||||
}
|
||||
|
||||
@objc var verticalModeOnlyChooseCandidateKey: KeyCode {
|
||||
useVerticalMode ? absorbedArrowKey : .none
|
||||
}
|
||||
|
||||
init(event: NSEvent, isVerticalMode: Bool) {
|
||||
self.event = event
|
||||
self.useVerticalMode = isVerticalMode
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import Cocoa
|
||||
|
||||
@objc enum KeyCode: UInt16 {
|
||||
case none = 0
|
||||
case enter = 76
|
||||
case up = 126
|
||||
case down = 125
|
||||
case left = 123
|
||||
case right = 124
|
||||
case pageUp = 116
|
||||
case pageDown = 121
|
||||
case home = 115
|
||||
case end = 119
|
||||
case delete = 117
|
||||
}
|
||||
|
||||
@objc class KeyHandlerInput: NSObject {
|
||||
@objc private (set) var event: NSEvent
|
||||
@objc private (set) var useVerticalMode: Bool
|
||||
|
||||
@objc var inputText: String? {
|
||||
event.characters
|
||||
}
|
||||
|
||||
@objc var keyCode: UInt16 {
|
||||
event.keyCode
|
||||
}
|
||||
|
||||
@objc var flags: NSEvent.ModifierFlags {
|
||||
event.modifierFlags
|
||||
}
|
||||
|
||||
@objc private (set) var charCode: UInt16
|
||||
@objc private (set) var cursorForwardKey: KeyCode
|
||||
@objc private (set) var cursorBackwardKey: KeyCode
|
||||
@objc private (set) var extraChooseCandidateKey: KeyCode
|
||||
@objc private (set) var absorbedArrowKey: KeyCode
|
||||
@objc private (set) var verticalModeOnlyChooseCandidateKey: KeyCode
|
||||
@objc private (set) var emacsKey: McBopomofoEmacsKey
|
||||
|
||||
@objc init(event: NSEvent, isVerticalMode: Bool) {
|
||||
self.event = event
|
||||
self.useVerticalMode = isVerticalMode
|
||||
let charCode: UInt16 = {
|
||||
guard let inputText = event.characters, inputText.count > 0 else {
|
||||
return 0
|
||||
}
|
||||
let first = inputText[inputText.startIndex].utf16.first!
|
||||
return first
|
||||
}()
|
||||
|
||||
self.charCode = charCode
|
||||
self.emacsKey = EmacsKeyHelper.detect(charCode: charCode, flags: event.modifierFlags)
|
||||
self.cursorForwardKey = useVerticalMode ? .up : .left
|
||||
self.cursorBackwardKey = useVerticalMode ? .up : .left
|
||||
self.extraChooseCandidateKey = useVerticalMode ? .left : .down
|
||||
self.absorbedArrowKey = useVerticalMode ? .right : .up
|
||||
self.verticalModeOnlyChooseCandidateKey = useVerticalMode ? absorbedArrowKey : .none
|
||||
super.init()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue