Moves the way to render attributed string to the states.
This commit is contained in:
parent
82a916c433
commit
6fe2fc59f3
|
@ -241,7 +241,6 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
|
||||||
_languageModel->setExternalConverterEnabled(Preferences.chineseConversionStyle == 1);
|
_languageModel->setExternalConverterEnabled(Preferences.chineseConversionStyle == 1);
|
||||||
|
|
||||||
[(AppDelegate *)[NSApp delegate] checkForUpdate];
|
[(AppDelegate *)[NSApp delegate] checkForUpdate];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)deactivateServer:(id)client
|
- (void)deactivateServer:(id)client
|
||||||
|
@ -669,7 +668,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
|
||||||
// see if we have a unigram for this
|
// see if we have a unigram for this
|
||||||
if (!_languageModel->hasUnigramsForKey(reading)) {
|
if (!_languageModel->hasUnigramsForKey(reading)) {
|
||||||
[self beep];
|
[self beep];
|
||||||
[self handleState:_state client:client];
|
InputStateInputting *inputting = [self buildInputingState];
|
||||||
|
[self handleState:inputting client:client];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Foundation
|
import Cocoa
|
||||||
|
|
||||||
class InputState: NSObject {
|
class InputState: NSObject {
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,15 @@ class InputStateInputting: InputState {
|
||||||
self.composingBuffer = composingBuffer
|
self.composingBuffer = composingBuffer
|
||||||
self.cursorIndex = cursorIndex
|
self.cursorIndex = cursorIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc var attributedSting: NSAttributedString {
|
||||||
|
let attrs: [NSAttributedString.Key : Any] = [
|
||||||
|
.underlineStyle: NSUnderlineStyle.single,
|
||||||
|
.markedClauseSegment: 0
|
||||||
|
]
|
||||||
|
let attributedSting = NSAttributedString(string: composingBuffer, attributes: attrs)
|
||||||
|
return attributedSting
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputStateMarking: InputStateInputting {
|
class InputStateMarking: InputStateInputting {
|
||||||
|
@ -43,6 +52,24 @@ class InputStateMarking: InputStateInputting {
|
||||||
let end = max(cursorIndex, markerIndex)
|
let end = max(cursorIndex, markerIndex)
|
||||||
self.markedRange = NSMakeRange(Int(begin), Int(end - begin))
|
self.markedRange = NSMakeRange(Int(begin), Int(end - begin))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc override var attributedSting: NSAttributedString {
|
||||||
|
let attributedSting = NSMutableAttributedString(string: composingBuffer)
|
||||||
|
attributedSting.setAttributes([
|
||||||
|
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single,
|
||||||
|
NSAttributedString.Key.markedClauseSegment: 0
|
||||||
|
], range: NSRange(location: 0, length: markedRange.location))
|
||||||
|
attributedSting.setAttributes([
|
||||||
|
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single,
|
||||||
|
NSAttributedString.Key.markedClauseSegment: 1
|
||||||
|
], range: markedRange)
|
||||||
|
attributedSting.setAttributes([
|
||||||
|
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single,
|
||||||
|
NSAttributedString.Key.markedClauseSegment: 2
|
||||||
|
], range: NSRange(location: markedRange.location + markedRange.length,
|
||||||
|
length: composingBuffer.count - (markedRange.location + markedRange.length) ))
|
||||||
|
return attributedSting
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputStateChoosingCandidate: InputStateInputting {
|
class InputStateChoosingCandidate: InputStateInputting {
|
||||||
|
|
|
@ -18,11 +18,11 @@ class KeyHandlerInput: NSObject {
|
||||||
private (set) var event: NSEvent
|
private (set) var event: NSEvent
|
||||||
private (set) var useVerticalMode: Bool
|
private (set) var useVerticalMode: Bool
|
||||||
|
|
||||||
var inputText: String? {
|
@objc var inputText: String? {
|
||||||
event.characters
|
event.characters
|
||||||
}
|
}
|
||||||
|
|
||||||
var charCode: UInt16 {
|
@objc var charCode: UInt16 {
|
||||||
guard let inputText = inputText, inputText.count > 0 else {
|
guard let inputText = inputText, inputText.count > 0 else {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -30,31 +30,31 @@ class KeyHandlerInput: NSObject {
|
||||||
return first
|
return first
|
||||||
}
|
}
|
||||||
|
|
||||||
var keyCode: UInt16 {
|
@objc var keyCode: UInt16 {
|
||||||
event.keyCode
|
event.keyCode
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags: NSEvent.ModifierFlags {
|
@objc var flags: NSEvent.ModifierFlags {
|
||||||
event.modifierFlags
|
event.modifierFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
var cursorForwardKey: KeyCode {
|
@objc var cursorForwardKey: KeyCode {
|
||||||
useVerticalMode ? .down : .right
|
useVerticalMode ? .down : .right
|
||||||
}
|
}
|
||||||
|
|
||||||
var cursorBackwardKey: KeyCode {
|
@objc var cursorBackwardKey: KeyCode {
|
||||||
useVerticalMode ? .up : .left
|
useVerticalMode ? .up : .left
|
||||||
}
|
}
|
||||||
|
|
||||||
var extraChooseCandidateKey: KeyCode {
|
@objc var extraChooseCandidateKey: KeyCode {
|
||||||
useVerticalMode ? .left : .down
|
useVerticalMode ? .left : .down
|
||||||
}
|
}
|
||||||
|
|
||||||
var absorbedArrowKey: KeyCode {
|
@objc var absorbedArrowKey: KeyCode {
|
||||||
useVerticalMode ? .right : .up
|
useVerticalMode ? .right : .up
|
||||||
}
|
}
|
||||||
|
|
||||||
var verticalModeOnlyChooseCandidateKey: KeyCode {
|
@objc var verticalModeOnlyChooseCandidateKey: KeyCode {
|
||||||
useVerticalMode ? absorbedArrowKey : .none
|
useVerticalMode ? absorbedArrowKey : .none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,138 +63,3 @@ class KeyHandlerInput: NSObject {
|
||||||
self.useVerticalMode = isVerticalMode
|
self.useVerticalMode = isVerticalMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias KeyHandlerStateCallback = (InputState) -> ()
|
|
||||||
typealias KeyHandlerErrorCallback = () -> ()
|
|
||||||
|
|
||||||
@objc protocol KeyHandlerDelegate: AnyObject {
|
|
||||||
func keyHandlerRequestCurrentInputtingState(_ handler: KeyHandler) -> InputStateInputting
|
|
||||||
func keyHandler(_ handler: KeyHandler, requestWriteUserPhrase state: InputStateMarking ) -> Bool
|
|
||||||
|
|
||||||
func keyHandler(_ handler: KeyHandler, isCharCodeValidBmpfReading charCode:UInt16 ) -> Bool
|
|
||||||
func keyHandler(_ handler: KeyHandler, insertCharCodeToBmpfReading charCode:UInt16 ) -> Void
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class KeyHandler: NSObject {
|
|
||||||
var delegate: KeyHandlerDelegate?
|
|
||||||
|
|
||||||
@objc func handle(_ input: KeyHandlerInput,
|
|
||||||
currentState: InputState,
|
|
||||||
stateCallback: @escaping KeyHandlerStateCallback,
|
|
||||||
errorCallback: @escaping KeyHandlerErrorCallback
|
|
||||||
) -> Bool {
|
|
||||||
guard let delegate = delegate else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the inputText is empty, it's a function key combination, we ignore it
|
|
||||||
guard let inputText = input.inputText else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if inputText.isEmpty {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let flags = input.flags
|
|
||||||
let charCode = input.charCode
|
|
||||||
let keyCode = input.keyCode
|
|
||||||
let emacsKey = EmacsKeyHelper.detect(charCode: charCode, flags: flags)
|
|
||||||
|
|
||||||
// if the composing buffer is empty and there's no reading, and there is some function key combination, we ignore it
|
|
||||||
let isFunctionKey = flags.contains(.command) || flags.contains(.control) || flags.contains(.option) || flags.contains(.numericPad)
|
|
||||||
if currentState is InputStateInputting == false && isFunctionKey {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Caps Lock processing : if Caps Lock is on, temporarily disable bopomofo.
|
|
||||||
if charCode == 8 ||
|
|
||||||
charCode == 13 ||
|
|
||||||
keyCode == input.absorbedArrowKey.rawValue ||
|
|
||||||
keyCode == input.cursorForwardKey.rawValue ||
|
|
||||||
keyCode == input.cursorBackwardKey.rawValue {
|
|
||||||
// do nothing if backspace is pressed -- we ignore the key
|
|
||||||
} else if flags.contains(.capsLock) {
|
|
||||||
// process all possible combination, we hope.
|
|
||||||
stateCallback(InputStateEmpty())
|
|
||||||
|
|
||||||
// first commit everything in the buffer.
|
|
||||||
if flags.contains(.shift) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// if ASCII but not printable, don't use insertText:replacementRange: as many apps don't handle non-ASCII char insertions.
|
|
||||||
if charCode < 0x80 && isprint(Int32(charCode)) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
stateCallback(InputStateCommitting(poppedText: inputText.lowercased()))
|
|
||||||
stateCallback(InputStateEmpty())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if flags.contains(.numericPad) {
|
|
||||||
if keyCode != KeyCode.left.rawValue &&
|
|
||||||
keyCode != KeyCode.right.rawValue &&
|
|
||||||
keyCode != KeyCode.down.rawValue &&
|
|
||||||
keyCode != KeyCode.up.rawValue &&
|
|
||||||
charCode != 32 &&
|
|
||||||
isprint(Int32(charCode)) != 0 {
|
|
||||||
stateCallback(InputStateEmpty())
|
|
||||||
stateCallback(InputStateCommitting(poppedText: inputText.lowercased()))
|
|
||||||
stateCallback(InputStateEmpty())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// candidates?
|
|
||||||
|
|
||||||
if let state = currentState as? InputStateMarking {
|
|
||||||
// ESC
|
|
||||||
if charCode == 27 {
|
|
||||||
let inputting = InputStateInputting(composingBuffer: state.composingBuffer, cursorIndex: state.cursorIndex)
|
|
||||||
stateCallback(inputting)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Enter
|
|
||||||
if charCode == 13 {
|
|
||||||
if delegate.keyHandler(self, requestWriteUserPhrase: state) == false {
|
|
||||||
errorCallback()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
let inputting = InputStateInputting(composingBuffer: state.composingBuffer, cursorIndex: state.cursorIndex)
|
|
||||||
stateCallback(inputting)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Shift + left
|
|
||||||
if (keyCode == input.cursorBackwardKey.rawValue || emacsKey == .backward) && flags.contains(.shift) {
|
|
||||||
var index = state.markerIndex
|
|
||||||
if index > 0 {
|
|
||||||
index -= 1
|
|
||||||
let marking = InputStateMarking(composingBuffer: state.composingBuffer, cursorIndex: state.cursorIndex, markerIndex: state.markerIndex)
|
|
||||||
stateCallback(marking)
|
|
||||||
} else {
|
|
||||||
errorCallback()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (keyCode == input.cursorForwardKey.rawValue || emacsKey == .forward) && flags.contains(.shift) {
|
|
||||||
var index = state.markerIndex
|
|
||||||
if index < state.composingBuffer.count {
|
|
||||||
index += 1
|
|
||||||
let marking = InputStateMarking(composingBuffer: state.composingBuffer, cursorIndex: state.cursorIndex, markerIndex: state.markerIndex)
|
|
||||||
stateCallback(marking)
|
|
||||||
} else {
|
|
||||||
errorCallback()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue