Repo // ctlCandidateIMK, continued.

This commit is contained in:
ShikiSuen 2022-08-11 17:05:30 +08:00
parent 15308a1148
commit 6fcf4356ea
6 changed files with 281 additions and 36 deletions

View File

@ -214,6 +214,11 @@ struct InputSignal: CustomStringConvertible {
18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6", 26: "7", 28: "8", 25: "9", 29: "0",
]
var isCandidateKey: Bool {
mgrPrefs.candidateKeys.contains(inputText)
|| mgrPrefs.candidateKeys.contains(inputTextIgnoringModifiers ?? "114514")
}
/// flags KeyCode
var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) }
var isMainAreaNumKey: Bool { arrMainAreaNumKey.contains(keyCode) }

View File

@ -25,7 +25,7 @@ class ctlInputMethod: IMKInputController {
static var areWeNerfing = false
///
static var ctlCandidateCurrent: ctlCandidateProtocol = ctlCandidateUniversal.init(.horizontal)
static var ctlCandidateCurrent: ctlCandidateProtocol = ctlCandidateIMK.init(.horizontal)
///
static let tooltipController = TooltipController()
@ -47,7 +47,7 @@ class ctlInputMethod: IMKInputController {
}
/// `handle(event:)` Shift
private var rencentKeyHandledByKeyHandler = false
var rencentKeyHandledByKeyHandler = false
// MARK: -
@ -205,6 +205,12 @@ class ctlInputMethod: IMKInputController {
}
}
/// IMK IMK
if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK, ctlCandidateCurrent.visible {
ctlCandidateCurrent.interpretKeyEvents([event])
return true
}
/// flags使 KeyHandler
/// flags
/// event.type == .flagsChanged return false
@ -270,14 +276,12 @@ class ctlInputMethod: IMKInputController {
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
}
}
if let state = state as? InputState.ChoosingCandidate {
} else if let state = state as? InputState.SymbolTable {
return state.candidates.map { theCandidate -> String in
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
}
}
if let state = state as? InputState.SymbolTable {
} else if let state = state as? InputState.ChoosingCandidate {
return state.candidates.map { theCandidate -> String in
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
@ -285,4 +289,53 @@ class ctlInputMethod: IMKInputController {
}
return .init()
}
override open func candidateSelectionChanged(_: NSAttributedString!) {
//
}
override open func candidateSelected(_ candidateString: NSAttributedString!) {
if state is InputState.AssociatedPhrases {
if !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter {
handle(state: InputState.EmptyIgnoringPreviousState())
handle(state: InputState.Empty())
return
}
}
var indexDeducted = 0
if let state = state as? InputState.AssociatedPhrases {
for (i, neta) in state.candidates.map(\.1).enumerated() {
let theConverted = IME.kanjiConversionIfRequired(neta)
let netaShown = (neta == theConverted) ? neta : "\(theConverted)(\(neta))"
if candidateString.string == netaShown {
indexDeducted = i
break
}
}
} else if let state = state as? InputState.SymbolTable {
for (i, neta) in state.candidates.map(\.1).enumerated() {
let theConverted = IME.kanjiConversionIfRequired(neta)
let netaShown = (neta == theConverted) ? neta : "\(theConverted)(\(neta))"
if candidateString.string == netaShown {
indexDeducted = i
break
}
}
} else if let state = state as? InputState.ChoosingCandidate {
for (i, neta) in state.candidates.map(\.1).enumerated() {
let theConverted = IME.kanjiConversionIfRequired(neta)
let netaShown = (neta == theConverted) ? neta : "\(theConverted)(\(neta))"
if candidateString.string == netaShown {
indexDeducted = i
break
}
}
}
keyHandler(
keyHandler,
didSelectCandidateAt: indexDeducted,
ctlCandidate: ctlInputMethod.ctlCandidateCurrent
)
}
}

View File

@ -49,7 +49,70 @@ extension ctlInputMethod: KeyHandlerDelegate {
// MARK: - Candidate Controller Delegate
extension ctlInputMethod: ctlCandidateDelegate {
func handleDelegateEvent(_ event: NSEvent!) -> Bool { handle(event, client: client()) }
func handleDelegateEvent(_ event: NSEvent!) -> Bool {
// Shift macOS 10.15 macOS
if #available(macOS 10.15, *) {
if ShiftKeyUpChecker.check(event) {
if !rencentKeyHandledByKeyHandler {
NotifierController.notify(
message: String(
format: "%@%@%@", NSLocalizedString("Alphanumerical Mode", comment: ""), "\n",
toggleASCIIMode()
? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: "")
)
)
}
rencentKeyHandledByKeyHandler = false
return false
}
}
/// flags使 KeyHandler
/// flags
/// event.type == .flagsChanged return false
/// NSInternalInconsistencyException
if event.type == .flagsChanged { return false }
//
ctlInputMethod.areWeNerfing = event.modifierFlags.contains([.shift, .command])
var textFrame = NSRect.zero
let attributes: [AnyHashable: Any]? = client().attributes(
forCharacterIndex: 0, lineHeightRectangle: &textFrame
)
let isTypingVertical =
(attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false
if client().bundleIdentifier()
== "org.atelierInmu.vChewing.vChewingPhraseEditor"
{
IME.areWeUsingOurOwnPhraseEditor = true
} else {
IME.areWeUsingOurOwnPhraseEditor = false
}
var input = InputSignal(event: event, isVerticalTyping: isTypingVertical)
input.isASCIIModeInput = isASCIIMode
//
// KeyHandler
if !input.charCode.isPrintable {
return false
}
/// 調
/// result bool IMK
let result = keyHandler.handleCandidate(state: state, input: input) { newState in
self.handle(state: newState)
} errorCallback: {
clsSFX.beep()
}
rencentKeyHandledByKeyHandler = result
return result
}
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int {
_ = controller //

View File

@ -46,6 +46,8 @@ extension ctlInputMethod {
candidates = state.candidates
}
if isTypingVertical { return true }
// IMK
guard ctlInputMethod.ctlCandidateCurrent is ctlCandidateUniversal else { return false }
// 使
//
// 使
@ -65,11 +67,11 @@ extension ctlInputMethod {
/// macOS 10.x SwiftUI
if isCandidateWindowVertical { // 使
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical)
ctlInputMethod.ctlCandidateCurrent = ctlCandidateIMK.init(.vertical)
} else if mgrPrefs.useHorizontalCandidateList {
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
ctlInputMethod.ctlCandidateCurrent = ctlCandidateIMK.init(.horizontal)
} else {
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical)
ctlInputMethod.ctlCandidateCurrent = ctlCandidateIMK.init(.vertical)
}
// set the attributes for the candidate panel (which uses NSAttributedString)

View File

@ -18,6 +18,14 @@ public enum clsSFX {
AudioServicesPlaySystemSound(soundID)
}
static func fart() {
let filePath = Bundle.main.path(forResource: "Fart", ofType: "m4a")!
let fileURL = URL(fileURLWithPath: filePath)
var soundID: SystemSoundID = 0
AudioServicesCreateSystemSoundID(fileURL as CFURL, &soundID)
AudioServicesPlaySystemSound(soundID)
}
static func beep(count: Int = 1) {
if count <= 1 {
clsSFX.beep()

View File

@ -18,8 +18,6 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
}
}
public var selectedCandidateIndex: Int = .max
public var visible: Bool = false {
didSet {
if visible {
@ -30,10 +28,14 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
}
}
public var windowTopLeftPoint: NSPoint = .init(x: 0, y: 0) {
didSet {
public var windowTopLeftPoint: NSPoint {
get {
let frameRect = candidateFrame()
return NSPoint(x: frameRect.minX, y: frameRect.maxY)
}
set {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
self.set(windowTopLeftPoint: self.windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: 0)
self.set(windowTopLeftPoint: newValue, bottomOutOfScreenAdjustmentHeight: 0)
}
}
}
@ -60,7 +62,8 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
case .vertical:
setPanelType(kIMKSingleColumnScrollingCandidatePanel)
}
setAttributes([IMKCandidatesSendServerKeyEventFirst: false])
// true ctlIME
setAttributes([IMKCandidatesSendServerKeyEventFirst: true])
}
public required init(_ layout: CandidateLayout = .horizontal) {
@ -86,40 +89,151 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
update()
}
/// IMKCandidates
/// IMK keyHandler `ctlIME_Core`
private var currentPageIndex: Int = 0
private var pageCount: Int {
guard let delegate = delegate else {
return 0
}
let totalCount = delegate.candidateCountForController(self)
let keyLabelCount = keyLabels.count
return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0)
}
public func showNextPage() -> Bool {
guard delegate != nil else { return false }
if pageCount == 1 { return highlightNextCandidate() }
if currentPageIndex + 1 >= pageCount { clsSFX.beep() }
currentPageIndex = (currentPageIndex + 1 >= pageCount) ? 0 : currentPageIndex + 1
if selectedCandidateIndex == candidates(self).count - 1 { return false }
selectedCandidateIndex = min(selectedCandidateIndex + keyCount, candidates(self).count - 1)
return selectCandidate(withIdentifier: selectedCandidateIndex)
pageDownAndModifySelection(self)
return true
}
public func showPreviousPage() -> Bool {
guard delegate != nil else { return false }
if pageCount == 1 { return highlightPreviousCandidate() }
if currentPageIndex == 0 { clsSFX.beep() }
currentPageIndex = (currentPageIndex == 0) ? pageCount - 1 : currentPageIndex - 1
if selectedCandidateIndex == 0 { return true }
selectedCandidateIndex = max(selectedCandidateIndex - keyCount, 0)
return selectCandidate(withIdentifier: selectedCandidateIndex)
pageUpAndModifySelection(self)
return true
}
public func highlightNextCandidate() -> Bool {
if selectedCandidateIndex == candidates(self).count - 1 { return false }
selectedCandidateIndex = min(selectedCandidateIndex + 1, candidates(self).count - 1)
return selectCandidate(withIdentifier: selectedCandidateIndex)
guard let delegate = delegate else { return false }
selectedCandidateIndex =
(selectedCandidateIndex + 1 >= delegate.candidateCountForController(self))
? 0 : selectedCandidateIndex + 1
switch currentLayout {
case .horizontal:
moveRight(self)
return true
case .vertical:
moveDown(self)
return true
}
}
public func highlightPreviousCandidate() -> Bool {
if selectedCandidateIndex == 0 { return true }
selectedCandidateIndex = max(selectedCandidateIndex - 1, 0)
return selectCandidate(withIdentifier: selectedCandidateIndex)
}
public func candidateIndexAtKeyLabelIndex(_: Int) -> Int {
selectedCandidateIndex
}
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight _: CGFloat = 0) {
setCandidateFrameTopLeft(windowTopLeftPoint)
}
override public func handle(_ event: NSEvent!, client _: Any!) -> Bool {
guard let delegate = delegate else { return false }
return delegate.handleDelegateEvent(event)
selectedCandidateIndex =
(selectedCandidateIndex == 0)
? delegate.candidateCountForController(self) - 1 : selectedCandidateIndex - 1
switch currentLayout {
case .horizontal:
moveLeft(self)
return true
case .vertical:
moveUp(self)
return true
}
}
public func candidateIndexAtKeyLabelIndex(_ index: Int) -> Int {
guard let delegate = delegate else {
return Int.max
}
let result = currentPageIndex * keyLabels.count + index
return result < delegate.candidateCountForController(self) ? result : Int.max
}
public var selectedCandidateIndex: Int {
get {
selectedCandidate()
}
set {
selectCandidate(withIdentifier: newValue)
}
}
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
self.doSet(
windowTopLeftPoint: windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: height
)
}
}
func doSet(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) {
var adjustedPoint = windowTopLeftPoint
var adjustedHeight = height
var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero
for screen in NSScreen.screens {
let frame = screen.visibleFrame
if windowTopLeftPoint.x >= frame.minX, windowTopLeftPoint.x <= frame.maxX,
windowTopLeftPoint.y >= frame.minY, windowTopLeftPoint.y <= frame.maxY
{
screenFrame = frame
break
}
}
if adjustedHeight > screenFrame.size.height / 2.0 {
adjustedHeight = 0.0
}
let windowSize = candidateFrame().size
// bottom beneath the screen?
if adjustedPoint.y - windowSize.height < screenFrame.minY {
adjustedPoint.y = windowTopLeftPoint.y + adjustedHeight + windowSize.height
}
// top over the screen?
if adjustedPoint.y >= screenFrame.maxY {
adjustedPoint.y = screenFrame.maxY - 1.0
}
// right
if adjustedPoint.x + windowSize.width >= screenFrame.maxX {
adjustedPoint.x = screenFrame.maxX - windowSize.width
}
// left
if adjustedPoint.x < screenFrame.minX {
adjustedPoint.x = screenFrame.minX
}
setCandidateFrameTopLeft(adjustedPoint)
}
override public func interpretKeyEvents(_ eventArray: [NSEvent]) {
guard !eventArray.isEmpty else { return }
let event = eventArray[0]
let input = InputSignal(event: event)
guard let delegate = delegate else { return }
if input.isEsc || input.isBackSpace || input.isDelete || input.isShiftHold {
_ = delegate.handleDelegateEvent(event)
} else {
super.interpretKeyEvents(eventArray)
}
}
}