CtlCandidate(s) // Refactor with partial packaging.
- The CtlCandidateIMK is not package-able due to its utilization of bridging-header. There is no workaround available at this moment.
This commit is contained in:
parent
0041c3d669
commit
e9137b9e53
|
@ -48,7 +48,7 @@ public class NSAttributedTextView: NSView {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var direction: writingDirection = .horizontal
|
public var direction: writingDirection = .horizontal
|
||||||
public var fontSize: CGFloat = NSFont.systemFontSize {
|
public var fontSize: Double = NSFont.systemFontSize {
|
||||||
didSet {
|
didSet {
|
||||||
attributes[.font] = NSFont.systemFont(ofSize: fontSize)
|
attributes[.font] = NSFont.systemFont(ofSize: fontSize)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/config/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
|
@ -0,0 +1,28 @@
|
||||||
|
// swift-tools-version:5.3
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "Voltaire",
|
||||||
|
platforms: [
|
||||||
|
.macOS(.v10_11)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "Voltaire",
|
||||||
|
targets: ["Voltaire"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "../vChewing_CandidateWindow"),
|
||||||
|
.package(path: "../vChewing_Shared"),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "Voltaire",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "Shared", package: "vChewing_Shared"),
|
||||||
|
.product(name: "CandidateWindow", package: "vChewing_CandidateWindow"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Voltaire
|
||||||
|
|
||||||
|
Voltaire is a UI component replacement for Apple's InputMethodKit (IMK). The
|
||||||
|
built-in candidate UI has a limited interaction model and is not very
|
||||||
|
extensible nor customizable.
|
||||||
|
|
||||||
|
The project also comes with a test app that demonstrates the features of the
|
||||||
|
UI component.
|
||||||
|
|
||||||
|
Voltaire MK3 provides following new features comparing to MK1 and MK2:
|
||||||
|
|
||||||
|
1. A brand-new candidate window design conforming to the latest macOS UI design style, plus a floating label indicating the current page number of candidates (a frequently-asked feature by vChewing users).
|
||||||
|
2. One class for both vertical and horizontal display purposes. This can be specified as a parameter on init().
|
||||||
|
|
||||||
|
3. Can specify whether default candidate fonts conform to MOE glyphs standard or continental glyphs standard, requiring macOS 12 Monterey or later.
|
||||||
|
4. Can specify whether page buttons are shown.
|
||||||
|
|
||||||
|
Regarding the horizontal and vertical layout:
|
||||||
|
|
||||||
|
1. It is recommended to use init() in lieu of directly changing the layout variable since the latter doesn't redraw page buttons correctly.
|
||||||
|
2. Vertical candidate mode doesn't support scrolling. This is a deliberated design.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Copyrights:
|
||||||
|
```
|
||||||
|
- (c) 2022 Shiki Suen for all modifications introduced to Voltaire MK3.
|
||||||
|
- (c) 2021 Zonble Yang for rewriting Voltaire MK2 in Swift.
|
||||||
|
- (c) 2012 Lukhnos Liu for Voltaire MK1 development in Objective-C.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
```
|
|
@ -10,6 +10,10 @@
|
||||||
|
|
||||||
// 將之前 Zonble 重寫的 Voltaire 選字窗隔的橫向版本與縱向版本合併到同一個型別實體內。
|
// 將之前 Zonble 重寫的 Voltaire 選字窗隔的橫向版本與縱向版本合併到同一個型別實體內。
|
||||||
|
|
||||||
|
import CandidateWindow
|
||||||
|
import Cocoa
|
||||||
|
import Shared
|
||||||
|
|
||||||
private class vwrCandidateUniversal: NSView {
|
private class vwrCandidateUniversal: NSView {
|
||||||
var highlightedIndex: Int = 0 {
|
var highlightedIndex: Int = 0 {
|
||||||
didSet { highlightedIndex = min(max(highlightedIndex, 0), dispCandidatesWithLabels.count - 1) }
|
didSet { highlightedIndex = min(max(highlightedIndex, 0), dispCandidatesWithLabels.count - 1) }
|
||||||
|
@ -17,22 +21,23 @@ private class vwrCandidateUniversal: NSView {
|
||||||
|
|
||||||
var action: Selector?
|
var action: Selector?
|
||||||
weak var target: AnyObject?
|
weak var target: AnyObject?
|
||||||
|
weak var controller: AnyObject?
|
||||||
var isVerticalLayout = false
|
var isVerticalLayout = false
|
||||||
var fractionFontSize: CGFloat = 12.0
|
var fractionFontSize: Double = 12.0
|
||||||
|
|
||||||
private var keyLabels: [String] = []
|
private var keyLabels: [String] = []
|
||||||
private var displayedCandidates: [String] = []
|
private var displayedCandidates: [String] = []
|
||||||
private var dispCandidatesWithLabels: [String] = []
|
private var dispCandidatesWithLabels: [String] = []
|
||||||
private var keyLabelHeight: CGFloat = 0
|
private var keyLabelHeight: Double = 0
|
||||||
private var keyLabelWidth: CGFloat = 0
|
private var keyLabelWidth: Double = 0
|
||||||
private var candidateTextHeight: CGFloat = 0
|
private var candidateTextHeight: Double = 0
|
||||||
private var cellPadding: CGFloat = 0
|
private var cellPadding: Double = 0
|
||||||
private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||||
private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||||
private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||||
private var windowWidth: CGFloat = 0 // 縱排專用
|
private var windowWidth: Double = 0 // 縱排專用
|
||||||
private var elementWidths: [CGFloat] = []
|
private var elementWidths: [Double] = []
|
||||||
private var elementHeights: [CGFloat] = [] // 縱排專用
|
private var elementHeights: [Double] = [] // 縱排專用
|
||||||
private var trackingHighlightedIndex: Int = .max {
|
private var trackingHighlightedIndex: Int = .max {
|
||||||
didSet { trackingHighlightedIndex = max(trackingHighlightedIndex, 0) }
|
didSet { trackingHighlightedIndex = max(trackingHighlightedIndex, 0) }
|
||||||
}
|
}
|
||||||
|
@ -50,17 +55,17 @@ private class vwrCandidateUniversal: NSView {
|
||||||
result.width = windowWidth
|
result.width = windowWidth
|
||||||
result.height = elementHeights.reduce(0, +)
|
result.height = elementHeights.reduce(0, +)
|
||||||
case false:
|
case false:
|
||||||
result.width = elementWidths.reduce(0, +) + CGFloat(elementWidths.count)
|
result.width = elementWidths.reduce(0, +) + Double(elementWidths.count)
|
||||||
result.height = candidateTextHeight + cellPadding
|
result.height = candidateTextHeight + cellPadding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc(setKeyLabels:displayedCandidates:)
|
|
||||||
func set(keyLabels labels: [String], displayedCandidates candidates: [String]) {
|
func set(keyLabels labels: [String], displayedCandidates candidates: [String]) {
|
||||||
|
guard let delegate = (controller as? CtlCandidateUniversal)?.delegate else { return }
|
||||||
let candidates = candidates.map { theCandidate -> String in
|
let candidates = candidates.map { theCandidate -> String in
|
||||||
let theConverted = ChineseConverter.kanjiConversionIfRequired(theCandidate)
|
let theConverted = delegate.kanjiConversionIfRequired(theCandidate)
|
||||||
return (theCandidate == theConverted) ? theCandidate : "\(theConverted)(\(theCandidate))"
|
return (theCandidate == theConverted) ? theCandidate : "\(theConverted)(\(theCandidate))"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,9 +74,9 @@ private class vwrCandidateUniversal: NSView {
|
||||||
displayedCandidates = Array(candidates[0..<count])
|
displayedCandidates = Array(candidates[0..<count])
|
||||||
dispCandidatesWithLabels = zip(keyLabels, displayedCandidates).map { $0 + $1 }
|
dispCandidatesWithLabels = zip(keyLabels, displayedCandidates).map { $0 + $1 }
|
||||||
|
|
||||||
var newWidths = [CGFloat]()
|
var newWidths = [Double]()
|
||||||
var calculatedWindowWidth = CGFloat()
|
var calculatedWindowWidth = Double()
|
||||||
var newHeights = [CGFloat]()
|
var newHeights = [Double]()
|
||||||
let baseSize = NSSize(width: 10240.0, height: 10240.0)
|
let baseSize = NSSize(width: 10240.0, height: 10240.0)
|
||||||
for index in 0..<count {
|
for index in 0..<count {
|
||||||
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
|
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
|
||||||
|
@ -133,55 +138,15 @@ private class vwrCandidateUniversal: NSView {
|
||||||
cellPadding = ceil(biggestSize / 4.0) * 2
|
cellPadding = ceil(biggestSize / 4.0) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureLangIdentifier(for attr: inout [NSAttributedString.Key: AnyObject]) {
|
|
||||||
if PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier {
|
|
||||||
switch IMEApp.currentInputMode {
|
|
||||||
case .imeModeCHS:
|
|
||||||
if #available(macOS 12.0, *) {
|
|
||||||
attr[.languageIdentifier] = "zh-Hans" as AnyObject
|
|
||||||
}
|
|
||||||
case .imeModeCHT:
|
|
||||||
if #available(macOS 12.0, *) {
|
|
||||||
attr[.languageIdentifier] =
|
|
||||||
(PrefMgr.shared.shiftJISShinjitaiOutputEnabled || PrefMgr.shared.chineseConversionEnabled)
|
|
||||||
? "ja" as AnyObject : "zh-Hant" as AnyObject
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var highlightedColor: NSColor {
|
|
||||||
var result = NSColor.alternateSelectedControlColor
|
|
||||||
var colorBlendAmount: CGFloat = NSApplication.isDarkMode ? 0.3 : 0.0
|
|
||||||
if #available(macOS 10.14, *), !NSApplication.isDarkMode, IMEApp.currentInputMode == .imeModeCHT {
|
|
||||||
colorBlendAmount = 0.15
|
|
||||||
}
|
|
||||||
// The background color of the highlightened candidate
|
|
||||||
switch IMEApp.currentInputMode {
|
|
||||||
case .imeModeCHS:
|
|
||||||
result = NSColor.systemRed
|
|
||||||
case .imeModeCHT:
|
|
||||||
result = NSColor.systemBlue
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
var blendingAgainstTarget: NSColor = NSApplication.isDarkMode ? NSColor.black : NSColor.white
|
|
||||||
if #unavailable(macOS 10.14) {
|
|
||||||
colorBlendAmount = 0.3
|
|
||||||
blendingAgainstTarget = NSColor.white
|
|
||||||
}
|
|
||||||
return result.blended(withFraction: colorBlendAmount, of: blendingAgainstTarget)!
|
|
||||||
}
|
|
||||||
|
|
||||||
override func draw(_: NSRect) {
|
override func draw(_: NSRect) {
|
||||||
|
guard let controller = controller as? CtlCandidateUniversal else { return }
|
||||||
let bounds = bounds
|
let bounds = bounds
|
||||||
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
|
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
|
||||||
NSBezierPath.fill(bounds)
|
NSBezierPath.fill(bounds)
|
||||||
|
|
||||||
switch isVerticalLayout {
|
switch isVerticalLayout {
|
||||||
case true:
|
case true:
|
||||||
var accuHeight: CGFloat = 0
|
var accuHeight: Double = 0
|
||||||
for (index, elementHeight) in elementHeights.enumerated() {
|
for (index, elementHeight) in elementHeights.enumerated() {
|
||||||
let currentHeight = elementHeight
|
let currentHeight = elementHeight
|
||||||
let rctCandidateArea = NSRect(
|
let rctCandidateArea = NSRect(
|
||||||
|
@ -200,7 +165,7 @@ private class vwrCandidateUniversal: NSView {
|
||||||
var activeCandidateIndexAttr = keyLabelAttrDict
|
var activeCandidateIndexAttr = keyLabelAttrDict
|
||||||
var activeCandidateAttr = candidateAttrDict
|
var activeCandidateAttr = candidateAttrDict
|
||||||
if index == highlightedIndex {
|
if index == highlightedIndex {
|
||||||
highlightedColor.setFill()
|
controller.highlightedColor().setFill()
|
||||||
// Highlightened index text color
|
// Highlightened index text color
|
||||||
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||||
.withAlphaComponent(0.84)
|
.withAlphaComponent(0.84)
|
||||||
|
@ -209,7 +174,11 @@ private class vwrCandidateUniversal: NSView {
|
||||||
let path: NSBezierPath = .init(roundedRect: rctCandidateArea, xRadius: 6, yRadius: 6)
|
let path: NSBezierPath = .init(roundedRect: rctCandidateArea, xRadius: 6, yRadius: 6)
|
||||||
path.fill()
|
path.fill()
|
||||||
}
|
}
|
||||||
ensureLangIdentifier(for: &activeCandidateAttr)
|
if #available(macOS 12, *) {
|
||||||
|
if controller.useLangIdentifier {
|
||||||
|
activeCandidateAttr[.languageIdentifier] = controller.locale as AnyObject
|
||||||
|
}
|
||||||
|
}
|
||||||
(keyLabels[index] as NSString).draw(
|
(keyLabels[index] as NSString).draw(
|
||||||
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
||||||
)
|
)
|
||||||
|
@ -219,7 +188,7 @@ private class vwrCandidateUniversal: NSView {
|
||||||
accuHeight += currentHeight
|
accuHeight += currentHeight
|
||||||
}
|
}
|
||||||
case false:
|
case false:
|
||||||
var accuWidth: CGFloat = 0
|
var accuWidth: Double = 0
|
||||||
for (index, elementWidth) in elementWidths.enumerated() {
|
for (index, elementWidth) in elementWidths.enumerated() {
|
||||||
let currentWidth = elementWidth
|
let currentWidth = elementWidth
|
||||||
let rctCandidateArea = NSRect(
|
let rctCandidateArea = NSRect(
|
||||||
|
@ -239,7 +208,7 @@ private class vwrCandidateUniversal: NSView {
|
||||||
var activeCandidateIndexAttr = keyLabelAttrDict
|
var activeCandidateIndexAttr = keyLabelAttrDict
|
||||||
var activeCandidateAttr = candidateAttrDict
|
var activeCandidateAttr = candidateAttrDict
|
||||||
if index == highlightedIndex {
|
if index == highlightedIndex {
|
||||||
highlightedColor.setFill()
|
controller.highlightedColor().setFill()
|
||||||
// Highlightened index text color
|
// Highlightened index text color
|
||||||
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||||
.withAlphaComponent(0.84)
|
.withAlphaComponent(0.84)
|
||||||
|
@ -248,7 +217,11 @@ private class vwrCandidateUniversal: NSView {
|
||||||
let path: NSBezierPath = .init(roundedRect: rctCandidateArea, xRadius: 6, yRadius: 6)
|
let path: NSBezierPath = .init(roundedRect: rctCandidateArea, xRadius: 6, yRadius: 6)
|
||||||
path.fill()
|
path.fill()
|
||||||
}
|
}
|
||||||
ensureLangIdentifier(for: &activeCandidateAttr)
|
if #available(macOS 12, *) {
|
||||||
|
if controller.useLangIdentifier {
|
||||||
|
activeCandidateAttr[.languageIdentifier] = controller.locale as AnyObject
|
||||||
|
}
|
||||||
|
}
|
||||||
(keyLabels[index] as NSString).draw(
|
(keyLabels[index] as NSString).draw(
|
||||||
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
||||||
)
|
)
|
||||||
|
@ -267,7 +240,7 @@ private class vwrCandidateUniversal: NSView {
|
||||||
}
|
}
|
||||||
switch isVerticalLayout {
|
switch isVerticalLayout {
|
||||||
case true:
|
case true:
|
||||||
var accuHeight: CGFloat = 0.0
|
var accuHeight = 0.0
|
||||||
for (index, elementHeight) in elementHeights.enumerated() {
|
for (index, elementHeight) in elementHeights.enumerated() {
|
||||||
let currentHeight = elementHeight
|
let currentHeight = elementHeight
|
||||||
|
|
||||||
|
@ -277,7 +250,7 @@ private class vwrCandidateUniversal: NSView {
|
||||||
accuHeight += currentHeight
|
accuHeight += currentHeight
|
||||||
}
|
}
|
||||||
case false:
|
case false:
|
||||||
var accuWidth: CGFloat = 0.0
|
var accuWidth = 0.0
|
||||||
for (index, elementWidth) in elementWidths.enumerated() {
|
for (index, elementWidth) in elementWidths.enumerated() {
|
||||||
let currentWidth = elementWidth
|
let currentWidth = elementWidth
|
||||||
|
|
||||||
|
@ -322,7 +295,7 @@ private class vwrCandidateUniversal: NSView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ctlCandidateUniversal: ctlCandidate {
|
public class CtlCandidateUniversal: CtlCandidate {
|
||||||
private var candidateView: vwrCandidateUniversal
|
private var candidateView: vwrCandidateUniversal
|
||||||
private var prevPageButton: NSButton
|
private var prevPageButton: NSButton
|
||||||
private var nextPageButton: NSButton
|
private var nextPageButton: NSButton
|
||||||
|
@ -408,6 +381,7 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
super.init(layout)
|
super.init(layout)
|
||||||
window = panel
|
window = panel
|
||||||
currentLayout = layout
|
currentLayout = layout
|
||||||
|
candidateView.controller = self
|
||||||
|
|
||||||
candidateView.target = self
|
candidateView.target = self
|
||||||
candidateView.action = #selector(candidateViewMouseDidClick(_:))
|
candidateView.action = #selector(candidateViewMouseDidClick(_:))
|
||||||
|
@ -431,9 +405,9 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult override public func showNextPage() -> Bool {
|
@discardableResult override public func showNextPage() -> Bool {
|
||||||
guard delegate != nil else { return false }
|
guard let delegate = delegate else { return false }
|
||||||
if pageCount == 1 { return highlightNextCandidate() }
|
if pageCount == 1 { return highlightNextCandidate() }
|
||||||
if currentPageIndex + 1 >= pageCount { IMEApp.buzz() }
|
if currentPageIndex + 1 >= pageCount { delegate.buzz() }
|
||||||
currentPageIndex = (currentPageIndex + 1 >= pageCount) ? 0 : currentPageIndex + 1
|
currentPageIndex = (currentPageIndex + 1 >= pageCount) ? 0 : currentPageIndex + 1
|
||||||
if currentPageIndex == pageCount - 1 {
|
if currentPageIndex == pageCount - 1 {
|
||||||
candidateView.highlightedIndex = min(lastPageContentCount - 1, candidateView.highlightedIndex)
|
candidateView.highlightedIndex = min(lastPageContentCount - 1, candidateView.highlightedIndex)
|
||||||
|
@ -444,9 +418,9 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult override public func showPreviousPage() -> Bool {
|
@discardableResult override public func showPreviousPage() -> Bool {
|
||||||
guard delegate != nil else { return false }
|
guard let delegate = delegate else { return false }
|
||||||
if pageCount == 1 { return highlightPreviousCandidate() }
|
if pageCount == 1 { return highlightPreviousCandidate() }
|
||||||
if currentPageIndex == 0 { IMEApp.buzz() }
|
if currentPageIndex == 0 { delegate.buzz() }
|
||||||
currentPageIndex = (currentPageIndex == 0) ? pageCount - 1 : currentPageIndex - 1
|
currentPageIndex = (currentPageIndex == 0) ? pageCount - 1 : currentPageIndex - 1
|
||||||
if currentPageIndex == pageCount - 1 {
|
if currentPageIndex == pageCount - 1 {
|
||||||
candidateView.highlightedIndex = min(lastPageContentCount - 1, candidateView.highlightedIndex)
|
candidateView.highlightedIndex = min(lastPageContentCount - 1, candidateView.highlightedIndex)
|
||||||
|
@ -499,7 +473,7 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ctlCandidateUniversal {
|
extension CtlCandidateUniversal {
|
||||||
private var pageCount: Int {
|
private var pageCount: Int {
|
||||||
guard let delegate = delegate else {
|
guard let delegate = delegate else {
|
||||||
return 0
|
return 0
|
||||||
|
@ -519,9 +493,7 @@ extension ctlCandidateUniversal {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func layoutCandidateView() {
|
private func layoutCandidateView() {
|
||||||
guard let delegate = delegate else {
|
guard let delegate = delegate, let window = window else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont)
|
candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont)
|
||||||
var candidates = [(String, String)]()
|
var candidates = [(String, String)]()
|
||||||
|
@ -540,14 +512,14 @@ extension ctlCandidateUniversal {
|
||||||
var frameRect = candidateView.frame
|
var frameRect = candidateView.frame
|
||||||
frameRect.size = newSize
|
frameRect.size = newSize
|
||||||
candidateView.frame = frameRect
|
candidateView.frame = frameRect
|
||||||
let counterHeight: CGFloat = newSize.height - 24
|
let counterHeight: Double = newSize.height - 24.0
|
||||||
|
|
||||||
if pageCount > 1, PrefMgr.shared.showPageButtonsInCandidateWindow {
|
if pageCount > 1, showPageButtons {
|
||||||
var buttonRect = nextPageButton.frame
|
var buttonRect = nextPageButton.frame
|
||||||
let spacing: CGFloat = 0.0
|
let spacing = 0.0
|
||||||
|
|
||||||
if currentLayout == .horizontal { buttonRect.size.height = floor(newSize.height / 2) }
|
if currentLayout == .horizontal { buttonRect.size.height = floor(newSize.height / 2) }
|
||||||
let buttonOriginY: CGFloat = {
|
let buttonOriginY: Double = {
|
||||||
if currentLayout == .vertical {
|
if currentLayout == .vertical {
|
||||||
return counterHeight
|
return counterHeight
|
||||||
}
|
}
|
||||||
|
@ -582,12 +554,12 @@ extension ctlCandidateUniversal {
|
||||||
|
|
||||||
rect.size.height += 3
|
rect.size.height += 3
|
||||||
rect.size.width += 4
|
rect.size.width += 4
|
||||||
let rectOriginY: CGFloat =
|
let rectOriginY: Double =
|
||||||
(currentLayout == .horizontal)
|
(currentLayout == .horizontal)
|
||||||
? (newSize.height - rect.height) / 2
|
? (newSize.height - rect.height) / 2
|
||||||
: counterHeight
|
: counterHeight
|
||||||
let rectOriginX: CGFloat =
|
let rectOriginX: Double =
|
||||||
PrefMgr.shared.showPageButtonsInCandidateWindow
|
showPageButtons
|
||||||
? newSize.width
|
? newSize.width
|
||||||
: newSize.width + 4
|
: newSize.width + 4
|
||||||
rect.origin = NSPoint(x: rectOriginX, y: rectOriginY)
|
rect.origin = NSPoint(x: rectOriginX, y: rectOriginY)
|
||||||
|
@ -598,12 +570,12 @@ extension ctlCandidateUniversal {
|
||||||
pageCounterLabel.isHidden = true
|
pageCounterLabel.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
frameRect = window?.frame ?? NSRect.seniorTheBeast
|
frameRect = window.frame
|
||||||
|
|
||||||
let topLeftPoint = NSPoint(x: frameRect.origin.x, y: frameRect.origin.y + frameRect.size.height)
|
let topLeftPoint = NSPoint(x: frameRect.origin.x, y: frameRect.origin.y + frameRect.size.height)
|
||||||
frameRect.size = newSize
|
frameRect.size = newSize
|
||||||
frameRect.origin = NSPoint(x: topLeftPoint.x, y: topLeftPoint.y - frameRect.size.height)
|
frameRect.origin = NSPoint(x: topLeftPoint.x, y: topLeftPoint.y - frameRect.size.height)
|
||||||
window?.setFrame(frameRect, display: false)
|
window.setFrame(frameRect, display: false)
|
||||||
candidateView.setNeedsDisplay(candidateView.bounds)
|
candidateView.setNeedsDisplay(candidateView.bounds)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ extension Preferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modifier(Section.LabelWidthModifier(maximumWidth: $maximumLabelWidth))
|
.modifier(Section.LabelWidthModifier(maximumWidth: $maximumLabelWidth))
|
||||||
.frame(width: CGFloat(contentWidth), alignment: .leading)
|
.frame(width: Double(contentWidth), alignment: .leading)
|
||||||
.padding(.vertical, 20)
|
.padding(.vertical, 20)
|
||||||
.padding(.horizontal, 30)
|
.padding(.horizontal, 30)
|
||||||
}
|
}
|
||||||
|
@ -65,9 +65,9 @@ extension Preferences {
|
||||||
if index != sections.count - 1, sections[index].bottomDivider {
|
if index != sections.count - 1, sections[index].bottomDivider {
|
||||||
Divider()
|
Divider()
|
||||||
// Strangely doesn't work without width being specified. Probably because of custom alignment.
|
// Strangely doesn't work without width being specified. Probably because of custom alignment.
|
||||||
.frame(width: CGFloat(contentWidth), height: 20)
|
.frame(width: Double(contentWidth), height: 20)
|
||||||
.alignmentGuide(.preferenceSectionLabel) {
|
.alignmentGuide(.preferenceSectionLabel) {
|
||||||
$0[.leading] + CGFloat(max(minimumLabelWidth, maximumLabelWidth))
|
$0[.leading] + Double(max(minimumLabelWidth, maximumLabelWidth))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,8 @@ final class SegmentedControlStyleViewController: NSViewController, PreferencesSt
|
||||||
)
|
)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let segmentBorderWidth = CGFloat(preferencePanes.count) + 1
|
let segmentBorderWidth = Double(preferencePanes.count) + 1
|
||||||
let segmentWidth = segmentSize.width * CGFloat(preferencePanes.count) + segmentBorderWidth
|
let segmentWidth = segmentSize.width * Double(preferencePanes.count) + segmentBorderWidth
|
||||||
let segmentHeight = segmentSize.height
|
let segmentHeight = segmentSize.height
|
||||||
segmentedControl.frame = CGRect(x: 0, y: 0, width: segmentWidth, height: segmentHeight)
|
segmentedControl.frame = CGRect(x: 0, y: 0, width: segmentWidth, height: segmentHeight)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/config/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
|
@ -0,0 +1,26 @@
|
||||||
|
// swift-tools-version:5.3
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "CandidateWindow",
|
||||||
|
platforms: [
|
||||||
|
.macOS(.v10_11)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "CandidateWindow",
|
||||||
|
targets: ["CandidateWindow"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "../vChewing_Shared")
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "CandidateWindow",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "Shared", package: "vChewing_Shared")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
# CandidateWindow
|
||||||
|
|
||||||
|
用以定義與威注音的選字窗有關的基礎內容,目前尚未完工。
|
||||||
|
|
||||||
|
```
|
||||||
|
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
```
|
|
@ -0,0 +1,147 @@
|
||||||
|
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import Shared
|
||||||
|
|
||||||
|
open class CtlCandidate: NSWindowController, CtlCandidateProtocol {
|
||||||
|
open var showPageButtons: Bool = false
|
||||||
|
open var currentLayout: CandidateLayout = .horizontal
|
||||||
|
open var locale: String = ""
|
||||||
|
open var useLangIdentifier: Bool = false
|
||||||
|
|
||||||
|
open func highlightedColor() -> NSColor {
|
||||||
|
var result = NSColor.alternateSelectedControlColor
|
||||||
|
var colorBlendAmount: Double = NSApplication.isDarkMode ? 0.3 : 0.0
|
||||||
|
if #available(macOS 10.14, *), !NSApplication.isDarkMode, locale == "zh-Hant" {
|
||||||
|
colorBlendAmount = 0.15
|
||||||
|
}
|
||||||
|
// The background color of the highlightened candidate
|
||||||
|
switch locale {
|
||||||
|
case "zh-Hans":
|
||||||
|
result = NSColor.systemRed
|
||||||
|
case "zh-Hant":
|
||||||
|
result = NSColor.systemBlue
|
||||||
|
case "ja":
|
||||||
|
result = NSColor.systemBrown
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
var blendingAgainstTarget: NSColor = NSApplication.isDarkMode ? NSColor.black : NSColor.white
|
||||||
|
if #unavailable(macOS 10.14) {
|
||||||
|
colorBlendAmount = 0.3
|
||||||
|
blendingAgainstTarget = NSColor.white
|
||||||
|
}
|
||||||
|
return result.blended(withFraction: colorBlendAmount, of: blendingAgainstTarget)!
|
||||||
|
}
|
||||||
|
|
||||||
|
open weak var delegate: CtlCandidateDelegate? {
|
||||||
|
didSet {
|
||||||
|
reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var windowTopLeftPoint: NSPoint {
|
||||||
|
get {
|
||||||
|
guard let frameRect = window?.frame else { return NSPoint.zero }
|
||||||
|
return NSPoint(x: frameRect.minX, y: frameRect.maxY)
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.set(windowTopLeftPoint: newValue, bottomOutOfScreenAdjustmentHeight: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var selectedCandidateIndex: Int = .max
|
||||||
|
open var visible = false {
|
||||||
|
didSet {
|
||||||
|
NSObject.cancelPreviousPerformRequests(withTarget: self)
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
_ = visible ? window?.orderFront(self) : window?.orderOut(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init(_: CandidateLayout = .horizontal) {
|
||||||
|
super.init(window: .init())
|
||||||
|
visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the location of the candidate window.
|
||||||
|
///
|
||||||
|
/// Please note that the method has side effects that modifies
|
||||||
|
/// `windowTopLeftPoint` to make the candidate window to stay in at least
|
||||||
|
/// in a screen.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - windowTopLeftPoint: The given location.
|
||||||
|
/// - height: The height that helps the window not to be out of the bottom
|
||||||
|
/// of a screen.
|
||||||
|
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double) {
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
guard let window = window, var screenFrame = NSScreen.main?.visibleFrame else { return }
|
||||||
|
let windowSize = window.frame.size
|
||||||
|
|
||||||
|
var adjustedPoint = windowTopLeftPoint
|
||||||
|
var delta = heightDelta
|
||||||
|
for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) {
|
||||||
|
screenFrame = frame
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta > screenFrame.size.height / 2.0 { delta = 0.0 }
|
||||||
|
|
||||||
|
if adjustedPoint.y < screenFrame.minY + windowSize.height {
|
||||||
|
adjustedPoint.y = windowTopLeftPoint.y + windowSize.height + delta
|
||||||
|
}
|
||||||
|
adjustedPoint.y = min(adjustedPoint.y, screenFrame.maxY - 1.0)
|
||||||
|
adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width - 1.0)
|
||||||
|
|
||||||
|
window.setFrameTopLeftPoint(adjustedPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Contents that are not needed to be implemented here.
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
public required init?(coder _: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
open var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||||
|
.map {
|
||||||
|
CandidateKeyLabel(key: $0, displayedText: $0)
|
||||||
|
}
|
||||||
|
|
||||||
|
open var candidateFont = NSFont.systemFont(ofSize: 18)
|
||||||
|
open var keyLabelFont = NSFont.monospacedDigitSystemFont(ofSize: 14, weight: .medium)
|
||||||
|
|
||||||
|
open var tooltip: String = ""
|
||||||
|
|
||||||
|
@discardableResult open func highlightNextCandidate() -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult open func highlightPreviousCandidate() -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult open func showNextPage() -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult open func showPreviousPage() -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
open func candidateIndexAtKeyLabelIndex(_: Int) -> Int {
|
||||||
|
Int.max
|
||||||
|
}
|
||||||
|
|
||||||
|
open func reloadData() {}
|
||||||
|
}
|
|
@ -168,7 +168,7 @@ public class PopupCompositionBuffer: NSWindowController {
|
||||||
with: NSSize(width: 1600.0, height: 1600.0),
|
with: NSSize(width: 1600.0, height: 1600.0),
|
||||||
options: [.usesLineFragmentOrigin, .usesFontLeading]
|
options: [.usesLineFragmentOrigin, .usesFontLeading]
|
||||||
)
|
)
|
||||||
rect.size.width = max(rect.size.width, 20 * CGFloat(attrString.string.count)) + 2
|
rect.size.width = max(rect.size.width, 20 * Double(attrString.string.count)) + 2
|
||||||
rect.size.height *= 1.2
|
rect.size.height *= 1.2
|
||||||
rect.size.height = max(22, rect.size.height)
|
rect.size.height = max(22, rect.size.height)
|
||||||
if isTypingDirectionVertical {
|
if isTypingDirectionVertical {
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
public protocol CtlCandidateDelegate: AnyObject {
|
||||||
|
func candidateCountForController(_ controller: CtlCandidateProtocol) -> Int
|
||||||
|
func candidatesForController(_ controller: CtlCandidateProtocol) -> [(String, String)]
|
||||||
|
func ctlCandidate(_ controller: CtlCandidateProtocol, candidateAtIndex index: Int)
|
||||||
|
-> (String, String)
|
||||||
|
func candidateSelected(at index: Int)
|
||||||
|
func buzz()
|
||||||
|
func kanjiConversionIfRequired(_ target: String) -> String
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol CtlCandidateProtocol {
|
||||||
|
var locale: String { get set }
|
||||||
|
var currentLayout: CandidateLayout { get set }
|
||||||
|
var delegate: CtlCandidateDelegate? { get set }
|
||||||
|
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 }
|
||||||
|
var useLangIdentifier: Bool { get set }
|
||||||
|
var showPageButtons: Bool { get set }
|
||||||
|
|
||||||
|
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: Double)
|
||||||
|
}
|
|
@ -103,6 +103,23 @@ public enum UserDef: String, CaseIterable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Enums and Structs used by Candidate Window
|
||||||
|
|
||||||
|
public enum CandidateLayout {
|
||||||
|
case horizontal
|
||||||
|
case vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CandidateKeyLabel {
|
||||||
|
public private(set) var key: String
|
||||||
|
public private(set) var displayedText: String
|
||||||
|
|
||||||
|
public init(key: String, displayedText: String) {
|
||||||
|
self.key = key
|
||||||
|
self.displayedText = displayedText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Tooltip Color States
|
// MARK: - Tooltip Color States
|
||||||
|
|
||||||
public enum TooltipColorState {
|
public enum TooltipColorState {
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class TooltipUI: NSWindowController {
|
||||||
|
|
||||||
public func show(
|
public func show(
|
||||||
tooltip: String = "", at point: NSPoint,
|
tooltip: String = "", at point: NSPoint,
|
||||||
bottomOutOfScreenAdjustmentHeight heightDelta: CGFloat,
|
bottomOutOfScreenAdjustmentHeight heightDelta: Double,
|
||||||
direction: NSAttributedTooltipTextView.writingDirection = .horizontal
|
direction: NSAttributedTooltipTextView.writingDirection = .horizontal
|
||||||
) {
|
) {
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
|
@ -118,7 +118,7 @@ public class TooltipUI: NSWindowController {
|
||||||
window?.orderOut(nil)
|
window?.orderOut(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: CGFloat) {
|
private func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double) {
|
||||||
guard let window = window else { return }
|
guard let window = window else { return }
|
||||||
let windowSize = window.frame.size
|
let windowSize = window.frame.size
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ public enum ChineseConverter {
|
||||||
///
|
///
|
||||||
/// - Parameter string: Text in Original Script.
|
/// - Parameter string: Text in Original Script.
|
||||||
/// - Returns: Text converted to Different Script.
|
/// - Returns: Text converted to Different Script.
|
||||||
public static func crossConvert(_ string: String) -> String? {
|
public static func crossConvert(_ string: String) -> String {
|
||||||
switch IMEApp.currentInputMode {
|
switch IMEApp.currentInputMode {
|
||||||
case .imeModeCHS:
|
case .imeModeCHS:
|
||||||
return shared.convert(string, to: .zhHantTW)
|
return shared.convert(string, to: .zhHantTW)
|
||||||
|
|
|
@ -208,7 +208,7 @@ extension StateData {
|
||||||
|
|
||||||
public var userPhraseDumpedConverted: String {
|
public var userPhraseDumpedConverted: String {
|
||||||
let pair = userPhraseKVPair
|
let pair = userPhraseKVPair
|
||||||
let text = ChineseConverter.crossConvert(pair.1) ?? ""
|
let text = ChineseConverter.crossConvert(pair.1)
|
||||||
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
|
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
|
||||||
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
|
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
|
||||||
return "\(text) \(pair.0)\(nerfedScore)\t\(convertedMark)"
|
return "\(text) \(pair.0)\(nerfedScore)\t\(convertedMark)"
|
||||||
|
|
|
@ -20,7 +20,7 @@ import Tekkon
|
||||||
/// KeyHandler 委任協定
|
/// KeyHandler 委任協定
|
||||||
public protocol KeyHandlerDelegate {
|
public protocol KeyHandlerDelegate {
|
||||||
var clientBundleIdentifier: String { get }
|
var clientBundleIdentifier: String { get }
|
||||||
func ctlCandidate() -> ctlCandidateProtocol
|
func ctlCandidate() -> CtlCandidateProtocol
|
||||||
func candidateSelectionCalledByKeyHandler(at index: Int)
|
func candidateSelectionCalledByKeyHandler(at index: Int)
|
||||||
func performUserPhraseOperation(with state: IMEStateProtocol, addToFilter: Bool)
|
func performUserPhraseOperation(with state: IMEStateProtocol, addToFilter: Bool)
|
||||||
-> Bool
|
-> Bool
|
||||||
|
|
|
@ -6,12 +6,19 @@
|
||||||
// marks, or product names of Contributor, except as required to fulfill notice
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
// requirements defined in MIT License.
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
|
import CandidateWindow
|
||||||
|
import Shared
|
||||||
|
|
||||||
|
/// 威注音自用的 IMKCandidates 型別。因為有用到 bridging header,所以無法弄成 Swift Package。
|
||||||
|
public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol {
|
||||||
|
public var showPageButtons: Bool = false
|
||||||
|
public var locale: String = ""
|
||||||
|
public var useLangIdentifier: Bool = false
|
||||||
public var currentLayout: CandidateLayout = .horizontal
|
public var currentLayout: CandidateLayout = .horizontal
|
||||||
public static let defaultIMKSelectionKey: [UInt16: String] = [
|
public static let defaultIMKSelectionKey: [UInt16: String] = [
|
||||||
18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6", 26: "7", 28: "8", 25: "9",
|
18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6", 26: "7", 28: "8", 25: "9",
|
||||||
]
|
]
|
||||||
public weak var delegate: ctlCandidateDelegate? {
|
public weak var delegate: CtlCandidateDelegate? {
|
||||||
didSet {
|
didSet {
|
||||||
reloadData()
|
reloadData()
|
||||||
}
|
}
|
||||||
|
@ -40,26 +47,15 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
|
||||||
ofSize: 14, weight: .medium
|
ofSize: 14, weight: .medium
|
||||||
)
|
)
|
||||||
|
|
||||||
public var candidateFont = NSFont.systemFont(ofSize: PrefMgr.shared.candidateListTextSize) {
|
public var candidateFont = NSFont.systemFont(ofSize: 16) {
|
||||||
didSet {
|
didSet {
|
||||||
if #available(macOS 10.14, *) { setFontSize(candidateFont.pointSize) }
|
if #available(macOS 10.14, *) { setFontSize(candidateFont.pointSize) }
|
||||||
var attributes = attributes()
|
var attributes = attributes()
|
||||||
// FB11300759: Set "NSAttributedString.Key.font" doesn't work.
|
// FB11300759: Set "NSAttributedString.Key.font" doesn't work.
|
||||||
attributes?[NSAttributedString.Key.font] = candidateFont
|
attributes?[NSAttributedString.Key.font] = candidateFont
|
||||||
if PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier {
|
if #available(macOS 12.0, *) {
|
||||||
switch IMEApp.currentInputMode {
|
if useLangIdentifier {
|
||||||
case .imeModeCHS:
|
attributes?[NSAttributedString.Key.languageIdentifier] = locale as AnyObject
|
||||||
if #available(macOS 12.0, *) {
|
|
||||||
attributes?[NSAttributedString.Key.languageIdentifier] = "zh-Hans" as AnyObject
|
|
||||||
}
|
|
||||||
case .imeModeCHT:
|
|
||||||
if #available(macOS 12.0, *) {
|
|
||||||
attributes?[NSAttributedString.Key.languageIdentifier] =
|
|
||||||
(PrefMgr.shared.shiftJISShinjitaiOutputEnabled || PrefMgr.shared.chineseConversionEnabled)
|
|
||||||
? "ja" as AnyObject : "zh-Hant" as AnyObject
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setAttributes(attributes)
|
setAttributes(attributes)
|
||||||
|
@ -159,17 +155,17 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
|
||||||
set { selectCandidate(withIdentifier: newValue) }
|
set { selectCandidate(withIdentifier: newValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) {
|
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: Double) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.doSet(windowTopLeftPoint: windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: height)
|
self.doSet(windowTopLeftPoint: windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doSet(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: CGFloat) {
|
func doSet(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double) {
|
||||||
|
guard var screenFrame = NSScreen.main?.visibleFrame else { return }
|
||||||
var adjustedPoint = windowTopLeftPoint
|
var adjustedPoint = windowTopLeftPoint
|
||||||
let windowSize = candidateFrame().size
|
let windowSize = candidateFrame().size
|
||||||
var delta = heightDelta
|
var delta = heightDelta
|
||||||
var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast
|
|
||||||
for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) {
|
for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) {
|
||||||
screenFrame = frame
|
screenFrame = frame
|
||||||
break
|
break
|
||||||
|
@ -209,7 +205,7 @@ var currentTISInputSource: TISInputSource? {
|
||||||
|
|
||||||
// MARK: - Translating NumPad KeyCodes to Default IMK Candidate Selection KeyCodes.
|
// MARK: - Translating NumPad KeyCodes to Default IMK Candidate Selection KeyCodes.
|
||||||
|
|
||||||
extension ctlCandidateIMK {
|
extension CtlCandidateIMK {
|
||||||
public static func replaceNumPadKeyCodes(target event: NSEvent) -> NSEvent? {
|
public static func replaceNumPadKeyCodes(target event: NSEvent) -> NSEvent? {
|
||||||
let mapNumPadKeyCodeTranslation: [UInt16: UInt16] = [
|
let mapNumPadKeyCodeTranslation: [UInt16: UInt16] = [
|
||||||
83: 18, 84: 19, 85: 20, 86: 21, 87: 23, 88: 22, 89: 26, 91: 28, 92: 25,
|
83: 18, 84: 19, 85: 20, 86: 21, 87: 23, 88: 22, 89: 26, 91: 28, 92: 25,
|
|
@ -1,173 +0,0 @@
|
||||||
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
|
|
||||||
// All possible vChewing-specific modifications are of:
|
|
||||||
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
|
||||||
// ====================
|
|
||||||
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
|
||||||
// ... with NTL restriction stating that:
|
|
||||||
// No trademark license is granted to use the trade names, trademarks, service
|
|
||||||
// marks, or product names of Contributor, except as required to fulfill notice
|
|
||||||
// requirements defined in MIT License.
|
|
||||||
|
|
||||||
public enum CandidateLayout {
|
|
||||||
case horizontal
|
|
||||||
case vertical
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CandidateKeyLabel: NSObject {
|
|
||||||
public private(set) var key: String
|
|
||||||
public private(set) var displayedText: String
|
|
||||||
|
|
||||||
public init(key: String, displayedText: String) {
|
|
||||||
self.key = key
|
|
||||||
self.displayedText = displayedText
|
|
||||||
super.init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol ctlCandidateDelegate: AnyObject {
|
|
||||||
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int
|
|
||||||
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)]
|
|
||||||
func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)
|
|
||||||
-> (String, String)
|
|
||||||
func candidateSelected(at index: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol ctlCandidateProtocol {
|
|
||||||
var currentLayout: CandidateLayout { get set }
|
|
||||||
var delegate: ctlCandidateDelegate? { get set }
|
|
||||||
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 }
|
|
||||||
|
|
||||||
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? {
|
|
||||||
didSet {
|
|
||||||
reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var selectedCandidateIndex: Int = .max
|
|
||||||
public var visible = false {
|
|
||||||
didSet {
|
|
||||||
NSObject.cancelPreviousPerformRequests(withTarget: self)
|
|
||||||
if visible {
|
|
||||||
window?.perform(#selector(NSWindow.orderFront(_:)), with: self, afterDelay: 0.0)
|
|
||||||
} else {
|
|
||||||
window?.perform(#selector(NSWindow.orderOut(_:)), with: self, afterDelay: 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var windowTopLeftPoint: NSPoint {
|
|
||||||
get {
|
|
||||||
guard let frameRect = window?.frame else {
|
|
||||||
return NSPoint.zero
|
|
||||||
}
|
|
||||||
return NSPoint(x: frameRect.minX, y: frameRect.maxY)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.set(windowTopLeftPoint: newValue, bottomOutOfScreenAdjustmentHeight: 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public required init(_: CandidateLayout = .horizontal) {
|
|
||||||
super.init(window: .init())
|
|
||||||
visible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(*, unavailable)
|
|
||||||
required init?(coder _: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
|
||||||
.map {
|
|
||||||
CandidateKeyLabel(key: $0, displayedText: $0)
|
|
||||||
}
|
|
||||||
|
|
||||||
public var keyLabelFont = NSFont.monospacedDigitSystemFont(
|
|
||||||
ofSize: 14, weight: .medium
|
|
||||||
)
|
|
||||||
public var candidateFont = NSFont.systemFont(ofSize: 18)
|
|
||||||
public var tooltip: String = ""
|
|
||||||
|
|
||||||
public func reloadData() {}
|
|
||||||
|
|
||||||
@discardableResult public func showNextPage() -> Bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult public func showPreviousPage() -> Bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult public func highlightNextCandidate() -> Bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult public func highlightPreviousCandidate() -> Bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
public func candidateIndexAtKeyLabelIndex(_: Int) -> Int {
|
|
||||||
Int.max
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the location of the candidate window.
|
|
||||||
///
|
|
||||||
/// Please note that the method has side effects that modifies
|
|
||||||
/// `windowTopLeftPoint` to make the candidate window to stay in at least
|
|
||||||
/// in a screen.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - windowTopLeftPoint: The given location.
|
|
||||||
/// - height: The height that helps the window not to be out of the bottom
|
|
||||||
/// of a screen.
|
|
||||||
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.doSet(
|
|
||||||
windowTopLeftPoint: windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: height
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func doSet(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: CGFloat) {
|
|
||||||
guard let window = window else { return }
|
|
||||||
let windowSize = window.frame.size
|
|
||||||
|
|
||||||
var adjustedPoint = windowTopLeftPoint
|
|
||||||
var delta = heightDelta
|
|
||||||
var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast
|
|
||||||
for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) {
|
|
||||||
screenFrame = frame
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if delta > screenFrame.size.height / 2.0 { delta = 0.0 }
|
|
||||||
|
|
||||||
if adjustedPoint.y < screenFrame.minY + windowSize.height {
|
|
||||||
adjustedPoint.y = windowTopLeftPoint.y + windowSize.height + delta
|
|
||||||
}
|
|
||||||
adjustedPoint.y = min(adjustedPoint.y, screenFrame.maxY - 1.0)
|
|
||||||
adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width - 1.0)
|
|
||||||
|
|
||||||
window.setFrameTopLeftPoint(adjustedPoint)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -60,7 +60,7 @@ struct suiPrefPaneGeneral: View {
|
||||||
Picker(
|
Picker(
|
||||||
"",
|
"",
|
||||||
selection: $selCandidateUIFontSize.onChange {
|
selection: $selCandidateUIFontSize.onChange {
|
||||||
PrefMgr.shared.candidateListTextSize = CGFloat(selCandidateUIFontSize)
|
PrefMgr.shared.candidateListTextSize = Double(selCandidateUIFontSize)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Group {
|
Group {
|
||||||
|
|
|
@ -53,7 +53,7 @@ extension ctlClientListMgr {
|
||||||
alert.addButton(withTitle: NSLocalizedString("Just Select", comment: "") + "…")
|
alert.addButton(withTitle: NSLocalizedString("Just Select", comment: "") + "…")
|
||||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
|
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
|
||||||
|
|
||||||
let maxFloat = CGFloat(Float.greatestFiniteMagnitude)
|
let maxFloat = Double(Float.greatestFiniteMagnitude)
|
||||||
let scrollview = NSScrollView(frame: NSRect(x: 0, y: 0, width: 370, height: 380))
|
let scrollview = NSScrollView(frame: NSRect(x: 0, y: 0, width: 370, height: 380))
|
||||||
let contentSize = scrollview.contentSize
|
let contentSize = scrollview.contentSize
|
||||||
scrollview.borderType = .noBorder
|
scrollview.borderType = .noBorder
|
||||||
|
|
|
@ -12,7 +12,7 @@ import BookmarkManager
|
||||||
import IMKUtils
|
import IMKUtils
|
||||||
import Shared
|
import Shared
|
||||||
|
|
||||||
private let kWindowTitleHeight: CGFloat = 78
|
private let kWindowTitleHeight: Double = 78
|
||||||
|
|
||||||
extension NSToolbarItem.Identifier {
|
extension NSToolbarItem.Identifier {
|
||||||
fileprivate static let ofGeneral = NSToolbarItem.Identifier(rawValue: "tabGeneral")
|
fileprivate static let ofGeneral = NSToolbarItem.Identifier(rawValue: "tabGeneral")
|
||||||
|
|
|
@ -13,6 +13,7 @@ import PopupCompositionBuffer
|
||||||
import Shared
|
import Shared
|
||||||
import ShiftKeyUpChecker
|
import ShiftKeyUpChecker
|
||||||
import TooltipUI
|
import TooltipUI
|
||||||
|
import Voltaire
|
||||||
|
|
||||||
/// 輸入法控制模組,乃在輸入法端用以控制輸入行為的基礎型別。
|
/// 輸入法控制模組,乃在輸入法端用以控制輸入行為的基礎型別。
|
||||||
///
|
///
|
||||||
|
@ -28,8 +29,8 @@ class ctlInputMethod: IMKInputController {
|
||||||
static var areWeNerfing = false
|
static var areWeNerfing = false
|
||||||
|
|
||||||
/// 目前在用的的選字窗副本。
|
/// 目前在用的的選字窗副本。
|
||||||
static var ctlCandidateCurrent: ctlCandidateProtocol =
|
static var ctlCandidateCurrent: CtlCandidateProtocol =
|
||||||
PrefMgr.shared.useIMKCandidateWindow ? ctlCandidateIMK.init(.horizontal) : ctlCandidateUniversal.init(.horizontal)
|
PrefMgr.shared.useIMKCandidateWindow ? CtlCandidateIMK(.horizontal) : CtlCandidateUniversal(.horizontal)
|
||||||
|
|
||||||
/// 工具提示視窗的共用副本。
|
/// 工具提示視窗的共用副本。
|
||||||
static var tooltipInstance = TooltipUI()
|
static var tooltipInstance = TooltipUI()
|
||||||
|
|
|
@ -18,7 +18,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
|
||||||
return client.bundleIdentifier() ?? ""
|
return client.bundleIdentifier() ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func ctlCandidate() -> ctlCandidateProtocol { ctlInputMethod.ctlCandidateCurrent }
|
func ctlCandidate() -> CtlCandidateProtocol { ctlInputMethod.ctlCandidateCurrent }
|
||||||
|
|
||||||
func candidateSelectionCalledByKeyHandler(at index: Int) {
|
func candidateSelectionCalledByKeyHandler(at index: Int) {
|
||||||
candidateSelected(at: index)
|
candidateSelected(at: index)
|
||||||
|
@ -49,8 +49,16 @@ extension ctlInputMethod: KeyHandlerDelegate {
|
||||||
|
|
||||||
// MARK: - Candidate Controller Delegate
|
// MARK: - Candidate Controller Delegate
|
||||||
|
|
||||||
extension ctlInputMethod: ctlCandidateDelegate {
|
extension ctlInputMethod: CtlCandidateDelegate {
|
||||||
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int {
|
func buzz() {
|
||||||
|
IMEApp.buzz()
|
||||||
|
}
|
||||||
|
|
||||||
|
func kanjiConversionIfRequired(_ target: String) -> String {
|
||||||
|
ChineseConverter.kanjiConversionIfRequired(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func candidateCountForController(_ controller: CtlCandidateProtocol) -> Int {
|
||||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||||
if state.isCandidateContainer {
|
if state.isCandidateContainer {
|
||||||
return state.candidates.count
|
return state.candidates.count
|
||||||
|
@ -61,7 +69,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
||||||
/// 直接給出全部的候選字詞的字音配對陣列
|
/// 直接給出全部的候選字詞的字音配對陣列
|
||||||
/// - Parameter controller: 對應的控制器。因為有唯一解,所以填錯了也不會有影響。
|
/// - Parameter controller: 對應的控制器。因為有唯一解,所以填錯了也不會有影響。
|
||||||
/// - Returns: 候選字詞陣列(字音配對)。
|
/// - Returns: 候選字詞陣列(字音配對)。
|
||||||
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] {
|
func candidatesForController(_ controller: CtlCandidateProtocol) -> [(String, String)] {
|
||||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||||
if state.isCandidateContainer {
|
if state.isCandidateContainer {
|
||||||
return state.candidates
|
return state.candidates
|
||||||
|
@ -69,7 +77,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
||||||
return .init()
|
return .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)
|
func ctlCandidate(_ controller: CtlCandidateProtocol, candidateAtIndex index: Int)
|
||||||
-> (String, String)
|
-> (String, String)
|
||||||
{
|
{
|
||||||
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
_ = controller // 防止格式整理工具毀掉與此對應的參數。
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
import NSAttributedTextView
|
import NSAttributedTextView
|
||||||
import Shared
|
import Shared
|
||||||
|
import Voltaire
|
||||||
|
|
||||||
// MARK: - Tooltip Display and Candidate Display Methods
|
// MARK: - Tooltip Display and Candidate Display Methods
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ extension ctlInputMethod {
|
||||||
guard client() != nil else { return }
|
guard client() != nil else { return }
|
||||||
let lineHeightRect = lineHeightRect()
|
let lineHeightRect = lineHeightRect()
|
||||||
var finalOrigin: NSPoint = lineHeightRect.origin
|
var finalOrigin: NSPoint = lineHeightRect.origin
|
||||||
let delta: CGFloat = lineHeightRect.size.height + 4.0 // bottomOutOfScreenAdjustmentHeight
|
let delta: Double = lineHeightRect.size.height + 4.0 // bottomOutOfScreenAdjustmentHeight
|
||||||
if isVerticalTyping {
|
if isVerticalTyping {
|
||||||
finalOrigin = NSPoint(
|
finalOrigin = NSPoint(
|
||||||
x: lineHeightRect.origin.x + lineHeightRect.size.width + 5, y: lineHeightRect.origin.y
|
x: lineHeightRect.origin.x + lineHeightRect.size.width + 5, y: lineHeightRect.origin.y
|
||||||
|
@ -79,7 +80,7 @@ extension ctlInputMethod {
|
||||||
}
|
}
|
||||||
if isVerticalTyping { return true }
|
if isVerticalTyping { return true }
|
||||||
// 接下來的判斷並非適用於 IMK 選字窗,所以先插入排除語句。
|
// 接下來的判斷並非適用於 IMK 選字窗,所以先插入排除語句。
|
||||||
guard ctlInputMethod.ctlCandidateCurrent is ctlCandidateUniversal else { return false }
|
guard ctlInputMethod.ctlCandidateCurrent is CtlCandidateUniversal else { return false }
|
||||||
// 以上是通用情形。接下來決定橫排輸入時是否使用縱排選字窗。
|
// 以上是通用情形。接下來決定橫排輸入時是否使用縱排選字窗。
|
||||||
// 因為在拿候選字陣列時已經排序過了,所以這裡不用再多排序。
|
// 因為在拿候選字陣列時已經排序過了,所以這裡不用再多排序。
|
||||||
// 測量每頁顯示候選字的累計總長度。如果太長的話就強制使用縱排候選字窗。
|
// 測量每頁顯示候選字的累計總長度。如果太長的話就強制使用縱排候選字窗。
|
||||||
|
@ -107,14 +108,14 @@ extension ctlInputMethod {
|
||||||
|
|
||||||
ctlInputMethod.ctlCandidateCurrent =
|
ctlInputMethod.ctlCandidateCurrent =
|
||||||
PrefMgr.shared.useIMKCandidateWindow
|
PrefMgr.shared.useIMKCandidateWindow
|
||||||
? ctlCandidateIMK.init(candidateLayout) : ctlCandidateUniversal.init(candidateLayout)
|
? CtlCandidateIMK(candidateLayout) : CtlCandidateUniversal(candidateLayout)
|
||||||
|
|
||||||
// set the attributes for the candidate panel (which uses NSAttributedString)
|
// set the attributes for the candidate panel (which uses NSAttributedString)
|
||||||
let textSize = PrefMgr.shared.candidateListTextSize
|
let textSize = PrefMgr.shared.candidateListTextSize
|
||||||
let minimumKeyLabelSize: Double = 10
|
let minimumKeyLabelSize: Double = 10
|
||||||
let keyLabelSize = max(textSize / 2, minimumKeyLabelSize)
|
let keyLabelSize = max(textSize / 2, minimumKeyLabelSize)
|
||||||
|
|
||||||
func labelFont(name: String?, size: CGFloat) -> NSFont {
|
func labelFont(name: String?, size: Double) -> NSFont {
|
||||||
if let name = name {
|
if let name = name {
|
||||||
return NSFont(name: name, size: size) ?? NSFont.systemFont(ofSize: size)
|
return NSFont(name: name, size: size) ?? NSFont.systemFont(ofSize: size)
|
||||||
}
|
}
|
||||||
|
@ -137,11 +138,24 @@ extension ctlInputMethod {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctlInputMethod.ctlCandidateCurrent.delegate = self
|
ctlInputMethod.ctlCandidateCurrent.delegate = self
|
||||||
|
ctlInputMethod.ctlCandidateCurrent.showPageButtons = PrefMgr.shared.showPageButtonsInCandidateWindow
|
||||||
|
ctlInputMethod.ctlCandidateCurrent.useLangIdentifier = PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier
|
||||||
|
ctlInputMethod.ctlCandidateCurrent.locale = {
|
||||||
|
switch inputMode {
|
||||||
|
case .imeModeCHS: return "zh-Hans"
|
||||||
|
case .imeModeCHT:
|
||||||
|
if !PrefMgr.shared.shiftJISShinjitaiOutputEnabled, !PrefMgr.shared.chineseConversionEnabled {
|
||||||
|
return "zh-Hant"
|
||||||
|
}
|
||||||
|
return "ja"
|
||||||
|
default: return ""
|
||||||
|
}
|
||||||
|
}()
|
||||||
ctlInputMethod.ctlCandidateCurrent.reloadData()
|
ctlInputMethod.ctlCandidateCurrent.reloadData()
|
||||||
|
|
||||||
if #available(macOS 10.14, *) {
|
if #available(macOS 10.14, *) {
|
||||||
// Spotlight 視窗會擋住 IMK 選字窗,所以需要特殊處理。
|
// Spotlight 視窗會擋住 IMK 選字窗,所以需要特殊處理。
|
||||||
if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK {
|
if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? CtlCandidateIMK {
|
||||||
while ctlCandidateCurrent.windowLevel() <= client.windowLevel() {
|
while ctlCandidateCurrent.windowLevel() <= client.windowLevel() {
|
||||||
ctlCandidateCurrent.setWindowLevel(UInt64(max(0, client.windowLevel() + 1000)))
|
ctlCandidateCurrent.setWindowLevel(UInt64(max(0, client.windowLevel() + 1000)))
|
||||||
}
|
}
|
||||||
|
@ -174,7 +188,7 @@ extension ctlInputMethod {
|
||||||
/// **REASON**: IMKCandidates has bug that it does not respect font attributes attached to the
|
/// **REASON**: IMKCandidates has bug that it does not respect font attributes attached to the
|
||||||
/// results generated from `candidiates() -> [Any]!` function. IMKCandidates is plagued with
|
/// results generated from `candidiates() -> [Any]!` function. IMKCandidates is plagued with
|
||||||
/// bugs which are not dealt in the recent decade, regardless Radar complaints from input method developers.
|
/// bugs which are not dealt in the recent decade, regardless Radar complaints from input method developers.
|
||||||
/// 1) Remove the usage of ".languageIdentifier" from ctlCandidateUniversal.swift (already done).
|
/// 1) Make sure the usage of ".languageIdentifier" is disabled in the Dev Zone of the vChewing Preferences.
|
||||||
/// 2) Run "make update" in the project folder to download the latest git-submodule of dictionary file.
|
/// 2) Run "make update" in the project folder to download the latest git-submodule of dictionary file.
|
||||||
/// 3) Compile the target "vChewingInstaller", run it. It will install the input method into
|
/// 3) Compile the target "vChewingInstaller", run it. It will install the input method into
|
||||||
/// "~/Library/Input Methods/" folder. Remember to ENABLE BOTH "vChewing-CHS"
|
/// "~/Library/Input Methods/" folder. Remember to ENABLE BOTH "vChewing-CHS"
|
||||||
|
@ -184,7 +198,7 @@ extension ctlInputMethod {
|
||||||
/// 5) Do NOT enable either KangXi conversion mode nor JIS conversion mode. They are disabled by default.
|
/// 5) Do NOT enable either KangXi conversion mode nor JIS conversion mode. They are disabled by default.
|
||||||
/// 6) Expecting the glyph differences of the candidate "骨" between PingFang SC and PingFang TC when rendering
|
/// 6) Expecting the glyph differences of the candidate "骨" between PingFang SC and PingFang TC when rendering
|
||||||
/// the candidate window in different "vChewing-CHS" and "vChewing-CHT" input modes.
|
/// the candidate window in different "vChewing-CHS" and "vChewing-CHT" input modes.
|
||||||
static func candidateFont(name: String? = nil, size: CGFloat) -> NSFont {
|
static func candidateFont(name: String? = nil, size: Double) -> NSFont {
|
||||||
let finalReturnFont: NSFont =
|
let finalReturnFont: NSFont =
|
||||||
{
|
{
|
||||||
switch IMEApp.currentInputMode {
|
switch IMEApp.currentInputMode {
|
||||||
|
|
|
@ -143,8 +143,8 @@ extension ctlInputMethod {
|
||||||
// 這樣可以讓 interpretKeyEvents() 函式自行判斷:
|
// 這樣可以讓 interpretKeyEvents() 函式自行判斷:
|
||||||
// - 是就地交給 imkCandidates.interpretKeyEvents() 處理?
|
// - 是就地交給 imkCandidates.interpretKeyEvents() 處理?
|
||||||
// - 還是藉由 delegate 扔回 ctlInputMethod 給 KeyHandler 處理?
|
// - 還是藉由 delegate 扔回 ctlInputMethod 給 KeyHandler 處理?
|
||||||
if let imkCandidates = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK, imkCandidates.visible {
|
if let imkCandidates = ctlInputMethod.ctlCandidateCurrent as? CtlCandidateIMK, imkCandidates.visible {
|
||||||
let event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal
|
let event: NSEvent = CtlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal
|
||||||
|
|
||||||
// Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。
|
// Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。
|
||||||
// 所以這裡直接將 Shift Flags 清空。
|
// 所以這裡直接將 Shift Flags 清空。
|
||||||
|
@ -158,7 +158,7 @@ extension ctlInputMethod {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 聯想詞選字。
|
// 聯想詞選字。
|
||||||
if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode],
|
if let newChar = CtlCandidateIMK.defaultIMKSelectionKey[event.keyCode],
|
||||||
event.isShiftHold, state.type == .ofAssociates,
|
event.isShiftHold, state.type == .ofAssociates,
|
||||||
let newEvent = event.reinitiate(modifierFlags: [], characters: newChar)
|
let newEvent = event.reinitiate(modifierFlags: [], characters: newChar)
|
||||||
{
|
{
|
||||||
|
@ -177,7 +177,7 @@ extension ctlInputMethod {
|
||||||
|
|
||||||
private func imkCandidatesEventSubHandler(event: NSEvent) -> Bool {
|
private func imkCandidatesEventSubHandler(event: NSEvent) -> Bool {
|
||||||
let eventArray = [event]
|
let eventArray = [event]
|
||||||
guard let imkC = Self.ctlCandidateCurrent as? ctlCandidateIMK else { return false }
|
guard let imkC = Self.ctlCandidateCurrent as? CtlCandidateIMK else { return false }
|
||||||
if event.isEsc || event.isBackSpace || event.isDelete || (event.isShiftHold && !event.isSpace) {
|
if event.isEsc || event.isBackSpace || event.isDelete || (event.isShiftHold && !event.isSpace) {
|
||||||
return commonEventHandler(event)
|
return commonEventHandler(event)
|
||||||
} else if event.isSymbolMenuPhysicalKey {
|
} else if event.isSymbolMenuPhysicalKey {
|
||||||
|
@ -200,7 +200,7 @@ extension ctlInputMethod {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode] {
|
if let newChar = CtlCandidateIMK.defaultIMKSelectionKey[event.keyCode] {
|
||||||
/// 根據 KeyCode 重新換算一下選字鍵的 NSEvent,糾正其 Character 數值。
|
/// 根據 KeyCode 重新換算一下選字鍵的 NSEvent,糾正其 Character 數值。
|
||||||
/// 反正 IMK 選字窗目前也沒辦法修改選字鍵。
|
/// 反正 IMK 選字窗目前也沒辦法修改選字鍵。
|
||||||
let newEvent = event.reinitiate(characters: newChar)
|
let newEvent = event.reinitiate(characters: newChar)
|
||||||
|
|
|
@ -15,12 +15,10 @@
|
||||||
5B21176C287539BB000443A9 /* ctlInputMethod_HandleStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */; };
|
5B21176C287539BB000443A9 /* ctlInputMethod_HandleStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */; };
|
||||||
5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */; };
|
5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */; };
|
||||||
5B21177028753B9D000443A9 /* ctlInputMethod_Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176F28753B9D000443A9 /* ctlInputMethod_Delegates.swift */; };
|
5B21177028753B9D000443A9 /* ctlInputMethod_Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176F28753B9D000443A9 /* ctlInputMethod_Delegates.swift */; };
|
||||||
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */; };
|
|
||||||
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */; };
|
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */; };
|
||||||
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
|
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
|
||||||
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; };
|
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; };
|
||||||
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
|
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
|
||||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; };
|
|
||||||
5B6C141228A9D4B30098ADF8 /* ctlInputMethod_HandleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_HandleEvent.swift */; };
|
5B6C141228A9D4B30098ADF8 /* ctlInputMethod_HandleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_HandleEvent.swift */; };
|
||||||
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
|
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
|
||||||
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; };
|
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; };
|
||||||
|
@ -34,6 +32,8 @@
|
||||||
5B963CA328D5C23600DCEE88 /* SwiftExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963CA228D5C23600DCEE88 /* SwiftExtension */; };
|
5B963CA328D5C23600DCEE88 /* SwiftExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963CA228D5C23600DCEE88 /* SwiftExtension */; };
|
||||||
5B963CA828D5DB1400DCEE88 /* PrefMgr_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B963CA728D5DB1400DCEE88 /* PrefMgr_Core.swift */; };
|
5B963CA828D5DB1400DCEE88 /* PrefMgr_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B963CA728D5DB1400DCEE88 /* PrefMgr_Core.swift */; };
|
||||||
5B98114828D6198700CBC605 /* PinyinPhonaConverter in Frameworks */ = {isa = PBXBuildFile; productRef = 5B98114728D6198700CBC605 /* PinyinPhonaConverter */; };
|
5B98114828D6198700CBC605 /* PinyinPhonaConverter in Frameworks */ = {isa = PBXBuildFile; productRef = 5B98114728D6198700CBC605 /* PinyinPhonaConverter */; };
|
||||||
|
5BA8C30328DF0360004C5CC4 /* CandidateWindow in Frameworks */ = {isa = PBXBuildFile; productRef = 5BA8C30228DF0360004C5CC4 /* CandidateWindow */; };
|
||||||
|
5BA8C30528DF0364004C5CC4 /* Voltaire in Frameworks */ = {isa = PBXBuildFile; productRef = 5BA8C30428DF0364004C5CC4 /* Voltaire */; };
|
||||||
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */; };
|
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */; };
|
||||||
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */; };
|
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */; };
|
||||||
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */; };
|
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */; };
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
5BFC63CC28D49BBC004A77B7 /* UpdateSputnik in Frameworks */ = {isa = PBXBuildFile; productRef = 5BFC63CB28D49BBC004A77B7 /* UpdateSputnik */; };
|
5BFC63CC28D49BBC004A77B7 /* UpdateSputnik in Frameworks */ = {isa = PBXBuildFile; productRef = 5BFC63CB28D49BBC004A77B7 /* UpdateSputnik */; };
|
||||||
5BFC63CF28D4ACA3004A77B7 /* LangModelAssembly in Frameworks */ = {isa = PBXBuildFile; productRef = 5BFC63CE28D4ACA3004A77B7 /* LangModelAssembly */; };
|
5BFC63CF28D4ACA3004A77B7 /* LangModelAssembly in Frameworks */ = {isa = PBXBuildFile; productRef = 5BFC63CE28D4ACA3004A77B7 /* LangModelAssembly */; };
|
||||||
5BFC63D128D4B9F7004A77B7 /* IMKUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 5BFC63D028D4B9F7004A77B7 /* IMKUtils */; };
|
5BFC63D128D4B9F7004A77B7 /* IMKUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 5BFC63D028D4B9F7004A77B7 /* IMKUtils */; };
|
||||||
5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */; };
|
5BFDF011289635C100417BBC /* IMKCandidatesImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFDF010289635C100417BBC /* IMKCandidatesImpl.swift */; };
|
||||||
6A187E2616004C5900466B2E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A187E2816004C5900466B2E /* MainMenu.xib */; };
|
6A187E2616004C5900466B2E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A187E2816004C5900466B2E /* MainMenu.xib */; };
|
||||||
6A225A1F23679F2600F685C6 /* NotarizedArchives in Resources */ = {isa = PBXBuildFile; fileRef = 6A225A1E23679F2600F685C6 /* NotarizedArchives */; };
|
6A225A1F23679F2600F685C6 /* NotarizedArchives in Resources */ = {isa = PBXBuildFile; fileRef = 6A225A1E23679F2600F685C6 /* NotarizedArchives */; };
|
||||||
6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; };
|
6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; };
|
||||||
|
@ -198,7 +198,6 @@
|
||||||
5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleStates.swift; sourceTree = "<group>"; };
|
5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleStates.swift; sourceTree = "<group>"; };
|
||||||
5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleDisplay.swift; sourceTree = "<group>"; };
|
5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleDisplay.swift; sourceTree = "<group>"; };
|
||||||
5B21176F28753B9D000443A9 /* ctlInputMethod_Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Delegates.swift; sourceTree = "<group>"; };
|
5B21176F28753B9D000443A9 /* ctlInputMethod_Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Delegates.swift; sourceTree = "<group>"; };
|
||||||
5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateUniversal.swift; sourceTree = "<group>"; };
|
|
||||||
5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = "<group>"; };
|
5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = "<group>"; };
|
||||||
5B2F2BB3286216A500B8557B /* vChewingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = vChewingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
5B2F2BB3286216A500B8557B /* vChewingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = vChewingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = "<group>"; };
|
5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = "<group>"; };
|
||||||
|
@ -206,7 +205,6 @@
|
||||||
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; };
|
5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; };
|
||||||
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
|
||||||
5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
5B6C141128A9D4B30098ADF8 /* ctlInputMethod_HandleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleEvent.swift; sourceTree = "<group>"; };
|
5B6C141128A9D4B30098ADF8 /* ctlInputMethod_HandleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleEvent.swift; sourceTree = "<group>"; };
|
||||||
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
|
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
|
||||||
|
@ -224,6 +222,8 @@
|
||||||
5B963CA128D5C22D00DCEE88 /* vChewing_SwiftExtension */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_SwiftExtension; path = Packages/vChewing_SwiftExtension; sourceTree = "<group>"; };
|
5B963CA128D5C22D00DCEE88 /* vChewing_SwiftExtension */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_SwiftExtension; path = Packages/vChewing_SwiftExtension; sourceTree = "<group>"; };
|
||||||
5B963CA728D5DB1400DCEE88 /* PrefMgr_Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefMgr_Core.swift; sourceTree = "<group>"; };
|
5B963CA728D5DB1400DCEE88 /* PrefMgr_Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefMgr_Core.swift; sourceTree = "<group>"; };
|
||||||
5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PinyinPhonaConverter; path = Packages/vChewing_PinyinPhonaConverter; sourceTree = "<group>"; };
|
5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PinyinPhonaConverter; path = Packages/vChewing_PinyinPhonaConverter; sourceTree = "<group>"; };
|
||||||
|
5BA8C30028DEFE4F004C5CC4 /* OpenVanilla_Voltaire */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenVanilla_Voltaire; path = Packages/OpenVanilla_Voltaire; sourceTree = "<group>"; };
|
||||||
|
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CandidateWindow; path = Packages/vChewing_CandidateWindow; sourceTree = "<group>"; };
|
||||||
5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneGeneral.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneGeneral.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneKeyboard.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneKeyboard.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefUI.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefUI.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
|
@ -298,7 +298,7 @@
|
||||||
5BFC63C728D49511004A77B7 /* vChewing_IMKUtils */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_IMKUtils; path = Packages/vChewing_IMKUtils; sourceTree = "<group>"; };
|
5BFC63C728D49511004A77B7 /* vChewing_IMKUtils */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_IMKUtils; path = Packages/vChewing_IMKUtils; sourceTree = "<group>"; };
|
||||||
5BFC63CA28D49B2B004A77B7 /* vChewing_UpdateSputnik */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_UpdateSputnik; path = Packages/vChewing_UpdateSputnik; sourceTree = "<group>"; };
|
5BFC63CA28D49B2B004A77B7 /* vChewing_UpdateSputnik */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_UpdateSputnik; path = Packages/vChewing_UpdateSputnik; sourceTree = "<group>"; };
|
||||||
5BFC63CD28D4AC98004A77B7 /* vChewing_LangModelAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_LangModelAssembly; path = Packages/vChewing_LangModelAssembly; sourceTree = "<group>"; };
|
5BFC63CD28D4AC98004A77B7 /* vChewing_LangModelAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_LangModelAssembly; path = Packages/vChewing_LangModelAssembly; sourceTree = "<group>"; };
|
||||||
5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateIMK.swift; sourceTree = "<group>"; };
|
5BFDF010289635C100417BBC /* IMKCandidatesImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKCandidatesImpl.swift; sourceTree = "<group>"; };
|
||||||
5BFDF48C27B51867009523B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = "<group>"; };
|
5BFDF48C27B51867009523B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = "<group>"; };
|
||||||
6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewing.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewing.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; };
|
6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; };
|
||||||
|
@ -357,7 +357,9 @@
|
||||||
5B40113928D7050D00A9D4CB /* Shared in Frameworks */,
|
5B40113928D7050D00A9D4CB /* Shared in Frameworks */,
|
||||||
5BFC63C628D48806004A77B7 /* NSAttributedTextView in Frameworks */,
|
5BFC63C628D48806004A77B7 /* NSAttributedTextView in Frameworks */,
|
||||||
5BDB7A3B28D4824A001AC277 /* FolderMonitor in Frameworks */,
|
5BDB7A3B28D4824A001AC277 /* FolderMonitor in Frameworks */,
|
||||||
|
5BA8C30328DF0360004C5CC4 /* CandidateWindow in Frameworks */,
|
||||||
5BC5E01E28DDE4770094E427 /* NotifierUI in Frameworks */,
|
5BC5E01E28DDE4770094E427 /* NotifierUI in Frameworks */,
|
||||||
|
5BA8C30528DF0364004C5CC4 /* Voltaire in Frameworks */,
|
||||||
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */,
|
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -468,9 +470,7 @@
|
||||||
5B62A33E27AE7CD900A19448 /* CandidateUI */ = {
|
5B62A33E27AE7CD900A19448 /* CandidateUI */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */,
|
5BFDF010289635C100417BBC /* IMKCandidatesImpl.swift */,
|
||||||
5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */,
|
|
||||||
5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */,
|
|
||||||
);
|
);
|
||||||
path = CandidateUI;
|
path = CandidateUI;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -565,9 +565,11 @@
|
||||||
5BDB7A3128D47587001AC277 /* DanielGalasko_FolderMonitor */,
|
5BDB7A3128D47587001AC277 /* DanielGalasko_FolderMonitor */,
|
||||||
5BFC63C428D487AE004A77B7 /* Fuziki_NSAttributedTextView */,
|
5BFC63C428D487AE004A77B7 /* Fuziki_NSAttributedTextView */,
|
||||||
5BDB7A3028D47587001AC277 /* Jad_BookmarkManager */,
|
5BDB7A3028D47587001AC277 /* Jad_BookmarkManager */,
|
||||||
|
5BA8C30028DEFE4F004C5CC4 /* OpenVanilla_Voltaire */,
|
||||||
5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */,
|
5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */,
|
||||||
5BDB7A3528D47587001AC277 /* RMJay_LineReader */,
|
5BDB7A3528D47587001AC277 /* RMJay_LineReader */,
|
||||||
5BDB7A2F28D47587001AC277 /* Sindresorhus_Preferences */,
|
5BDB7A2F28D47587001AC277 /* Sindresorhus_Preferences */,
|
||||||
|
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */,
|
||||||
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */,
|
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */,
|
||||||
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */,
|
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */,
|
||||||
5BFC63C728D49511004A77B7 /* vChewing_IMKUtils */,
|
5BFC63C728D49511004A77B7 /* vChewing_IMKUtils */,
|
||||||
|
@ -780,6 +782,8 @@
|
||||||
5BC5E01D28DDE4770094E427 /* NotifierUI */,
|
5BC5E01D28DDE4770094E427 /* NotifierUI */,
|
||||||
5BC5E02028DDEFE00094E427 /* TooltipUI */,
|
5BC5E02028DDEFE00094E427 /* TooltipUI */,
|
||||||
5BC5E02328DE07860094E427 /* PopupCompositionBuffer */,
|
5BC5E02328DE07860094E427 /* PopupCompositionBuffer */,
|
||||||
|
5BA8C30228DF0360004C5CC4 /* CandidateWindow */,
|
||||||
|
5BA8C30428DF0364004C5CC4 /* Voltaire */,
|
||||||
);
|
);
|
||||||
productName = vChewing;
|
productName = vChewing;
|
||||||
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */;
|
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */;
|
||||||
|
@ -1067,13 +1071,11 @@
|
||||||
5BAEFAD028012565001F42C9 /* LMMgr.swift in Sources */,
|
5BAEFAD028012565001F42C9 /* LMMgr.swift in Sources */,
|
||||||
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
|
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
|
||||||
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
|
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
|
||||||
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */,
|
|
||||||
5BCCAFF828DB19A300AB1B27 /* PrefMgr_Extension.swift in Sources */,
|
5BCCAFF828DB19A300AB1B27 /* PrefMgr_Extension.swift in Sources */,
|
||||||
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
|
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
|
||||||
5B8457A12871ADBE00C93B01 /* ChineseConverterBridge.swift in Sources */,
|
5B8457A12871ADBE00C93B01 /* ChineseConverterBridge.swift in Sources */,
|
||||||
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
|
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
|
||||||
5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */,
|
5BFDF011289635C100417BBC /* IMKCandidatesImpl.swift in Sources */,
|
||||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
|
|
||||||
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
|
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
|
||||||
5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */,
|
5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */,
|
||||||
5B00FA0C28DEC17200F6D436 /* ctlInputMethod_IMKCandidatesData.swift in Sources */,
|
5B00FA0C28DEC17200F6D436 /* ctlInputMethod_IMKCandidatesData.swift in Sources */,
|
||||||
|
@ -1774,6 +1776,14 @@
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = PinyinPhonaConverter;
|
productName = PinyinPhonaConverter;
|
||||||
};
|
};
|
||||||
|
5BA8C30228DF0360004C5CC4 /* CandidateWindow */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = CandidateWindow;
|
||||||
|
};
|
||||||
|
5BA8C30428DF0364004C5CC4 /* Voltaire */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Voltaire;
|
||||||
|
};
|
||||||
5BC5E01D28DDE4770094E427 /* NotifierUI */ = {
|
5BC5E01D28DDE4770094E427 /* NotifierUI */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = NotifierUI;
|
productName = NotifierUI;
|
||||||
|
|
Loading…
Reference in New Issue