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:
ShikiSuen 2022-09-24 14:40:43 +08:00
parent 0041c3d669
commit e9137b9e53
28 changed files with 489 additions and 321 deletions

View File

@ -48,7 +48,7 @@ public class NSAttributedTextView: NSView {
}
public var direction: writingDirection = .horizontal
public var fontSize: CGFloat = NSFont.systemFontSize {
public var fontSize: Double = NSFont.systemFontSize {
didSet {
attributes[.font] = NSFont.systemFont(ofSize: fontSize)
}

View File

@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

View File

@ -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"),
]
)
]
)

View File

@ -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.
```

View File

@ -10,6 +10,10 @@
// Zonble Voltaire
import CandidateWindow
import Cocoa
import Shared
private class vwrCandidateUniversal: NSView {
var highlightedIndex: Int = 0 {
didSet { highlightedIndex = min(max(highlightedIndex, 0), dispCandidatesWithLabels.count - 1) }
@ -17,22 +21,23 @@ private class vwrCandidateUniversal: NSView {
var action: Selector?
weak var target: AnyObject?
weak var controller: AnyObject?
var isVerticalLayout = false
var fractionFontSize: CGFloat = 12.0
var fractionFontSize: Double = 12.0
private var keyLabels: [String] = []
private var displayedCandidates: [String] = []
private var dispCandidatesWithLabels: [String] = []
private var keyLabelHeight: CGFloat = 0
private var keyLabelWidth: CGFloat = 0
private var candidateTextHeight: CGFloat = 0
private var cellPadding: CGFloat = 0
private var keyLabelHeight: Double = 0
private var keyLabelWidth: Double = 0
private var candidateTextHeight: Double = 0
private var cellPadding: Double = 0
private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:]
private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
private var windowWidth: CGFloat = 0 //
private var elementWidths: [CGFloat] = []
private var elementHeights: [CGFloat] = [] //
private var windowWidth: Double = 0 //
private var elementWidths: [Double] = []
private var elementHeights: [Double] = [] //
private var trackingHighlightedIndex: Int = .max {
didSet { trackingHighlightedIndex = max(trackingHighlightedIndex, 0) }
}
@ -50,17 +55,17 @@ private class vwrCandidateUniversal: NSView {
result.width = windowWidth
result.height = elementHeights.reduce(0, +)
case false:
result.width = elementWidths.reduce(0, +) + CGFloat(elementWidths.count)
result.width = elementWidths.reduce(0, +) + Double(elementWidths.count)
result.height = candidateTextHeight + cellPadding
}
}
return result
}
@objc(setKeyLabels:displayedCandidates:)
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 theConverted = ChineseConverter.kanjiConversionIfRequired(theCandidate)
let theConverted = delegate.kanjiConversionIfRequired(theCandidate)
return (theCandidate == theConverted) ? theCandidate : "\(theConverted)(\(theCandidate))"
}
@ -69,9 +74,9 @@ private class vwrCandidateUniversal: NSView {
displayedCandidates = Array(candidates[0..<count])
dispCandidatesWithLabels = zip(keyLabels, displayedCandidates).map { $0 + $1 }
var newWidths = [CGFloat]()
var calculatedWindowWidth = CGFloat()
var newHeights = [CGFloat]()
var newWidths = [Double]()
var calculatedWindowWidth = Double()
var newHeights = [Double]()
let baseSize = NSSize(width: 10240.0, height: 10240.0)
for index in 0..<count {
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
@ -133,55 +138,15 @@ private class vwrCandidateUniversal: NSView {
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) {
guard let controller = controller as? CtlCandidateUniversal else { return }
let bounds = bounds
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
NSBezierPath.fill(bounds)
switch isVerticalLayout {
case true:
var accuHeight: CGFloat = 0
var accuHeight: Double = 0
for (index, elementHeight) in elementHeights.enumerated() {
let currentHeight = elementHeight
let rctCandidateArea = NSRect(
@ -200,7 +165,7 @@ private class vwrCandidateUniversal: NSView {
var activeCandidateIndexAttr = keyLabelAttrDict
var activeCandidateAttr = candidateAttrDict
if index == highlightedIndex {
highlightedColor.setFill()
controller.highlightedColor().setFill()
// Highlightened index text color
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
.withAlphaComponent(0.84)
@ -209,7 +174,11 @@ private class vwrCandidateUniversal: NSView {
let path: NSBezierPath = .init(roundedRect: rctCandidateArea, xRadius: 6, yRadius: 6)
path.fill()
}
ensureLangIdentifier(for: &activeCandidateAttr)
if #available(macOS 12, *) {
if controller.useLangIdentifier {
activeCandidateAttr[.languageIdentifier] = controller.locale as AnyObject
}
}
(keyLabels[index] as NSString).draw(
in: rctLabel, withAttributes: activeCandidateIndexAttr
)
@ -219,7 +188,7 @@ private class vwrCandidateUniversal: NSView {
accuHeight += currentHeight
}
case false:
var accuWidth: CGFloat = 0
var accuWidth: Double = 0
for (index, elementWidth) in elementWidths.enumerated() {
let currentWidth = elementWidth
let rctCandidateArea = NSRect(
@ -239,7 +208,7 @@ private class vwrCandidateUniversal: NSView {
var activeCandidateIndexAttr = keyLabelAttrDict
var activeCandidateAttr = candidateAttrDict
if index == highlightedIndex {
highlightedColor.setFill()
controller.highlightedColor().setFill()
// Highlightened index text color
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
.withAlphaComponent(0.84)
@ -248,7 +217,11 @@ private class vwrCandidateUniversal: NSView {
let path: NSBezierPath = .init(roundedRect: rctCandidateArea, xRadius: 6, yRadius: 6)
path.fill()
}
ensureLangIdentifier(for: &activeCandidateAttr)
if #available(macOS 12, *) {
if controller.useLangIdentifier {
activeCandidateAttr[.languageIdentifier] = controller.locale as AnyObject
}
}
(keyLabels[index] as NSString).draw(
in: rctLabel, withAttributes: activeCandidateIndexAttr
)
@ -267,7 +240,7 @@ private class vwrCandidateUniversal: NSView {
}
switch isVerticalLayout {
case true:
var accuHeight: CGFloat = 0.0
var accuHeight = 0.0
for (index, elementHeight) in elementHeights.enumerated() {
let currentHeight = elementHeight
@ -277,7 +250,7 @@ private class vwrCandidateUniversal: NSView {
accuHeight += currentHeight
}
case false:
var accuWidth: CGFloat = 0.0
var accuWidth = 0.0
for (index, elementWidth) in elementWidths.enumerated() {
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 prevPageButton: NSButton
private var nextPageButton: NSButton
@ -408,6 +381,7 @@ public class ctlCandidateUniversal: ctlCandidate {
super.init(layout)
window = panel
currentLayout = layout
candidateView.controller = self
candidateView.target = self
candidateView.action = #selector(candidateViewMouseDidClick(_:))
@ -431,9 +405,9 @@ public class ctlCandidateUniversal: ctlCandidate {
}
@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 currentPageIndex + 1 >= pageCount { IMEApp.buzz() }
if currentPageIndex + 1 >= pageCount { delegate.buzz() }
currentPageIndex = (currentPageIndex + 1 >= pageCount) ? 0 : currentPageIndex + 1
if currentPageIndex == pageCount - 1 {
candidateView.highlightedIndex = min(lastPageContentCount - 1, candidateView.highlightedIndex)
@ -444,9 +418,9 @@ public class ctlCandidateUniversal: ctlCandidate {
}
@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 currentPageIndex == 0 { IMEApp.buzz() }
if currentPageIndex == 0 { delegate.buzz() }
currentPageIndex = (currentPageIndex == 0) ? pageCount - 1 : currentPageIndex - 1
if currentPageIndex == pageCount - 1 {
candidateView.highlightedIndex = min(lastPageContentCount - 1, candidateView.highlightedIndex)
@ -499,7 +473,7 @@ public class ctlCandidateUniversal: ctlCandidate {
}
}
extension ctlCandidateUniversal {
extension CtlCandidateUniversal {
private var pageCount: Int {
guard let delegate = delegate else {
return 0
@ -519,9 +493,7 @@ extension ctlCandidateUniversal {
}
private func layoutCandidateView() {
guard let delegate = delegate else {
return
}
guard let delegate = delegate, let window = window else { return }
candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont)
var candidates = [(String, String)]()
@ -540,14 +512,14 @@ extension ctlCandidateUniversal {
var frameRect = candidateView.frame
frameRect.size = newSize
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
let spacing: CGFloat = 0.0
let spacing = 0.0
if currentLayout == .horizontal { buttonRect.size.height = floor(newSize.height / 2) }
let buttonOriginY: CGFloat = {
let buttonOriginY: Double = {
if currentLayout == .vertical {
return counterHeight
}
@ -582,12 +554,12 @@ extension ctlCandidateUniversal {
rect.size.height += 3
rect.size.width += 4
let rectOriginY: CGFloat =
let rectOriginY: Double =
(currentLayout == .horizontal)
? (newSize.height - rect.height) / 2
: counterHeight
let rectOriginX: CGFloat =
PrefMgr.shared.showPageButtonsInCandidateWindow
let rectOriginX: Double =
showPageButtons
? newSize.width
: newSize.width + 4
rect.origin = NSPoint(x: rectOriginX, y: rectOriginY)
@ -598,12 +570,12 @@ extension ctlCandidateUniversal {
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)
frameRect.size = newSize
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)
}

View File

@ -54,7 +54,7 @@ extension Preferences {
}
}
.modifier(Section.LabelWidthModifier(maximumWidth: $maximumLabelWidth))
.frame(width: CGFloat(contentWidth), alignment: .leading)
.frame(width: Double(contentWidth), alignment: .leading)
.padding(.vertical, 20)
.padding(.horizontal, 30)
}
@ -65,9 +65,9 @@ extension Preferences {
if index != sections.count - 1, sections[index].bottomDivider {
Divider()
// 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) {
$0[.leading] + CGFloat(max(minimumLabelWidth, maximumLabelWidth))
$0[.leading] + Double(max(minimumLabelWidth, maximumLabelWidth))
}
}
}

View File

@ -77,8 +77,8 @@ final class SegmentedControlStyleViewController: NSViewController, PreferencesSt
)
}()
let segmentBorderWidth = CGFloat(preferencePanes.count) + 1
let segmentWidth = segmentSize.width * CGFloat(preferencePanes.count) + segmentBorderWidth
let segmentBorderWidth = Double(preferencePanes.count) + 1
let segmentWidth = segmentSize.width * Double(preferencePanes.count) + segmentBorderWidth
let segmentHeight = segmentSize.height
segmentedControl.frame = CGRect(x: 0, y: 0, width: segmentWidth, height: segmentHeight)

View File

@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

View File

@ -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")
]
)
]
)

View File

@ -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.
```

View File

@ -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() {}
}

View File

@ -168,7 +168,7 @@ public class PopupCompositionBuffer: NSWindowController {
with: NSSize(width: 1600.0, height: 1600.0),
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 = max(22, rect.size.height)
if isTypingDirectionVertical {

View File

@ -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)
}

View File

@ -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
public enum TooltipColorState {

View File

@ -52,7 +52,7 @@ public class TooltipUI: NSWindowController {
public func show(
tooltip: String = "", at point: NSPoint,
bottomOutOfScreenAdjustmentHeight heightDelta: CGFloat,
bottomOutOfScreenAdjustmentHeight heightDelta: Double,
direction: NSAttributedTooltipTextView.writingDirection = .horizontal
) {
self.direction = direction
@ -118,7 +118,7 @@ public class TooltipUI: NSWindowController {
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 }
let windowSize = window.frame.size

View File

@ -73,7 +73,7 @@ public enum ChineseConverter {
///
/// - Parameter string: Text in Original Script.
/// - Returns: Text converted to Different Script.
public static func crossConvert(_ string: String) -> String? {
public static func crossConvert(_ string: String) -> String {
switch IMEApp.currentInputMode {
case .imeModeCHS:
return shared.convert(string, to: .zhHantTW)

View File

@ -208,7 +208,7 @@ extension StateData {
public var userPhraseDumpedConverted: String {
let pair = userPhraseKVPair
let text = ChineseConverter.crossConvert(pair.1) ?? ""
let text = ChineseConverter.crossConvert(pair.1)
let nerfedScore = ctlInputMethod.areWeNerfing && markedTargetExists ? " -114.514" : ""
let convertedMark = "#𝙃𝙪𝙢𝙖𝙣𝘾𝙝𝙚𝙘𝙠𝙍𝙚𝙦𝙪𝙞𝙧𝙚𝙙"
return "\(text) \(pair.0)\(nerfedScore)\t\(convertedMark)"

View File

@ -20,7 +20,7 @@ import Tekkon
/// KeyHandler
public protocol KeyHandlerDelegate {
var clientBundleIdentifier: String { get }
func ctlCandidate() -> ctlCandidateProtocol
func ctlCandidate() -> CtlCandidateProtocol
func candidateSelectionCalledByKeyHandler(at index: Int)
func performUserPhraseOperation(with state: IMEStateProtocol, addToFilter: Bool)
-> Bool

View File

@ -6,12 +6,19 @@
// marks, or product names of Contributor, except as required to fulfill notice
// 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 static let defaultIMKSelectionKey: [UInt16: String] = [
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 {
reloadData()
}
@ -40,26 +47,15 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
ofSize: 14, weight: .medium
)
public var candidateFont = NSFont.systemFont(ofSize: PrefMgr.shared.candidateListTextSize) {
public var candidateFont = NSFont.systemFont(ofSize: 16) {
didSet {
if #available(macOS 10.14, *) { setFontSize(candidateFont.pointSize) }
var attributes = attributes()
// FB11300759: Set "NSAttributedString.Key.font" doesn't work.
attributes?[NSAttributedString.Key.font] = candidateFont
if PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier {
switch IMEApp.currentInputMode {
case .imeModeCHS:
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
if #available(macOS 12.0, *) {
if useLangIdentifier {
attributes?[NSAttributedString.Key.languageIdentifier] = locale as AnyObject
}
}
setAttributes(attributes)
@ -159,17 +155,17 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol {
set { selectCandidate(withIdentifier: newValue) }
}
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) {
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: Double) {
DispatchQueue.main.async {
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
let windowSize = candidateFrame().size
var delta = heightDelta
var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast
for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) {
screenFrame = frame
break
@ -209,7 +205,7 @@ var currentTISInputSource: TISInputSource? {
// MARK: - Translating NumPad KeyCodes to Default IMK Candidate Selection KeyCodes.
extension ctlCandidateIMK {
extension CtlCandidateIMK {
public static func replaceNumPadKeyCodes(target event: NSEvent) -> NSEvent? {
let mapNumPadKeyCodeTranslation: [UInt16: UInt16] = [
83: 18, 84: 19, 85: 20, 86: 21, 87: 23, 88: 22, 89: 26, 91: 28, 92: 25,

View File

@ -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)
}
}

View File

@ -60,7 +60,7 @@ struct suiPrefPaneGeneral: View {
Picker(
"",
selection: $selCandidateUIFontSize.onChange {
PrefMgr.shared.candidateListTextSize = CGFloat(selCandidateUIFontSize)
PrefMgr.shared.candidateListTextSize = Double(selCandidateUIFontSize)
}
) {
Group {

View File

@ -53,7 +53,7 @@ extension ctlClientListMgr {
alert.addButton(withTitle: NSLocalizedString("Just Select", 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 contentSize = scrollview.contentSize
scrollview.borderType = .noBorder

View File

@ -12,7 +12,7 @@ import BookmarkManager
import IMKUtils
import Shared
private let kWindowTitleHeight: CGFloat = 78
private let kWindowTitleHeight: Double = 78
extension NSToolbarItem.Identifier {
fileprivate static let ofGeneral = NSToolbarItem.Identifier(rawValue: "tabGeneral")

View File

@ -13,6 +13,7 @@ import PopupCompositionBuffer
import Shared
import ShiftKeyUpChecker
import TooltipUI
import Voltaire
///
///
@ -28,8 +29,8 @@ class ctlInputMethod: IMKInputController {
static var areWeNerfing = false
///
static var ctlCandidateCurrent: ctlCandidateProtocol =
PrefMgr.shared.useIMKCandidateWindow ? ctlCandidateIMK.init(.horizontal) : ctlCandidateUniversal.init(.horizontal)
static var ctlCandidateCurrent: CtlCandidateProtocol =
PrefMgr.shared.useIMKCandidateWindow ? CtlCandidateIMK(.horizontal) : CtlCandidateUniversal(.horizontal)
///
static var tooltipInstance = TooltipUI()

View File

@ -18,7 +18,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
return client.bundleIdentifier() ?? ""
}
func ctlCandidate() -> ctlCandidateProtocol { ctlInputMethod.ctlCandidateCurrent }
func ctlCandidate() -> CtlCandidateProtocol { ctlInputMethod.ctlCandidateCurrent }
func candidateSelectionCalledByKeyHandler(at index: Int) {
candidateSelected(at: index)
@ -49,8 +49,16 @@ extension ctlInputMethod: KeyHandlerDelegate {
// MARK: - Candidate Controller Delegate
extension ctlInputMethod: ctlCandidateDelegate {
func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int {
extension ctlInputMethod: CtlCandidateDelegate {
func buzz() {
IMEApp.buzz()
}
func kanjiConversionIfRequired(_ target: String) -> String {
ChineseConverter.kanjiConversionIfRequired(target)
}
func candidateCountForController(_ controller: CtlCandidateProtocol) -> Int {
_ = controller //
if state.isCandidateContainer {
return state.candidates.count
@ -61,7 +69,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
///
/// - Parameter controller:
/// - Returns:
func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] {
func candidatesForController(_ controller: CtlCandidateProtocol) -> [(String, String)] {
_ = controller //
if state.isCandidateContainer {
return state.candidates
@ -69,7 +77,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
return .init()
}
func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)
func ctlCandidate(_ controller: CtlCandidateProtocol, candidateAtIndex index: Int)
-> (String, String)
{
_ = controller //

View File

@ -10,6 +10,7 @@
import NSAttributedTextView
import Shared
import Voltaire
// MARK: - Tooltip Display and Candidate Display Methods
@ -45,7 +46,7 @@ extension ctlInputMethod {
guard client() != nil else { return }
let lineHeightRect = lineHeightRect()
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 {
finalOrigin = NSPoint(
x: lineHeightRect.origin.x + lineHeightRect.size.width + 5, y: lineHeightRect.origin.y
@ -79,7 +80,7 @@ extension ctlInputMethod {
}
if isVerticalTyping { return true }
// 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 =
PrefMgr.shared.useIMKCandidateWindow
? ctlCandidateIMK.init(candidateLayout) : ctlCandidateUniversal.init(candidateLayout)
? CtlCandidateIMK(candidateLayout) : CtlCandidateUniversal(candidateLayout)
// set the attributes for the candidate panel (which uses NSAttributedString)
let textSize = PrefMgr.shared.candidateListTextSize
let minimumKeyLabelSize: Double = 10
let keyLabelSize = max(textSize / 2, minimumKeyLabelSize)
func labelFont(name: String?, size: CGFloat) -> NSFont {
func labelFont(name: String?, size: Double) -> NSFont {
if let name = name {
return NSFont(name: name, size: size) ?? NSFont.systemFont(ofSize: size)
}
@ -137,11 +138,24 @@ extension ctlInputMethod {
}
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()
if #available(macOS 10.14, *) {
// Spotlight IMK
if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK {
if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? CtlCandidateIMK {
while ctlCandidateCurrent.windowLevel() <= client.windowLevel() {
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
/// 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.
/// 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.
/// 3) Compile the target "vChewingInstaller", run it. It will install the input method into
/// "~/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.
/// 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.
static func candidateFont(name: String? = nil, size: CGFloat) -> NSFont {
static func candidateFont(name: String? = nil, size: Double) -> NSFont {
let finalReturnFont: NSFont =
{
switch IMEApp.currentInputMode {

View File

@ -143,8 +143,8 @@ extension ctlInputMethod {
// interpretKeyEvents()
// - imkCandidates.interpretKeyEvents()
// - delegate ctlInputMethod KeyHandler
if let imkCandidates = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK, imkCandidates.visible {
let event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal
if let imkCandidates = ctlInputMethod.ctlCandidateCurrent as? CtlCandidateIMK, imkCandidates.visible {
let event: NSEvent = CtlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal
// Shift+Enter delegate keyHandler
// 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,
let newEvent = event.reinitiate(modifierFlags: [], characters: newChar)
{
@ -177,7 +177,7 @@ extension ctlInputMethod {
private func imkCandidatesEventSubHandler(event: NSEvent) -> Bool {
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) {
return commonEventHandler(event)
} else if event.isSymbolMenuPhysicalKey {
@ -200,7 +200,7 @@ extension ctlInputMethod {
}
return true
} else {
if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode] {
if let newChar = CtlCandidateIMK.defaultIMKSelectionKey[event.keyCode] {
/// KeyCode NSEvent Character
/// IMK
let newEvent = event.reinitiate(characters: newChar)

View File

@ -15,12 +15,10 @@
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 */; };
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 */; };
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; };
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 */; };
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
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 */; };
5B963CA828D5DB1400DCEE88 /* PrefMgr_Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B963CA728D5DB1400DCEE88 /* PrefMgr_Core.swift */; };
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 */; };
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.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 */; };
5BFC63CF28D4ACA3004A77B7 /* LangModelAssembly in Frameworks */ = {isa = PBXBuildFile; productRef = 5BFC63CE28D4ACA3004A77B7 /* LangModelAssembly */; };
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 */; };
6A225A1F23679F2600F685C6 /* NotarizedArchives in Resources */ = {isa = PBXBuildFile; fileRef = 6A225A1E23679F2600F685C6 /* NotarizedArchives */; };
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>"; };
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>"; };
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>"; };
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>"; };
@ -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; };
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; };
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>"; };
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; };
@ -224,6 +222,8 @@
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>"; };
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; };
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; };
@ -298,7 +298,7 @@
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>"; };
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>"; };
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>"; };
@ -357,7 +357,9 @@
5B40113928D7050D00A9D4CB /* Shared in Frameworks */,
5BFC63C628D48806004A77B7 /* NSAttributedTextView in Frameworks */,
5BDB7A3B28D4824A001AC277 /* FolderMonitor in Frameworks */,
5BA8C30328DF0360004C5CC4 /* CandidateWindow in Frameworks */,
5BC5E01E28DDE4770094E427 /* NotifierUI in Frameworks */,
5BA8C30528DF0364004C5CC4 /* Voltaire in Frameworks */,
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -468,9 +470,7 @@
5B62A33E27AE7CD900A19448 /* CandidateUI */ = {
isa = PBXGroup;
children = (
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */,
5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */,
5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */,
5BFDF010289635C100417BBC /* IMKCandidatesImpl.swift */,
);
path = CandidateUI;
sourceTree = "<group>";
@ -565,9 +565,11 @@
5BDB7A3128D47587001AC277 /* DanielGalasko_FolderMonitor */,
5BFC63C428D487AE004A77B7 /* Fuziki_NSAttributedTextView */,
5BDB7A3028D47587001AC277 /* Jad_BookmarkManager */,
5BA8C30028DEFE4F004C5CC4 /* OpenVanilla_Voltaire */,
5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */,
5BDB7A3528D47587001AC277 /* RMJay_LineReader */,
5BDB7A2F28D47587001AC277 /* Sindresorhus_Preferences */,
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */,
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */,
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */,
5BFC63C728D49511004A77B7 /* vChewing_IMKUtils */,
@ -780,6 +782,8 @@
5BC5E01D28DDE4770094E427 /* NotifierUI */,
5BC5E02028DDEFE00094E427 /* TooltipUI */,
5BC5E02328DE07860094E427 /* PopupCompositionBuffer */,
5BA8C30228DF0360004C5CC4 /* CandidateWindow */,
5BA8C30428DF0364004C5CC4 /* Voltaire */,
);
productName = vChewing;
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */;
@ -1067,13 +1071,11 @@
5BAEFAD028012565001F42C9 /* LMMgr.swift in Sources */,
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */,
5BCCAFF828DB19A300AB1B27 /* PrefMgr_Extension.swift in Sources */,
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
5B8457A12871ADBE00C93B01 /* ChineseConverterBridge.swift in Sources */,
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */,
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
5BFDF011289635C100417BBC /* IMKCandidatesImpl.swift in Sources */,
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */,
5B00FA0C28DEC17200F6D436 /* ctlInputMethod_IMKCandidatesData.swift in Sources */,
@ -1774,6 +1776,14 @@
isa = XCSwiftPackageProductDependency;
productName = PinyinPhonaConverter;
};
5BA8C30228DF0360004C5CC4 /* CandidateWindow */ = {
isa = XCSwiftPackageProductDependency;
productName = CandidateWindow;
};
5BA8C30428DF0364004C5CC4 /* Voltaire */ = {
isa = XCSwiftPackageProductDependency;
productName = Voltaire;
};
5BC5E01D28DDE4770094E427 /* NotifierUI */ = {
isa = XCSwiftPackageProductDependency;
productName = NotifierUI;