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 fontSize: CGFloat = NSFont.systemFontSize {
|
||||
public var fontSize: Double = NSFont.systemFontSize {
|
||||
didSet {
|
||||
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 選字窗隔的橫向版本與縱向版本合併到同一個型別實體內。
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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),
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
public enum TooltipColorState {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
|
@ -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(
|
||||
"",
|
||||
selection: $selCandidateUIFontSize.onChange {
|
||||
PrefMgr.shared.candidateListTextSize = CGFloat(selCandidateUIFontSize)
|
||||
PrefMgr.shared.candidateListTextSize = Double(selCandidateUIFontSize)
|
||||
}
|
||||
) {
|
||||
Group {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 // 防止格式整理工具毀掉與此對應的參數。
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue