Repo // Introducing ctlCandidateProtocol.
This commit is contained in:
parent
bdb7736f4c
commit
e0762339ef
|
@ -34,10 +34,10 @@ import Cocoa
|
||||||
|
|
||||||
/// KeyHandler 委任協定
|
/// KeyHandler 委任協定
|
||||||
protocol KeyHandlerDelegate {
|
protocol KeyHandlerDelegate {
|
||||||
func ctlCandidate() -> ctlCandidate
|
func ctlCandidate() -> ctlCandidateProtocol
|
||||||
func keyHandler(
|
func keyHandler(
|
||||||
_: KeyHandler, didSelectCandidateAt index: Int,
|
_: KeyHandler, didSelectCandidateAt index: Int,
|
||||||
ctlCandidate controller: ctlCandidate
|
ctlCandidate controller: ctlCandidateProtocol
|
||||||
)
|
)
|
||||||
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol)
|
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol)
|
||||||
-> Bool
|
-> Bool
|
||||||
|
|
|
@ -46,7 +46,7 @@ extension KeyHandler {
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
let inputText = input.inputText
|
let inputText = input.inputText
|
||||||
let charCode: UniChar = input.charCode
|
let charCode: UniChar = input.charCode
|
||||||
guard let ctlCandidateCurrent = delegate?.ctlCandidate() else {
|
guard var ctlCandidateCurrent = delegate?.ctlCandidate() else {
|
||||||
IME.prtDebugIntel("06661F6E")
|
IME.prtDebugIntel("06661F6E")
|
||||||
errorCallback()
|
errorCallback()
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -41,7 +41,7 @@ class ctlInputMethod: IMKInputController {
|
||||||
static var areWeDeleting = false
|
static var areWeDeleting = false
|
||||||
|
|
||||||
/// 目前在用的的選字窗副本。
|
/// 目前在用的的選字窗副本。
|
||||||
static var ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
|
static var ctlCandidateCurrent: ctlCandidateProtocol = ctlCandidateUniversal.init(.horizontal)
|
||||||
|
|
||||||
/// 工具提示視窗的副本。
|
/// 工具提示視窗的副本。
|
||||||
static let tooltipController = TooltipController()
|
static let tooltipController = TooltipController()
|
||||||
|
@ -236,4 +236,30 @@ class ctlInputMethod: IMKInputController {
|
||||||
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||||
resetKeyHandler()
|
resetKeyHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 生成 IMK 選字窗專用的候選字串陣列。
|
||||||
|
/// - Parameter sender: 呼叫了該函式的客體(無須使用)。
|
||||||
|
/// - Returns: IMK 選字窗專用的候選字串陣列。
|
||||||
|
override func candidates(_ sender: Any!) -> [Any]! {
|
||||||
|
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||||
|
if let state = state as? InputState.AssociatedPhrases {
|
||||||
|
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.ChoosingCandidate {
|
||||||
|
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 {
|
||||||
|
return state.candidates.map { theCandidate -> String in
|
||||||
|
let theConverted = IME.kanjiConversionIfRequired(theCandidate.1)
|
||||||
|
return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .init()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,11 @@ import Foundation
|
||||||
// MARK: - KeyHandler Delegate
|
// MARK: - KeyHandler Delegate
|
||||||
|
|
||||||
extension ctlInputMethod: KeyHandlerDelegate {
|
extension ctlInputMethod: KeyHandlerDelegate {
|
||||||
func ctlCandidate() -> ctlCandidate { ctlInputMethod.ctlCandidateCurrent }
|
func ctlCandidate() -> ctlCandidateProtocol { ctlInputMethod.ctlCandidateCurrent }
|
||||||
|
|
||||||
func keyHandler(
|
func keyHandler(
|
||||||
_: KeyHandler, didSelectCandidateAt index: Int,
|
_: KeyHandler, didSelectCandidateAt index: Int,
|
||||||
ctlCandidate controller: ctlCandidate
|
ctlCandidate controller: ctlCandidateProtocol
|
||||||
) {
|
) {
|
||||||
ctlCandidate(controller, didSelectCandidateAtIndex: index)
|
ctlCandidate(controller, didSelectCandidateAtIndex: index)
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
|
||||||
// MARK: - Candidate Controller Delegate
|
// MARK: - Candidate Controller Delegate
|
||||||
|
|
||||||
extension ctlInputMethod: ctlCandidateDelegate {
|
extension ctlInputMethod: ctlCandidateDelegate {
|
||||||
func candidateCountForController(_ controller: ctlCandidate) -> Int {
|
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int {
|
||||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||||
if let state = state as? InputState.ChoosingCandidate {
|
if let state = state as? InputState.ChoosingCandidate {
|
||||||
return state.candidates.count
|
return state.candidates.count
|
||||||
|
@ -80,7 +80,20 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
|
/// 直接給出全部的候選字詞的字音配對陣列
|
||||||
|
/// - Parameter controller: 對應的控制器。因為有唯一解,所以填錯了也不會有影響。
|
||||||
|
/// - Returns: 候選字詞陣列(字音配對)。
|
||||||
|
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] {
|
||||||
|
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||||
|
if let state = state as? InputState.ChoosingCandidate {
|
||||||
|
return state.candidates
|
||||||
|
} else if let state = state as? InputState.AssociatedPhrases {
|
||||||
|
return state.candidates
|
||||||
|
}
|
||||||
|
return .init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)
|
||||||
-> (String, String)
|
-> (String, String)
|
||||||
{
|
{
|
||||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||||
|
@ -92,7 +105,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
||||||
return ("", "")
|
return ("", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) {
|
func ctlCandidate(_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int) {
|
||||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||||
|
|
||||||
if let state = state as? InputState.SymbolTable,
|
if let state = state as? InputState.SymbolTable,
|
||||||
|
|
|
@ -81,11 +81,11 @@ extension ctlInputMethod {
|
||||||
/// 該問題徹底解決的價值並不大,直接等到 macOS 10.x 全線淘汰之後用 SwiftUI 重寫選字窗吧。
|
/// 該問題徹底解決的價值並不大,直接等到 macOS 10.x 全線淘汰之後用 SwiftUI 重寫選字窗吧。
|
||||||
|
|
||||||
if isCandidateWindowVertical { // 縱排輸入時強制使用縱排選字窗
|
if isCandidateWindowVertical { // 縱排輸入時強制使用縱排選字窗
|
||||||
ctlInputMethod.ctlCandidateCurrent = .init(.vertical)
|
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical)
|
||||||
} else if mgrPrefs.useHorizontalCandidateList {
|
} else if mgrPrefs.useHorizontalCandidateList {
|
||||||
ctlInputMethod.ctlCandidateCurrent = .init(.horizontal)
|
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
|
||||||
} else {
|
} else {
|
||||||
ctlInputMethod.ctlCandidateCurrent = .init(.vertical)
|
ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the attributes for the candidate panel (which uses NSAttributedString)
|
// set the attributes for the candidate panel (which uses NSAttributedString)
|
||||||
|
|
|
@ -26,6 +26,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
public enum CandidateLayout {
|
||||||
|
case horizontal
|
||||||
|
case vertical
|
||||||
|
}
|
||||||
|
|
||||||
public class CandidateKeyLabel: NSObject {
|
public class CandidateKeyLabel: NSObject {
|
||||||
public private(set) var key: String
|
public private(set) var key: String
|
||||||
public private(set) var displayedText: String
|
public private(set) var displayedText: String
|
||||||
|
@ -38,21 +43,38 @@ public class CandidateKeyLabel: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol ctlCandidateDelegate: AnyObject {
|
public protocol ctlCandidateDelegate: AnyObject {
|
||||||
func candidateCountForController(_ controller: ctlCandidate) -> Int
|
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int
|
||||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
|
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)]
|
||||||
|
func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)
|
||||||
-> (String, String)
|
-> (String, String)
|
||||||
func ctlCandidate(
|
func ctlCandidate(
|
||||||
_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int
|
_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ctlCandidate: NSWindowController {
|
public protocol ctlCandidateProtocol {
|
||||||
public enum Layout {
|
var currentLayout: CandidateLayout { get set }
|
||||||
case horizontal
|
var delegate: ctlCandidateDelegate? { get set }
|
||||||
case vertical
|
var selectedCandidateIndex: Int { get set }
|
||||||
}
|
var visible: Bool { get set }
|
||||||
|
var windowTopLeftPoint: NSPoint { get set }
|
||||||
|
var keyLabels: [CandidateKeyLabel] { get set }
|
||||||
|
var keyLabelFont: NSFont { get set }
|
||||||
|
var candidateFont: NSFont { get set }
|
||||||
|
var tooltip: String { get set }
|
||||||
|
|
||||||
public var currentLayout: Layout = .horizontal
|
init(_ layout: CandidateLayout)
|
||||||
|
func reloadData()
|
||||||
|
func showNextPage() -> Bool
|
||||||
|
func showPreviousPage() -> Bool
|
||||||
|
func highlightNextCandidate() -> Bool
|
||||||
|
func highlightPreviousCandidate() -> Bool
|
||||||
|
func candidateIndexAtKeyLabelIndex(_: Int) -> Int
|
||||||
|
func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ctlCandidate: NSWindowController, ctlCandidateProtocol {
|
||||||
|
public var currentLayout: CandidateLayout = .horizontal
|
||||||
public weak var delegate: ctlCandidateDelegate? {
|
public weak var delegate: ctlCandidateDelegate? {
|
||||||
didSet {
|
didSet {
|
||||||
reloadData()
|
reloadData()
|
||||||
|
@ -85,6 +107,15 @@ public class ctlCandidate: NSWindowController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
required public init(_ layout: CandidateLayout = .horizontal) {
|
||||||
|
super.init(window: .init())
|
||||||
|
visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||||
.map {
|
.map {
|
||||||
CandidateKeyLabel(key: $0, displayedText: $0)
|
CandidateKeyLabel(key: $0, displayedText: $0)
|
||||||
|
|
|
@ -370,7 +370,7 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
private var nextPageButton: NSButton
|
private var nextPageButton: NSButton
|
||||||
private var pageCounterLabel: NSTextField
|
private var pageCounterLabel: NSTextField
|
||||||
private var currentPageIndex: Int = 0
|
private var currentPageIndex: Int = 0
|
||||||
override public var currentLayout: Layout {
|
override public var currentLayout: CandidateLayout {
|
||||||
get { candidateView.isVerticalLayout ? .vertical : .horizontal }
|
get { candidateView.isVerticalLayout ? .vertical : .horizontal }
|
||||||
set {
|
set {
|
||||||
switch newValue {
|
switch newValue {
|
||||||
|
@ -380,7 +380,7 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(_ layout: Layout = .horizontal) {
|
required public init(_ layout: CandidateLayout = .horizontal) {
|
||||||
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
|
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
|
||||||
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
|
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
|
||||||
let panel = NSPanel(
|
let panel = NSPanel(
|
||||||
|
@ -448,7 +448,8 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
|
|
||||||
// MARK: Post-Init()
|
// MARK: Post-Init()
|
||||||
|
|
||||||
super.init(window: panel)
|
super.init(layout)
|
||||||
|
self.window = panel
|
||||||
currentLayout = layout
|
currentLayout = layout
|
||||||
|
|
||||||
candidateView.target = self
|
candidateView.target = self
|
||||||
|
|
Loading…
Reference in New Issue