From e9137b9e53c57fcb78697681b4aa52dac0a7d15d Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 24 Sep 2022 14:40:43 +0800 Subject: [PATCH] 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. --- .../NSAttributedTextView.swift | 2 +- Packages/OpenVanilla_Voltaire/.gitignore | 9 + Packages/OpenVanilla_Voltaire/Package.swift | 28 +++ Packages/OpenVanilla_Voltaire/README.md | 48 +++++ .../Voltaire/CtlCandidateUniversal.swift | 134 ++++++-------- .../Source/Preferences/Container.swift | 6 +- .../SegmentedControlStyleViewController.swift | 4 +- Packages/vChewing_CandidateWindow/.gitignore | 9 + .../vChewing_CandidateWindow/Package.swift | 26 +++ Packages/vChewing_CandidateWindow/README.md | 13 ++ .../CandidateWindow/CtlCandidate.swift | 147 +++++++++++++++ .../PopupCompositionBuffer.swift | 2 +- .../Protocols/CtlCandidateProtocol.swift | 43 +++++ .../Sources/Shared/Shared.swift | 17 ++ .../Sources/TooltipUI/TooltipUI.swift | 4 +- Source/Modules/ChineseConverterBridge.swift | 2 +- Source/Modules/IMEStateData.swift | 2 +- Source/Modules/KeyHandler_Core.swift | 2 +- ...idateIMK.swift => IMKCandidatesImpl.swift} | 38 ++-- .../UIModules/CandidateUI/ctlCandidate.swift | 173 ------------------ .../UIModules/PrefUI/suiPrefPaneGeneral.swift | 2 +- .../WindowControllers/ctlClientListMgr.swift | 2 +- .../WindowControllers/ctlPrefWindow.swift | 2 +- Source/Modules/ctlInputMethod_Core.swift | 5 +- Source/Modules/ctlInputMethod_Delegates.swift | 18 +- .../ctlInputMethod_HandleDisplay.swift | 28 ++- .../Modules/ctlInputMethod_HandleEvent.swift | 10 +- vChewing.xcodeproj/project.pbxproj | 34 ++-- 28 files changed, 489 insertions(+), 321 deletions(-) create mode 100644 Packages/OpenVanilla_Voltaire/.gitignore create mode 100644 Packages/OpenVanilla_Voltaire/Package.swift create mode 100644 Packages/OpenVanilla_Voltaire/README.md rename Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift => Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift (85%) create mode 100644 Packages/vChewing_CandidateWindow/.gitignore create mode 100644 Packages/vChewing_CandidateWindow/Package.swift create mode 100644 Packages/vChewing_CandidateWindow/README.md create mode 100644 Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift create mode 100644 Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift rename Source/Modules/UIModules/CandidateUI/{ctlCandidateIMK.swift => IMKCandidatesImpl.swift} (87%) delete mode 100644 Source/Modules/UIModules/CandidateUI/ctlCandidate.swift diff --git a/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift b/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift index b11cec88..7978874c 100644 --- a/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift +++ b/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift @@ -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) } diff --git a/Packages/OpenVanilla_Voltaire/.gitignore b/Packages/OpenVanilla_Voltaire/.gitignore new file mode 100644 index 00000000..3b298120 --- /dev/null +++ b/Packages/OpenVanilla_Voltaire/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Packages/OpenVanilla_Voltaire/Package.swift b/Packages/OpenVanilla_Voltaire/Package.swift new file mode 100644 index 00000000..2bb82029 --- /dev/null +++ b/Packages/OpenVanilla_Voltaire/Package.swift @@ -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"), + ] + ) + ] +) diff --git a/Packages/OpenVanilla_Voltaire/README.md b/Packages/OpenVanilla_Voltaire/README.md new file mode 100644 index 00000000..933d23a4 --- /dev/null +++ b/Packages/OpenVanilla_Voltaire/README.md @@ -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. +``` diff --git a/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift b/Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift similarity index 85% rename from Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift rename to Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift index 330110df..ae5b6ddc 100644 --- a/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift +++ b/Packages/OpenVanilla_Voltaire/Sources/Voltaire/CtlCandidateUniversal.swift @@ -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.. 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) } diff --git a/Packages/Sindresorhus_Preferences/Source/Preferences/Container.swift b/Packages/Sindresorhus_Preferences/Source/Preferences/Container.swift index 39bcd60f..c99bce24 100755 --- a/Packages/Sindresorhus_Preferences/Source/Preferences/Container.swift +++ b/Packages/Sindresorhus_Preferences/Source/Preferences/Container.swift @@ -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)) } } } diff --git a/Packages/Sindresorhus_Preferences/Source/Preferences/SegmentedControlStyleViewController.swift b/Packages/Sindresorhus_Preferences/Source/Preferences/SegmentedControlStyleViewController.swift index 0aebd1d1..13119ad4 100755 --- a/Packages/Sindresorhus_Preferences/Source/Preferences/SegmentedControlStyleViewController.swift +++ b/Packages/Sindresorhus_Preferences/Source/Preferences/SegmentedControlStyleViewController.swift @@ -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) diff --git a/Packages/vChewing_CandidateWindow/.gitignore b/Packages/vChewing_CandidateWindow/.gitignore new file mode 100644 index 00000000..3b298120 --- /dev/null +++ b/Packages/vChewing_CandidateWindow/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Packages/vChewing_CandidateWindow/Package.swift b/Packages/vChewing_CandidateWindow/Package.swift new file mode 100644 index 00000000..0ca9b8be --- /dev/null +++ b/Packages/vChewing_CandidateWindow/Package.swift @@ -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") + ] + ) + ] +) diff --git a/Packages/vChewing_CandidateWindow/README.md b/Packages/vChewing_CandidateWindow/README.md new file mode 100644 index 00000000..628d5b76 --- /dev/null +++ b/Packages/vChewing_CandidateWindow/README.md @@ -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. +``` diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift new file mode 100644 index 00000000..ef5509c9 --- /dev/null +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift @@ -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() {} +} diff --git a/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift b/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift index c06ee064..0cb5fb85 100644 --- a/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift +++ b/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift @@ -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 { diff --git a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift new file mode 100644 index 00000000..64f3ac52 --- /dev/null +++ b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift @@ -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) +} diff --git a/Packages/vChewing_Shared/Sources/Shared/Shared.swift b/Packages/vChewing_Shared/Sources/Shared/Shared.swift index d7ba66e8..78c63280 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Shared.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Shared.swift @@ -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 { diff --git a/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift b/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift index 9e827049..af044ce6 100644 --- a/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift +++ b/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift @@ -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 diff --git a/Source/Modules/ChineseConverterBridge.swift b/Source/Modules/ChineseConverterBridge.swift index 6952ffff..a0223b77 100644 --- a/Source/Modules/ChineseConverterBridge.swift +++ b/Source/Modules/ChineseConverterBridge.swift @@ -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) diff --git a/Source/Modules/IMEStateData.swift b/Source/Modules/IMEStateData.swift index 8fa4ccfc..57035f74 100644 --- a/Source/Modules/IMEStateData.swift +++ b/Source/Modules/IMEStateData.swift @@ -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)" diff --git a/Source/Modules/KeyHandler_Core.swift b/Source/Modules/KeyHandler_Core.swift index a0c44473..163e5d62 100644 --- a/Source/Modules/KeyHandler_Core.swift +++ b/Source/Modules/KeyHandler_Core.swift @@ -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 diff --git a/Source/Modules/UIModules/CandidateUI/ctlCandidateIMK.swift b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift similarity index 87% rename from Source/Modules/UIModules/CandidateUI/ctlCandidateIMK.swift rename to Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift index 8ad1fa85..e44d2e84 100644 --- a/Source/Modules/UIModules/CandidateUI/ctlCandidateIMK.swift +++ b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift @@ -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, diff --git a/Source/Modules/UIModules/CandidateUI/ctlCandidate.swift b/Source/Modules/UIModules/CandidateUI/ctlCandidate.swift deleted file mode 100644 index 49479bb7..00000000 --- a/Source/Modules/UIModules/CandidateUI/ctlCandidate.swift +++ /dev/null @@ -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) - } -} diff --git a/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift b/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift index 14acbd93..9d51f20e 100644 --- a/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift +++ b/Source/Modules/UIModules/PrefUI/suiPrefPaneGeneral.swift @@ -60,7 +60,7 @@ struct suiPrefPaneGeneral: View { Picker( "", selection: $selCandidateUIFontSize.onChange { - PrefMgr.shared.candidateListTextSize = CGFloat(selCandidateUIFontSize) + PrefMgr.shared.candidateListTextSize = Double(selCandidateUIFontSize) } ) { Group { diff --git a/Source/Modules/WindowControllers/ctlClientListMgr.swift b/Source/Modules/WindowControllers/ctlClientListMgr.swift index 787bec77..019e0c71 100644 --- a/Source/Modules/WindowControllers/ctlClientListMgr.swift +++ b/Source/Modules/WindowControllers/ctlClientListMgr.swift @@ -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 diff --git a/Source/Modules/WindowControllers/ctlPrefWindow.swift b/Source/Modules/WindowControllers/ctlPrefWindow.swift index ab5d3749..37856bdf 100644 --- a/Source/Modules/WindowControllers/ctlPrefWindow.swift +++ b/Source/Modules/WindowControllers/ctlPrefWindow.swift @@ -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") diff --git a/Source/Modules/ctlInputMethod_Core.swift b/Source/Modules/ctlInputMethod_Core.swift index ed3d05f5..f71f9179 100644 --- a/Source/Modules/ctlInputMethod_Core.swift +++ b/Source/Modules/ctlInputMethod_Core.swift @@ -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() diff --git a/Source/Modules/ctlInputMethod_Delegates.swift b/Source/Modules/ctlInputMethod_Delegates.swift index 6e1c45cf..88c245ca 100644 --- a/Source/Modules/ctlInputMethod_Delegates.swift +++ b/Source/Modules/ctlInputMethod_Delegates.swift @@ -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 // 防止格式整理工具毀掉與此對應的參數。 diff --git a/Source/Modules/ctlInputMethod_HandleDisplay.swift b/Source/Modules/ctlInputMethod_HandleDisplay.swift index bd7246d9..bb27fe89 100644 --- a/Source/Modules/ctlInputMethod_HandleDisplay.swift +++ b/Source/Modules/ctlInputMethod_HandleDisplay.swift @@ -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 { diff --git a/Source/Modules/ctlInputMethod_HandleEvent.swift b/Source/Modules/ctlInputMethod_HandleEvent.swift index 15c98fe6..a3e3218d 100644 --- a/Source/Modules/ctlInputMethod_HandleEvent.swift +++ b/Source/Modules/ctlInputMethod_HandleEvent.swift @@ -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) diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 31c495aa..6808a3c8 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -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 = ""; }; 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleDisplay.swift; sourceTree = ""; }; 5B21176F28753B9D000443A9 /* ctlInputMethod_Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Delegates.swift; sourceTree = ""; }; - 5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateUniversal.swift; sourceTree = ""; }; 5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = ""; }; 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 = ""; }; @@ -206,7 +205,6 @@ 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = ""; }; 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; - 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_HandleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_HandleEvent.swift; sourceTree = ""; }; 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 = ""; }; 5B963CA728D5DB1400DCEE88 /* PrefMgr_Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefMgr_Core.swift; sourceTree = ""; }; 5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PinyinPhonaConverter; path = Packages/vChewing_PinyinPhonaConverter; sourceTree = ""; }; + 5BA8C30028DEFE4F004C5CC4 /* OpenVanilla_Voltaire */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenVanilla_Voltaire; path = Packages/OpenVanilla_Voltaire; sourceTree = ""; }; + 5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CandidateWindow; path = Packages/vChewing_CandidateWindow; sourceTree = ""; }; 5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneGeneral.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = suiPrefPaneKeyboard.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlPrefUI.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; @@ -298,7 +298,7 @@ 5BFC63C728D49511004A77B7 /* vChewing_IMKUtils */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_IMKUtils; path = Packages/vChewing_IMKUtils; sourceTree = ""; }; 5BFC63CA28D49B2B004A77B7 /* vChewing_UpdateSputnik */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_UpdateSputnik; path = Packages/vChewing_UpdateSputnik; sourceTree = ""; }; 5BFC63CD28D4AC98004A77B7 /* vChewing_LangModelAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_LangModelAssembly; path = Packages/vChewing_LangModelAssembly; sourceTree = ""; }; - 5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateIMK.swift; sourceTree = ""; }; + 5BFDF010289635C100417BBC /* IMKCandidatesImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKCandidatesImpl.swift; sourceTree = ""; }; 5BFDF48C27B51867009523B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = ""; }; 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 = ""; }; @@ -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 = ""; @@ -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;