Proj // Swift-format tweaks.

This commit is contained in:
ShikiSuen 2022-04-18 09:50:42 +08:00
parent 6d7f260991
commit c8f83a28aa
59 changed files with 837 additions and 593 deletions

View File

@ -31,10 +31,12 @@ extension String {
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
do { do {
let regex = try NSRegularExpression( let regex = try NSRegularExpression(
pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]) pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]
let range = NSRange(self.startIndex..., in: self) )
let range = NSRange(startIndex..., in: self)
self = regex.stringByReplacingMatches( self = regex.stringByReplacingMatches(
in: self, options: [], range: range, withTemplate: replaceWith) in: self, options: [], range: range, withTemplate: replaceWith
)
} catch { return } } catch { return }
} }
} }
@ -59,9 +61,11 @@ if CommandLine.arguments.count == 3 {
} }
strXcodeProjContent.regReplace( strXcodeProjContent.regReplace(
pattern: #"CURRENT_PROJECT_VERSION = .*$"#, replaceWith: "CURRENT_PROJECT_VERSION = " + verBuild + ";") pattern: #"CURRENT_PROJECT_VERSION = .*$"#, replaceWith: "CURRENT_PROJECT_VERSION = " + verBuild + ";"
)
strXcodeProjContent.regReplace( strXcodeProjContent.regReplace(
pattern: #"MARKETING_VERSION = .*$"#, replaceWith: "MARKETING_VERSION = " + verMarket + ";") pattern: #"MARKETING_VERSION = .*$"#, replaceWith: "MARKETING_VERSION = " + verMarket + ";"
)
do { do {
try strXcodeProjContent.write(to: URL(fileURLWithPath: dirXcodeProjectFile), atomically: false, encoding: .utf8) try strXcodeProjContent.write(to: URL(fileURLWithPath: dirXcodeProjectFile), atomically: false, encoding: .utf8)
} catch { } catch {
@ -81,5 +85,4 @@ if CommandLine.arguments.count == 3 {
theDictionary?.setValue(verMarket, forKeyPath: "CFBundleShortVersionString") theDictionary?.setValue(verMarket, forKeyPath: "CFBundleShortVersionString")
theDictionary?.write(toFile: dirUpdateInfoPlist, atomically: true) theDictionary?.write(toFile: dirUpdateInfoPlist, atomically: true)
NSLog(" - 更新用通知 plist 版本資訊更新完成:\(verMarket) \(verBuild)") NSLog(" - 更新用通知 plist 版本資訊更新完成:\(verMarket) \(verBuild)")
} }

View File

@ -27,20 +27,24 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Foundation import Foundation
// MARK: - // MARK: -
extension String { extension String {
fileprivate mutating func regReplace(pattern: String, replaceWith: String = "") { fileprivate mutating func regReplace(pattern: String, replaceWith: String = "") {
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
do { do {
let regex = try NSRegularExpression( let regex = try NSRegularExpression(
pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]) pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]
let range = NSRange(self.startIndex..., in: self) )
let range = NSRange(startIndex..., in: self)
self = regex.stringByReplacingMatches( self = regex.stringByReplacingMatches(
in: self, options: [], range: range, withTemplate: replaceWith) in: self, options: [], range: range, withTemplate: replaceWith
)
} catch { return } } catch { return }
} }
} }
// MARK: - // MARK: -
// Ref: https://stackoverflow.com/a/32581409/4162914 // Ref: https://stackoverflow.com/a/32581409/4162914
extension Float { extension Float {
fileprivate func rounded(toPlaces places: Int) -> Float { fileprivate func rounded(toPlaces places: Int) -> Float {
@ -50,6 +54,7 @@ extension Float {
} }
// MARK: - // MARK: -
// Ref: https://stackoverflow.com/a/41581695/4162914 // Ref: https://stackoverflow.com/a/41581695/4162914
precedencegroup ExponentiationPrecedence { precedencegroup ExponentiationPrecedence {
associativity: right associativity: right
@ -59,11 +64,11 @@ precedencegroup ExponentiationPrecedence {
infix operator **: ExponentiationPrecedence infix operator **: ExponentiationPrecedence
func ** (_ base: Double, _ exp: Double) -> Double { func ** (_ base: Double, _ exp: Double) -> Double {
return pow(base, exp) pow(base, exp)
} }
func ** (_ base: Float, _ exp: Float) -> Float { func ** (_ base: Float, _ exp: Float) -> Float {
return pow(base, exp) pow(base, exp)
} }
// MARK: - // MARK: -
@ -101,7 +106,7 @@ private let urlOutputCHT: String = "./data-cht.txt"
func rawDictForPhrases(isCHS: Bool) -> [Entry] { func rawDictForPhrases(isCHS: Bool) -> [Entry] {
var arrEntryRAW: [Entry] = [] var arrEntryRAW: [Entry] = []
var strRAW: String = "" var strRAW = ""
let urlCustom: String = isCHS ? urlCHSforCustom : urlCHTforCustom let urlCustom: String = isCHS ? urlCHSforCustom : urlCHTforCustom
let urlMCBP: String = isCHS ? urlCHSforMCBP : urlCHTforMCBP let urlMCBP: String = isCHS ? urlCHSforMCBP : urlCHTforMCBP
let urlMOE: String = isCHS ? urlCHSforMOE : urlCHTforMOE let urlMOE: String = isCHS ? urlCHSforMOE : urlCHTforMOE
@ -136,7 +141,7 @@ func rawDictForPhrases(isCHS: Bool) -> [Entry] {
for lineData in arrData { for lineData in arrData {
// //
let arrLineData = lineData.components(separatedBy: " ") let arrLineData = lineData.components(separatedBy: " ")
var varLineDataProcessed: String = "" var varLineDataProcessed = ""
var count = 0 var count = 0
for currentCell in arrLineData { for currentCell in arrLineData {
count += 1 count += 1
@ -165,9 +170,10 @@ func rawDictForPhrases(isCHS: Bool) -> [Entry] {
} }
if phrase != "" { // if phrase != "" { //
arrEntryRAW += [ arrEntryRAW += [
Entry.init( Entry(
valPhone: phone, valPhrase: phrase, valWeight: 0.0, valPhone: phone, valPhrase: phrase, valWeight: 0.0,
valCount: occurrence) valCount: occurrence
)
] ]
} }
} }
@ -179,7 +185,7 @@ func rawDictForPhrases(isCHS: Bool) -> [Entry] {
func rawDictForKanjis(isCHS: Bool) -> [Entry] { func rawDictForKanjis(isCHS: Bool) -> [Entry] {
var arrEntryRAW: [Entry] = [] var arrEntryRAW: [Entry] = []
var strRAW: String = "" var strRAW = ""
let i18n: String = isCHS ? "簡體中文" : "繁體中文" let i18n: String = isCHS ? "簡體中文" : "繁體中文"
// //
do { do {
@ -201,7 +207,7 @@ func rawDictForKanjis(isCHS: Bool) -> [Entry] {
// //
let arrData = Array( let arrData = Array(
NSOrderedSet(array: strRAW.components(separatedBy: "\n")).array as! [String]) NSOrderedSet(array: strRAW.components(separatedBy: "\n")).array as! [String])
var varLineData: String = "" var varLineData = ""
for lineData in arrData { for lineData in arrData {
// 1,2,4 1,3,4 // 1,2,4 1,3,4
let varLineDataPre = lineData.components(separatedBy: " ").prefix(isCHS ? 2 : 1) let varLineDataPre = lineData.components(separatedBy: " ").prefix(isCHS ? 2 : 1)
@ -212,7 +218,7 @@ func rawDictForKanjis(isCHS: Bool) -> [Entry] {
separator: "\t") separator: "\t")
varLineData = varLineDataPre + "\t" + varLineDataPost varLineData = varLineDataPre + "\t" + varLineDataPost
let arrLineData = varLineData.components(separatedBy: " ") let arrLineData = varLineData.components(separatedBy: " ")
var varLineDataProcessed: String = "" var varLineDataProcessed = ""
var count = 0 var count = 0
for currentCell in arrLineData { for currentCell in arrLineData {
count += 1 count += 1
@ -241,9 +247,10 @@ func rawDictForKanjis(isCHS: Bool) -> [Entry] {
} }
if phrase != "" { // if phrase != "" { //
arrEntryRAW += [ arrEntryRAW += [
Entry.init( Entry(
valPhone: phone, valPhrase: phrase, valWeight: 0.0, valPhone: phone, valPhrase: phrase, valWeight: 0.0,
valCount: occurrence) valCount: occurrence
)
] ]
} }
} }
@ -255,7 +262,7 @@ func rawDictForKanjis(isCHS: Bool) -> [Entry] {
func rawDictForNonKanjis(isCHS: Bool) -> [Entry] { func rawDictForNonKanjis(isCHS: Bool) -> [Entry] {
var arrEntryRAW: [Entry] = [] var arrEntryRAW: [Entry] = []
var strRAW: String = "" var strRAW = ""
let i18n: String = isCHS ? "簡體中文" : "繁體中文" let i18n: String = isCHS ? "簡體中文" : "繁體中文"
// //
do { do {
@ -279,14 +286,14 @@ func rawDictForNonKanjis(isCHS: Bool) -> [Entry] {
// //
let arrData = Array( let arrData = Array(
NSOrderedSet(array: strRAW.components(separatedBy: "\n")).array as! [String]) NSOrderedSet(array: strRAW.components(separatedBy: "\n")).array as! [String])
var varLineData: String = "" var varLineData = ""
for lineData in arrData { for lineData in arrData {
varLineData = lineData varLineData = lineData
// //
varLineData = varLineData.components(separatedBy: " ").prefix(3).joined( varLineData = varLineData.components(separatedBy: " ").prefix(3).joined(
separator: "\t") // separator: "\t") //
let arrLineData = varLineData.components(separatedBy: " ") let arrLineData = varLineData.components(separatedBy: " ")
var varLineDataProcessed: String = "" var varLineDataProcessed = ""
var count = 0 var count = 0
for currentCell in arrLineData { for currentCell in arrLineData {
count += 1 count += 1
@ -315,9 +322,10 @@ func rawDictForNonKanjis(isCHS: Bool) -> [Entry] {
} }
if phrase != "" { // if phrase != "" { //
arrEntryRAW += [ arrEntryRAW += [
Entry.init( Entry(
valPhone: phone, valPhrase: phrase, valWeight: 0.0, valPhone: phone, valPhrase: phrase, valWeight: 0.0,
valCount: occurrence) valCount: occurrence
)
] ]
} }
} }
@ -356,16 +364,17 @@ func weightAndSort(_ arrStructUncalculated: [Entry], isCHS: Bool) -> [Entry] {
} }
let weightRounded: Float = weight.rounded(toPlaces: 3) // let weightRounded: Float = weight.rounded(toPlaces: 3) //
arrStructCalculated += [ arrStructCalculated += [
Entry.init( Entry(
valPhone: entry.valPhone, valPhrase: entry.valPhrase, valWeight: weightRounded, valPhone: entry.valPhone, valPhrase: entry.valPhrase, valWeight: weightRounded,
valCount: entry.valCount) valCount: entry.valCount
)
] ]
} }
NSLog(" - \(i18n): 成功計算權重。") NSLog(" - \(i18n): 成功計算權重。")
// ========================================== // ==========================================
// //
let arrStructSorted: [Entry] = arrStructCalculated.sorted(by: { (lhs, rhs) -> Bool in let arrStructSorted: [Entry] = arrStructCalculated.sorted(by: { lhs, rhs -> Bool in
return (lhs.valPhone, rhs.valCount) < (rhs.valPhone, lhs.valCount) (lhs.valPhone, rhs.valCount) < (rhs.valPhone, lhs.valCount)
}) })
NSLog(" - \(i18n): 排序整理完畢,準備編譯要寫入的檔案內容。") NSLog(" - \(i18n): 排序整理完畢,準備編譯要寫入的檔案內容。")
return arrStructSorted return arrStructSorted
@ -406,6 +415,7 @@ func fileOutput(isCHS: Bool) {
} }
// MARK: - // MARK: -
func main() { func main() {
NSLog("// 準備編譯繁體中文核心語料檔案。") NSLog("// 準備編譯繁體中文核心語料檔案。")
fileOutput(isCHS: false) fileOutput(isCHS: false)

View File

@ -31,7 +31,8 @@ private let kTargetType = "app"
private let kTargetBundle = "vChewing.app" private let kTargetBundle = "vChewing.app"
private let urlDestinationPartial = FileManager.default.urls( private let urlDestinationPartial = FileManager.default.urls(
for: .inputMethodsDirectory, in: .userDomainMask)[0] for: .inputMethodsDirectory, in: .userDomainMask
)[0]
private let urlTargetPartial = urlDestinationPartial.appendingPathComponent(kTargetBundle) private let urlTargetPartial = urlDestinationPartial.appendingPathComponent(kTargetBundle)
private let urlTargetFullBinPartial = urlTargetPartial.appendingPathComponent("Contents/MacOS/") private let urlTargetFullBinPartial = urlTargetPartial.appendingPathComponent("Contents/MacOS/")
.appendingPathComponent(kTargetBin) .appendingPathComponent(kTargetBin)
@ -46,12 +47,12 @@ private let kTranslocationRemovalDeadline: TimeInterval = 60.0
@NSApplicationMain @NSApplicationMain
@objc(AppDelegate) @objc(AppDelegate)
class AppDelegate: NSWindowController, NSApplicationDelegate { class AppDelegate: NSWindowController, NSApplicationDelegate {
@IBOutlet weak private var installButton: NSButton! @IBOutlet private var installButton: NSButton!
@IBOutlet weak private var cancelButton: NSButton! @IBOutlet private var cancelButton: NSButton!
@IBOutlet weak private var progressSheet: NSWindow! @IBOutlet private var progressSheet: NSWindow!
@IBOutlet weak private var progressIndicator: NSProgressIndicator! @IBOutlet private var progressIndicator: NSProgressIndicator!
@IBOutlet weak private var appVersionLabel: NSTextField! @IBOutlet private var appVersionLabel: NSTextField!
@IBOutlet weak private var appCopyrightLabel: NSTextField! @IBOutlet private var appCopyrightLabel: NSTextField!
@IBOutlet private var appEULAContent: NSTextView! @IBOutlet private var appEULAContent: NSTextView!
private var archiveUtil: ArchiveUtil? private var archiveUtil: ArchiveUtil?
@ -69,7 +70,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
alert.runModal() alert.runModal()
} }
func applicationDidFinishLaunching(_ notification: Notification) { func applicationDidFinishLaunching(_: Notification) {
guard guard
let installingVersion = Bundle.main.infoDictionary?[kCFBundleVersionKey as String] let installingVersion = Bundle.main.infoDictionary?[kCFBundleVersionKey as String]
as? String, as? String,
@ -96,11 +97,13 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
appEULAContent.string = eulaContent appEULAContent.string = eulaContent
} }
appVersionLabel.stringValue = String( appVersionLabel.stringValue = String(
format: "%@ Build %@", versionString, installingVersion) format: "%@ Build %@", versionString, installingVersion
)
window?.title = String( window?.title = String(
format: NSLocalizedString("%@ (for version %@, r%@)", comment: ""), window?.title ?? "", format: NSLocalizedString("%@ (for version %@, r%@)", comment: ""), window?.title ?? "",
versionString, installingVersion) versionString, installingVersion
)
window?.standardWindowButton(.closeButton)?.isHidden = true window?.standardWindowButton(.closeButton)?.isHidden = true
window?.standardWindowButton(.miniaturizeButton)?.isHidden = true window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
window?.standardWindowButton(.zoomButton)?.isHidden = true window?.standardWindowButton(.zoomButton)?.isHidden = true
@ -130,7 +133,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
@IBAction func agreeAndInstallAction(_ sender: AnyObject) { @IBAction func agreeAndInstallAction(_: AnyObject) {
cancelButton.isEnabled = false cancelButton.isEnabled = false
installButton.isEnabled = false installButton.isEnabled = false
removeThenInstallInputMethod() removeThenInstallInputMethod()
@ -154,7 +157,8 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
== false == false
{ {
installInputMethod( installInputMethod(
previousExists: false, previousVersionNotFullyDeactivatedWarning: false) previousExists: false, previousVersionNotFullyDeactivatedWarning: false
)
return return
} }
@ -194,11 +198,13 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
if returnCode == .continue { if returnCode == .continue {
self.installInputMethod( self.installInputMethod(
previousExists: true, previousExists: true,
previousVersionNotFullyDeactivatedWarning: false) previousVersionNotFullyDeactivatedWarning: false
)
} else { } else {
self.installInputMethod( self.installInputMethod(
previousExists: true, previousExists: true,
previousVersionNotFullyDeactivatedWarning: true) previousVersionNotFullyDeactivatedWarning: true
)
} }
} }
} }
@ -206,15 +212,17 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
translocationRemovalStartTime = Date() translocationRemovalStartTime = Date()
Timer.scheduledTimer( Timer.scheduledTimer(
timeInterval: kTranslocationRemovalTickInterval, target: self, timeInterval: kTranslocationRemovalTickInterval, target: self,
selector: #selector(timerTick(_:)), userInfo: nil, repeats: true) selector: #selector(timerTick(_:)), userInfo: nil, repeats: true
)
} else { } else {
installInputMethod( installInputMethod(
previousExists: false, previousVersionNotFullyDeactivatedWarning: false) previousExists: false, previousVersionNotFullyDeactivatedWarning: false
)
} }
} }
func installInputMethod( func installInputMethod(
previousExists: Bool, previousVersionNotFullyDeactivatedWarning warning: Bool previousExists _: Bool, previousVersionNotFullyDeactivatedWarning warning: Bool
) { ) {
guard guard
let targetBundle = archiveUtil?.unzipNotarizedArchive() let targetBundle = archiveUtil?.unzipNotarizedArchive()
@ -234,7 +242,8 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
runAlertPanel( runAlertPanel(
title: NSLocalizedString("Install Failed", comment: ""), title: NSLocalizedString("Install Failed", comment: ""),
message: NSLocalizedString("Cannot copy the file to the destination.", comment: ""), message: NSLocalizedString("Cannot copy the file to the destination.", comment: ""),
buttonTitle: NSLocalizedString("Cancel", comment: "")) buttonTitle: NSLocalizedString("Cancel", comment: "")
)
endAppWithDelay() endAppWithDelay()
} }
@ -254,11 +263,14 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
if !status { if !status {
let message = String( let message = String(
format: NSLocalizedString( format: NSLocalizedString(
"Cannot find input source %@ after registration.", comment: ""), "Cannot find input source %@ after registration.", comment: ""
imeIdentifier) ),
imeIdentifier
)
runAlertPanel( runAlertPanel(
title: NSLocalizedString("Fatal Error", comment: ""), message: message, title: NSLocalizedString("Fatal Error", comment: ""), message: message,
buttonTitle: NSLocalizedString("Abort", comment: "")) buttonTitle: NSLocalizedString("Abort", comment: "")
)
endAppWithDelay() endAppWithDelay()
return return
} }
@ -267,11 +279,14 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
if inputSource == nil { if inputSource == nil {
let message = String( let message = String(
format: NSLocalizedString( format: NSLocalizedString(
"Cannot find input source %@ after registration.", comment: ""), "Cannot find input source %@ after registration.", comment: ""
imeIdentifier) ),
imeIdentifier
)
runAlertPanel( runAlertPanel(
title: NSLocalizedString("Fatal Error", comment: ""), message: message, title: NSLocalizedString("Fatal Error", comment: ""), message: message,
buttonTitle: NSLocalizedString("Abort", comment: "")) buttonTitle: NSLocalizedString("Abort", comment: "")
)
} }
} }
@ -304,20 +319,24 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
ntfPostInstall.messageText = NSLocalizedString("Attention", comment: "") ntfPostInstall.messageText = NSLocalizedString("Attention", comment: "")
ntfPostInstall.informativeText = NSLocalizedString( ntfPostInstall.informativeText = NSLocalizedString(
"vChewing is upgraded, but please log out or reboot for the new version to be fully functional.", "vChewing is upgraded, but please log out or reboot for the new version to be fully functional.",
comment: "") comment: ""
)
ntfPostInstall.addButton(withTitle: NSLocalizedString("OK", comment: "")) ntfPostInstall.addButton(withTitle: NSLocalizedString("OK", comment: ""))
} else { } else {
if !mainInputSourceEnabled && !isMacOS12OrAbove { if !mainInputSourceEnabled, !isMacOS12OrAbove {
ntfPostInstall.messageText = NSLocalizedString("Warning", comment: "") ntfPostInstall.messageText = NSLocalizedString("Warning", comment: "")
ntfPostInstall.informativeText = NSLocalizedString( ntfPostInstall.informativeText = NSLocalizedString(
"Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources.", "Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources.",
comment: "") comment: ""
)
ntfPostInstall.addButton(withTitle: NSLocalizedString("Continue", comment: "")) ntfPostInstall.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
} else { } else {
ntfPostInstall.messageText = NSLocalizedString( ntfPostInstall.messageText = NSLocalizedString(
"Installation Successful", comment: "") "Installation Successful", comment: ""
)
ntfPostInstall.informativeText = NSLocalizedString( ntfPostInstall.informativeText = NSLocalizedString(
"vChewing is ready to use.", comment: "") "vChewing is ready to use.", comment: ""
)
ntfPostInstall.addButton(withTitle: NSLocalizedString("OK", comment: "")) ntfPostInstall.addButton(withTitle: NSLocalizedString("OK", comment: ""))
} }
} }
@ -332,12 +351,11 @@ class AppDelegate: NSWindowController, NSApplicationDelegate {
} }
} }
@IBAction func cancelAction(_ sender: AnyObject) { @IBAction func cancelAction(_: AnyObject) {
NSApp.terminate(self) NSApp.terminate(self)
} }
func windowWillClose(_ notification: Notification) { func windowWillClose(_: Notification) {
NSApp.terminate(self) NSApp.terminate(self)
} }
} }

View File

@ -53,7 +53,8 @@ struct ArchiveUtil {
let notarizedArchiveExists = FileManager.default.fileExists(atPath: notarizedArchive) let notarizedArchiveExists = FileManager.default.fileExists(atPath: notarizedArchive)
let devModeAppBundleExists = FileManager.default.fileExists(atPath: devModeAppBundlePath) let devModeAppBundleExists = FileManager.default.fileExists(atPath: devModeAppBundlePath)
if count > 0 { if !notarizedArchivesContent.isEmpty {
// count > 0!isEmpty滿
if count != 1 || !notarizedArchiveExists || devModeAppBundleExists { if count != 1 || !notarizedArchiveExists || devModeAppBundleExists {
let alert = NSAlert() let alert = NSAlert()
alert.alertStyle = .informational alert.alertStyle = .informational
@ -105,7 +106,8 @@ struct ArchiveUtil {
let result = (tempFilePath as NSString).appendingPathComponent(targetAppBundleName) let result = (tempFilePath as NSString).appendingPathComponent(targetAppBundleName)
assert( assert(
FileManager.default.fileExists(atPath: result), FileManager.default.fileExists(atPath: result),
"App bundle must be unzipped at \(result).") "App bundle must be unzipped at \(result)."
)
return result return result
} }
@ -130,5 +132,4 @@ struct ArchiveUtil {
notarizedArchiveBasename) notarizedArchiveBasename)
return notarizedArchive return notarizedArchive
} }
} }

View File

@ -7,7 +7,8 @@ let package = Package(
products: [ products: [
.library( .library(
name: "OpenCC", name: "OpenCC",
targets: ["OpenCC"]) targets: ["OpenCC"]
)
], ],
targets: [ targets: [
.target( .target(
@ -15,14 +16,16 @@ let package = Package(
dependencies: ["copencc"], dependencies: ["copencc"],
resources: [ resources: [
.copy("Dictionary") .copy("Dictionary")
]), ]
),
.testTarget( .testTarget(
name: "OpenCCTests", name: "OpenCCTests",
dependencies: ["OpenCC"], dependencies: ["OpenCC"],
resources: [ resources: [
.copy("benchmark"), .copy("benchmark"),
.copy("testcases"), .copy("testcases"),
]), ]
),
.target( .target(
name: "copencc", name: "copencc",
exclude: [ exclude: [
@ -79,7 +82,8 @@ let package = Package(
.headerSearchPath("deps/marisa-0.2.6/include"), .headerSearchPath("deps/marisa-0.2.6/include"),
.headerSearchPath("deps/marisa-0.2.6/lib"), .headerSearchPath("deps/marisa-0.2.6/lib"),
.define("ENABLE_DARTS"), .define("ENABLE_DARTS"),
]), ]
),
], ],
cxxLanguageStandard: .cxx14 cxxLanguageStandard: .cxx14
) )

View File

@ -22,10 +22,8 @@ import copencc
/// However, the string on which it is operating should not be mutated /// However, the string on which it is operating should not be mutated
/// during the course of a conversion. /// during the course of a conversion.
public class ChineseConverter { public class ChineseConverter {
/// These constants define the ChineseConverter options. /// These constants define the ChineseConverter options.
public struct Options: OptionSet { public struct Options: OptionSet {
public let rawValue: Int public let rawValue: Int
public init(rawValue: Int) { public init(rawValue: Int) {
@ -62,7 +60,7 @@ public class ChineseConverter {
private init(loader: DictionaryLoader, options: Options) throws { private init(loader: DictionaryLoader, options: Options) throws {
seg = try loader.segmentation(options: options) seg = try loader.segmentation(options: options)
chain = try loader.conversionChain(options: options) chain = try loader.conversionChain(options: options)
var rawChain = chain.map { $0.dict } var rawChain = chain.map(\.dict)
converter = CCConverterCreate("SwiftyOpenCC", seg.dict, &rawChain, rawChain.count) converter = CCConverterCreate("SwiftyOpenCC", seg.dict, &rawChain, rawChain.count)
} }
@ -85,5 +83,4 @@ public class ChineseConverter {
defer { STLStringDestroy(stlStr) } defer { STLStringDestroy(stlStr) }
return String(utf8String: STLStringGetUTF8String(stlStr))! return String(utf8String: STLStringGetUTF8String(stlStr))!
} }
} }

View File

@ -9,7 +9,6 @@ import Foundation
import copencc import copencc
class ConversionDictionary { class ConversionDictionary {
let group: [ConversionDictionary] let group: [ConversionDictionary]
let dict: CCDictRef let dict: CCDictRef
@ -23,7 +22,7 @@ class ConversionDictionary {
} }
init(group: [ConversionDictionary]) { init(group: [ConversionDictionary]) {
var rawGroup = group.map { $0.dict } var rawGroup = group.map(\.dict)
self.group = group self.group = group
dict = CCDictCreateWithGroup(&rawGroup, rawGroup.count) dict = CCDictCreateWithGroup(&rawGroup, rawGroup.count)
} }

View File

@ -9,7 +9,6 @@ import Foundation
import copencc import copencc
public enum ConversionError: Error { public enum ConversionError: Error {
case fileNotFound case fileNotFound
case invalidFormat case invalidFormat

View File

@ -9,9 +9,7 @@ import Foundation
import copencc import copencc
extension ChineseConverter { extension ChineseConverter {
struct DictionaryLoader { struct DictionaryLoader {
private static let subdirectory = "Dictionary" private static let subdirectory = "Dictionary"
private static let dictCache = WeakValueCache<String, ConversionDictionary>() private static let dictCache = WeakValueCache<String, ConversionDictionary>()
@ -25,7 +23,8 @@ extension ChineseConverter {
guard guard
let path = bundle.path( let path = bundle.path(
forResource: name.description, ofType: "ocd2", forResource: name.description, ofType: "ocd2",
inDirectory: DictionaryLoader.subdirectory) inDirectory: DictionaryLoader.subdirectory
)
else { else {
throw ConversionError.fileNotFound throw ConversionError.fileNotFound
} }
@ -37,7 +36,6 @@ extension ChineseConverter {
} }
extension ChineseConverter.DictionaryLoader { extension ChineseConverter.DictionaryLoader {
func segmentation(options: ChineseConverter.Options) throws -> ConversionDictionary { func segmentation(options: ChineseConverter.Options) throws -> ConversionDictionary {
let dictName = options.segmentationDictName let dictName = options.segmentationDictName
return try dict(dictName) return try dict(dictName)

View File

@ -8,9 +8,7 @@
import Foundation import Foundation
extension ChineseConverter { extension ChineseConverter {
enum DictionaryName: CustomStringConvertible { enum DictionaryName: CustomStringConvertible {
case hkVariants case hkVariants
case hkVariantsRev case hkVariantsRev
case hkVariantsRevPhrases case hkVariantsRevPhrases
@ -46,7 +44,6 @@ extension ChineseConverter {
} }
extension ChineseConverter.Options { extension ChineseConverter.Options {
var segmentationDictName: ChineseConverter.DictionaryName { var segmentationDictName: ChineseConverter.DictionaryName {
if contains(.traditionalize) { if contains(.traditionalize) {
return .stPhrases return .stPhrases

View File

@ -8,7 +8,6 @@
import Foundation import Foundation
class WeakBox<Value: AnyObject> { class WeakBox<Value: AnyObject> {
private(set) weak var value: Value? private(set) weak var value: Value?
init(_ value: Value) { init(_ value: Value) {
@ -17,7 +16,6 @@ class WeakBox<Value: AnyObject> {
} }
class WeakValueCache<Key: Hashable, Value: AnyObject> { class WeakValueCache<Key: Hashable, Value: AnyObject> {
private var storage: [Key: WeakBox<Value>] = [:] private var storage: [Key: WeakBox<Value>] = [:]
private var lock = NSLock() private var lock = NSLock()

View File

@ -14,7 +14,6 @@ let testCases: [(String, ChineseConverter.Options)] = [
] ]
class OpenCCTests: XCTestCase { class OpenCCTests: XCTestCase {
func converter(option: ChineseConverter.Options) throws -> ChineseConverter { func converter(option: ChineseConverter.Options) throws -> ChineseConverter {
try ChineseConverter(options: option) try ChineseConverter(options: option)
} }
@ -22,7 +21,8 @@ class OpenCCTests: XCTestCase {
func testConversion() throws { func testConversion() throws {
func testCase(name: String, ext: String) -> String { func testCase(name: String, ext: String) -> String {
let url = Bundle.module.url( let url = Bundle.module.url(
forResource: name, withExtension: ext, subdirectory: "testcases")! forResource: name, withExtension: ext, subdirectory: "testcases"
)!
return try! String(contentsOf: url) return try! String(contentsOf: url)
} }
for (name, opt) in testCases { for (name, opt) in testCases {
@ -47,7 +47,7 @@ class OpenCCTests: XCTestCase {
let options: ChineseConverter.Options = [.traditionalize, .twStandard, .twIdiom] let options: ChineseConverter.Options = [.traditionalize, .twStandard, .twIdiom]
let holder = try! ChineseConverter(options: options) let holder = try! ChineseConverter(options: options)
measure { measure {
for _ in 0..<1_000 { for _ in 0..<1000 {
_ = try! ChineseConverter(options: options) _ = try! ChineseConverter(options: options)
} }
} }
@ -57,7 +57,8 @@ class OpenCCTests: XCTestCase {
func testConversionPerformance() throws { func testConversionPerformance() throws {
let cov = try converter(option: [.traditionalize, .twStandard, .twIdiom]) let cov = try converter(option: [.traditionalize, .twStandard, .twIdiom])
let url = Bundle.module.url( let url = Bundle.module.url(
forResource: "zuozhuan", withExtension: "txt", subdirectory: "benchmark")! forResource: "zuozhuan", withExtension: "txt", subdirectory: "benchmark"
)!
// 1.9 MB, 624k word // 1.9 MB, 624k word
let str = try String(contentsOf: url) let str = try String(contentsOf: url)
measure { measure {

View File

@ -36,7 +36,7 @@ public class OpenCCBridge: NSObject {
private var simplify: ChineseConverter? private var simplify: ChineseConverter?
private var traditionalize: ChineseConverter? private var traditionalize: ChineseConverter?
private override init() { override private init() {
try? simplify = ChineseConverter(options: .simplify) try? simplify = ChineseConverter(options: .simplify)
try? traditionalize = ChineseConverter(options: [.traditionalize, .twStandard]) try? traditionalize = ChineseConverter(options: [.traditionalize, .twStandard])
super.init() super.init()

View File

@ -26,7 +26,7 @@ extension Preferences {
Function builder for `Preferences` components used in order to restrict types of child views to be of type `Section`. Function builder for `Preferences` components used in order to restrict types of child views to be of type `Section`.
*/ */
@resultBuilder @resultBuilder
public struct SectionBuilder { public enum SectionBuilder {
public static func buildBlock(_ sections: Section...) -> [Section] { public static func buildBlock(_ sections: Section...) -> [Section] {
sections sections
} }
@ -78,7 +78,7 @@ extension Preferences {
@ViewBuilder @ViewBuilder
private func viewForSection(_ sections: [Section], index: Int) -> some View { private func viewForSection(_ sections: [Section], index: Int) -> some View {
sections[index] sections[index]
if index != sections.count - 1 && sections[index].bottomDivider { if index != sections.count - 1, sections[index].bottomDivider {
Divider() Divider()
// Strangely doesn't work without width being specified. Probably because of custom alignment. // Strangely doesn't work without width being specified. Probably because of custom alignment.
.frame(width: CGFloat(contentWidth), height: 20) .frame(width: CGFloat(contentWidth), height: 20)

View File

@ -93,7 +93,7 @@ extension Preferences {
@available(*, unavailable) @available(*, unavailable)
@objc @objc
dynamic required init?(coder: NSCoder) { dynamic required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
} }

View File

@ -112,7 +112,7 @@ final class PreferencesTabViewController: NSViewController, PreferencesStyleCont
} }
} }
private func updateWindowTitle(tabIndex: Int) { private func updateWindowTitle(tabIndex _: Int) {
window.title = { window.title = {
// if preferencePanes.count > 1 { // if preferencePanes.count > 1 {
// return preferencePanes[tabIndex].preferencePaneTitle // return preferencePanes[tabIndex].preferencePaneTitle
@ -200,7 +200,8 @@ final class PreferencesTabViewController: NSViewController, PreferencesStyleCont
options: options, options: options,
completionHandler: completion completionHandler: completion
) )
}, completionHandler: nil) }, completionHandler: nil
)
} else { } else {
super.transition( super.transition(
from: fromViewController, from: fromViewController,
@ -234,22 +235,22 @@ final class PreferencesTabViewController: NSViewController, PreferencesStyleCont
} }
extension PreferencesTabViewController: NSToolbarDelegate { extension PreferencesTabViewController: NSToolbarDelegate {
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
toolbarItemIdentifiers toolbarItemIdentifiers
} }
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { func toolbarAllowedItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
toolbarItemIdentifiers toolbarItemIdentifiers
} }
func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { func toolbarSelectableItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
style == .segmentedControl ? [] : toolbarItemIdentifiers style == .segmentedControl ? [] : toolbarItemIdentifiers
} }
public func toolbar( public func toolbar(
_ toolbar: NSToolbar, _: NSToolbar,
itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool willBeInsertedIntoToolbar _: Bool
) -> NSToolbarItem? { ) -> NSToolbarItem? {
if itemIdentifier == .flexibleSpace { if itemIdentifier == .flexibleSpace {
return nil return nil

View File

@ -87,12 +87,12 @@ public final class PreferencesWindowController: NSWindowController {
} }
@available(*, unavailable) @available(*, unavailable)
override public init(window: NSWindow?) { override public init(window _: NSWindow?) {
fatalError("init(window:) is not supported, use init(preferences:style:animated:)") fatalError("init(window:) is not supported, use init(preferences:style:animated:)")
} }
@available(*, unavailable) @available(*, unavailable)
public required init?(coder: NSCoder) { public required init?(coder _: NSCoder) {
fatalError("init(coder:) is not supported, use init(preferences:style:animated:)") fatalError("init(coder:) is not supported, use init(preferences:style:animated:)")
} }
@ -140,7 +140,7 @@ public final class PreferencesWindowController: NSWindowController {
extension PreferencesWindowController { extension PreferencesWindowController {
/// Returns the active pane if it responds to the given action. /// Returns the active pane if it responds to the given action.
override public func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? { public override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? {
if let target = super.supplementalTarget(forAction: action, sender: sender) { if let target = super.supplementalTarget(forAction: action, sender: sender) {
return target return target
} }

View File

@ -48,7 +48,7 @@ final class SegmentedControlStyleViewController: NSViewController, PreferencesSt
} }
@available(*, unavailable) @available(*, unavailable)
required init?(coder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

View File

@ -36,11 +36,13 @@ extension NSView {
result.append( result.append(
contentsOf: NSLayoutConstraint.constraints( contentsOf: NSLayoutConstraint.constraints(
withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil,
views: ["subview": self])) views: ["subview": self]
))
result.append( result.append(
contentsOf: NSLayoutConstraint.constraints( contentsOf: NSLayoutConstraint.constraints(
withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil,
views: ["subview": self])) views: ["subview": self]
))
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
superview.addConstraints(result) superview.addConstraints(result)
@ -104,7 +106,7 @@ extension Bundle {
/// A window that allows you to disable all user interactions via `isUserInteractionEnabled`. /// A window that allows you to disable all user interactions via `isUserInteractionEnabled`.
/// ///
/// Used to avoid breaking animations when the user clicks too fast. Disable user interactions during animations and you're set. /// Used to avoid breaking animations when the user clicks too fast. Disable user interactions during animations and you're set.
class UserInteractionPausableWindow: NSWindow { // swiftlint:disable:this final_class class UserInteractionPausableWindow: NSWindow {
var isUserInteractionEnabled = true var isUserInteractionEnabled = true
override func sendEvent(_ event: NSEvent) { override func sendEvent(_ event: NSEvent) {

View File

@ -6,6 +6,7 @@
import SwiftUI import SwiftUI
// MARK: - NSComboBox // MARK: - NSComboBox
// Ref: https://stackoverflow.com/a/71058587/4162914 // Ref: https://stackoverflow.com/a/71058587/4162914
@available(macOS 11.0, *) @available(macOS 11.0, *)
struct ComboBox: NSViewRepresentable { struct ComboBox: NSViewRepresentable {

View File

@ -31,7 +31,7 @@ import InputMethodKit
class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelegate, class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelegate,
FSEventStreamHelperDelegate FSEventStreamHelperDelegate
{ {
func helper(_ helper: FSEventStreamHelper, didReceive events: [FSEventStreamHelper.Event]) { func helper(_: FSEventStreamHelper, didReceive _: [FSEventStreamHelper.Event]) {
// 100ms 使使 // 100ms 使使
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
if mgrPrefs.shouldAutoReloadUserDataFiles { if mgrPrefs.shouldAutoReloadUserDataFiles {
@ -42,14 +42,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
// let vChewingKeyLayoutBundle = Bundle.init(path: URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("vChewingKeyLayout.bundle").path) // let vChewingKeyLayoutBundle = Bundle.init(path: URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("vChewingKeyLayout.bundle").path)
@IBOutlet weak var window: NSWindow? @IBOutlet var window: NSWindow?
private var ctlPrefWindowInstance: ctlPrefWindow? private var ctlPrefWindowInstance: ctlPrefWindow?
private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window
private var checkTask: URLSessionTask? private var checkTask: URLSessionTask?
private var updateNextStepURL: URL? private var updateNextStepURL: URL?
private var fsStreamHelper = FSEventStreamHelper( private var fsStreamHelper = FSEventStreamHelper(
path: mgrLangModel.dataFolderPath(isDefaultFolder: false), path: mgrLangModel.dataFolderPath(isDefaultFolder: false),
queue: DispatchQueue(label: "vChewing User Phrases")) queue: DispatchQueue(label: "vChewing User Phrases")
)
private var currentAlertType: String = "" private var currentAlertType: String = ""
// dealloc // dealloc
@ -62,7 +63,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
fsStreamHelper.delegate = nil fsStreamHelper.delegate = nil
} }
func applicationDidFinishLaunching(_ notification: Notification) { func applicationDidFinishLaunching(_: Notification) {
IME.initLangModels(userOnly: false) IME.initLangModels(userOnly: false)
fsStreamHelper.delegate = self fsStreamHelper.delegate = self
_ = fsStreamHelper.start() _ = fsStreamHelper.start()
@ -104,7 +105,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
@objc(checkForUpdateForced:) @objc(checkForUpdateForced:)
func checkForUpdate(forced: Bool) { func checkForUpdate(forced: Bool) {
if checkTask != nil { if checkTask != nil {
// busy // busy
return return
@ -137,24 +137,30 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
let content = String( let content = String(
format: NSLocalizedString( format: NSLocalizedString(
"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@", "You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@",
comment: ""), comment: ""
),
report.currentShortVersion, report.currentShortVersion,
report.currentVersion, report.currentVersion,
report.remoteShortVersion, report.remoteShortVersion,
report.remoteVersion, report.remoteVersion,
report.versionDescription) report.versionDescription
)
IME.prtDebugIntel("vChewingDebug: \(content)") IME.prtDebugIntel("vChewingDebug: \(content)")
currentAlertType = "Update" currentAlertType = "Update"
ctlNonModalAlertWindow.shared.show( ctlNonModalAlertWindow.shared.show(
title: NSLocalizedString( title: NSLocalizedString(
"New Version Available", comment: ""), "New Version Available", comment: ""
),
content: content, content: content,
confirmButtonTitle: NSLocalizedString( confirmButtonTitle: NSLocalizedString(
"Visit Website", comment: ""), "Visit Website", comment: ""
),
cancelButtonTitle: NSLocalizedString( cancelButtonTitle: NSLocalizedString(
"Not Now", comment: ""), "Not Now", comment: ""
),
cancelAsDefault: false, cancelAsDefault: false,
delegate: self) delegate: self
)
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
case .noNeedToUpdate, .ignored: case .noNeedToUpdate, .ignored:
break break
@ -163,11 +169,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
switch error { switch error {
case VersionUpdateApiError.connectionError(let message): case VersionUpdateApiError.connectionError(let message):
let title = NSLocalizedString( let title = NSLocalizedString(
"Update Check Failed", comment: "") "Update Check Failed", comment: ""
)
let content = String( let content = String(
format: NSLocalizedString( format: NSLocalizedString(
"There may be no internet connection or the server failed to respond.\n\nError message: %@", "There may be no internet connection or the server failed to respond.\n\nError message: %@",
comment: ""), message) comment: ""
), message
)
let buttonTitle = NSLocalizedString("Dismiss", comment: "") let buttonTitle = NSLocalizedString("Dismiss", comment: "")
IME.prtDebugIntel("vChewingDebug: \(content)") IME.prtDebugIntel("vChewingDebug: \(content)")
currentAlertType = "Update" currentAlertType = "Update"
@ -175,7 +184,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
title: title, content: content, title: title, content: content,
confirmButtonTitle: buttonTitle, confirmButtonTitle: buttonTitle,
cancelButtonTitle: nil, cancelButtonTitle: nil,
cancelAsDefault: false, delegate: nil) cancelAsDefault: false, delegate: nil
)
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
default: default:
break break
@ -189,20 +199,23 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
let content = String( let content = String(
format: NSLocalizedString( format: NSLocalizedString(
"This will remove vChewing Input Method from this user account, requiring your confirmation.", "This will remove vChewing Input Method from this user account, requiring your confirmation.",
comment: "")) comment: ""
))
ctlNonModalAlertWindow.shared.show( ctlNonModalAlertWindow.shared.show(
title: NSLocalizedString("Uninstallation", comment: ""), content: content, title: NSLocalizedString("Uninstallation", comment: ""), content: content,
confirmButtonTitle: NSLocalizedString("OK", comment: ""), confirmButtonTitle: NSLocalizedString("OK", comment: ""),
cancelButtonTitle: NSLocalizedString("Not Now", comment: ""), cancelAsDefault: false, cancelButtonTitle: NSLocalizedString("Not Now", comment: ""), cancelAsDefault: false,
delegate: self) delegate: self
)
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
} }
func ctlNonModalAlertWindowDidConfirm(_ controller: ctlNonModalAlertWindow) { func ctlNonModalAlertWindowDidConfirm(_: ctlNonModalAlertWindow) {
switch currentAlertType { switch currentAlertType {
case "Uninstall": case "Uninstall":
NSWorkspace.shared.openFile( NSWorkspace.shared.openFile(
mgrLangModel.dataFolderPath(isDefaultFolder: true), withApplication: "Finder") mgrLangModel.dataFolderPath(isDefaultFolder: true), withApplication: "Finder"
)
IME.uninstall(isSudo: false, selfKill: true) IME.uninstall(isSudo: false, selfKill: true)
case "Update": case "Update":
if let updateNextStepURL = updateNextStepURL { if let updateNextStepURL = updateNextStepURL {
@ -214,7 +227,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
} }
} }
func ctlNonModalAlertWindowDidCancel(_ controller: ctlNonModalAlertWindow) { func ctlNonModalAlertWindowDidCancel(_: ctlNonModalAlertWindow) {
switch currentAlertType { switch currentAlertType {
case "Update": case "Update":
updateNextStepURL = nil updateNextStepURL = nil
@ -224,7 +237,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
} }
// New About Window // New About Window
@IBAction func about(_ sender: Any) { @IBAction func about(_: Any) {
(NSApp.delegate as? AppDelegate)?.showAbout() (NSApp.delegate as? AppDelegate)?.showAbout()
NSApplication.shared.activate(ignoringOtherApps: true) NSApplication.shared.activate(ignoringOtherApps: true)
} }

View File

@ -42,6 +42,7 @@ class AppleKeyboardConverter: NSObject {
@objc class func isDynamicBasicKeyboardLayoutEnabled() -> Bool { @objc class func isDynamicBasicKeyboardLayoutEnabled() -> Bool {
AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout)
} }
// Apple // Apple
@objc class func cnvApple2ABC(_ charCode: UniChar) -> UniChar { @objc class func cnvApple2ABC(_ charCode: UniChar) -> UniChar {
var charCode = charCode var charCode = charCode

View File

@ -58,7 +58,6 @@ import Cocoa
/// - Choosing Candidate: The candidate window is open to let the user to choose /// - Choosing Candidate: The candidate window is open to let the user to choose
/// one among the candidates. /// one among the candidates.
class InputState: NSObject { class InputState: NSObject {
/// Represents that the input controller is deactivated. /// Represents that the input controller is deactivated.
@objc(InputStateDeactivated) @objc(InputStateDeactivated)
class Deactivated: InputState { class Deactivated: InputState {
@ -89,6 +88,7 @@ class InputState: NSObject {
@objc var composingBuffer: String { @objc var composingBuffer: String {
"" ""
} }
override var description: String { override var description: String {
"<InputState.EmptyIgnoringPreviousState>" "<InputState.EmptyIgnoringPreviousState>"
} }
@ -147,7 +147,8 @@ class InputState: NSObject {
attributes: [ attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue, .underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0, .markedClauseSegment: 0,
]) ]
)
return attributedSting return attributedSting
} }
@ -164,18 +165,18 @@ class InputState: NSObject {
/// Represents that the user is marking a range in the composing buffer. /// Represents that the user is marking a range in the composing buffer.
@objc(InputStateMarking) @objc(InputStateMarking)
class Marking: NotEmpty { class Marking: NotEmpty {
@objc private(set) var markerIndex: UInt @objc private(set) var markerIndex: UInt
@objc private(set) var markedRange: NSRange @objc private(set) var markedRange: NSRange
@objc private var deleteTargetExists = false @objc private var deleteTargetExists = false
@objc var tooltip: String { @objc var tooltip: String {
if composingBuffer.count != readings.count { if composingBuffer.count != readings.count {
TooltipController.backgroundColor = NSColor( TooltipController.backgroundColor = NSColor(
red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00) red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00
)
TooltipController.textColor = NSColor.white TooltipController.textColor = NSColor.white
return NSLocalizedString( return NSLocalizedString(
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: "") "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: ""
)
} }
if mgrPrefs.phraseReplacementEnabled { if mgrPrefs.phraseReplacementEnabled {
@ -192,21 +193,29 @@ class InputState: NSObject {
let text = (composingBuffer as NSString).substring(with: markedRange) let text = (composingBuffer as NSString).substring(with: markedRange)
if markedRange.length < kMinMarkRangeLength { if markedRange.length < kMinMarkRangeLength {
TooltipController.backgroundColor = NSColor( TooltipController.backgroundColor = NSColor(
red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00) red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00
)
TooltipController.textColor = NSColor( TooltipController.textColor = NSColor(
red: 0.86, green: 0.86, blue: 0.86, alpha: 1.00) red: 0.86, green: 0.86, blue: 0.86, alpha: 1.00
)
return String( return String(
format: NSLocalizedString( format: NSLocalizedString(
"\"%@\" length must ≥ 2 for a user phrase.", comment: ""), text) "\"%@\" length must ≥ 2 for a user phrase.", comment: ""
), text
)
} else if markedRange.length > kMaxMarkRangeLength { } else if markedRange.length > kMaxMarkRangeLength {
TooltipController.backgroundColor = NSColor( TooltipController.backgroundColor = NSColor(
red: 0.26, green: 0.16, blue: 0.00, alpha: 1.00) red: 0.26, green: 0.16, blue: 0.00, alpha: 1.00
)
TooltipController.textColor = NSColor( TooltipController.textColor = NSColor(
red: 1.00, green: 0.60, blue: 0.00, alpha: 1.00) red: 1.00, green: 0.60, blue: 0.00, alpha: 1.00
)
return String( return String(
format: NSLocalizedString( format: NSLocalizedString(
"\"%@\" length should ≤ %d for a user phrase.", comment: ""), "\"%@\" length should ≤ %d for a user phrase.", comment: ""
text, kMaxMarkRangeLength) ),
text, kMaxMarkRangeLength
)
} }
let (exactBegin, _) = (composingBuffer as NSString).characterIndex( let (exactBegin, _) = (composingBuffer as NSString).characterIndex(
@ -216,24 +225,30 @@ class InputState: NSObject {
let selectedReadings = readings[exactBegin..<exactEnd] let selectedReadings = readings[exactBegin..<exactEnd]
let joined = selectedReadings.joined(separator: "-") let joined = selectedReadings.joined(separator: "-")
let exist = mgrLangModel.checkIfUserPhraseExist( let exist = mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined) userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined
)
if exist { if exist {
deleteTargetExists = exist deleteTargetExists = exist
TooltipController.backgroundColor = NSColor( TooltipController.backgroundColor = NSColor(
red: 0.00, green: 0.18, blue: 0.13, alpha: 1.00) red: 0.00, green: 0.18, blue: 0.13, alpha: 1.00
)
TooltipController.textColor = NSColor( TooltipController.textColor = NSColor(
red: 0.00, green: 1.00, blue: 0.74, alpha: 1.00) red: 0.00, green: 1.00, blue: 0.74, alpha: 1.00
)
return String( return String(
format: NSLocalizedString( format: NSLocalizedString(
"\"%@\" already exists: ↩ to boost, ⇧⌘↩ to exclude.", comment: ""), text "\"%@\" already exists: ↩ to boost, ⇧⌘↩ to exclude.", comment: ""
), text
) )
} }
TooltipController.backgroundColor = NSColor( TooltipController.backgroundColor = NSColor(
red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00) red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00
)
TooltipController.textColor = NSColor.white TooltipController.textColor = NSColor.white
return String( return String(
format: NSLocalizedString("\"%@\" selected. ↩ to add user phrase.", comment: ""), format: NSLocalizedString("\"%@\" selected. ↩ to add user phrase.", comment: ""),
text) text
)
} }
@objc var tooltipForInputting: String = "" @objc var tooltipForInputting: String = ""
@ -256,12 +271,14 @@ class InputState: NSObject {
[ [
.underlineStyle: NSUnderlineStyle.single.rawValue, .underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0, .markedClauseSegment: 0,
], range: NSRange(location: 0, length: markedRange.location)) ], range: NSRange(location: 0, length: markedRange.location)
)
attributedSting.setAttributes( attributedSting.setAttributes(
[ [
.underlineStyle: NSUnderlineStyle.thick.rawValue, .underlineStyle: NSUnderlineStyle.thick.rawValue,
.markedClauseSegment: 1, .markedClauseSegment: 1,
], range: markedRange) ], range: markedRange
)
attributedSting.setAttributes( attributedSting.setAttributes(
[ [
.underlineStyle: NSUnderlineStyle.single.rawValue, .underlineStyle: NSUnderlineStyle.single.rawValue,
@ -269,7 +286,9 @@ class InputState: NSObject {
], ],
range: NSRange( range: NSRange(
location: end, location: end,
length: (composingBuffer as NSString).length - end)) length: (composingBuffer as NSString).length - end
)
)
return attributedSting return attributedSting
} }
@ -297,7 +316,7 @@ class InputState: NSObject {
if markedRange.length > kMaxMarkRangeLength { if markedRange.length > kMaxMarkRangeLength {
return false return false
} }
if ctlInputMethod.areWeDeleting && !deleteTargetExists { if ctlInputMethod.areWeDeleting, !deleteTargetExists {
return false return false
} }
return markedRange.length >= kMinMarkRangeLength return markedRange.length >= kMinMarkRangeLength
@ -313,7 +332,8 @@ class InputState: NSObject {
let selectedReadings = readings[exactBegin..<exactEnd] let selectedReadings = readings[exactBegin..<exactEnd]
let joined = selectedReadings.joined(separator: "-") let joined = selectedReadings.joined(separator: "-")
return mgrLangModel.checkIfUserPhraseExist( return mgrLangModel.checkIfUserPhraseExist(
userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined) userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined
)
== true == true
} }
@ -363,7 +383,8 @@ class InputState: NSObject {
attributes: [ attributes: [
.underlineStyle: NSUnderlineStyle.single.rawValue, .underlineStyle: NSUnderlineStyle.single.rawValue,
.markedClauseSegment: 0, .markedClauseSegment: 0,
]) ]
)
return attributedSting return attributedSting
} }
@ -397,17 +418,17 @@ class InputState: NSObject {
@objc init(node: SymbolNode, useVerticalMode: Bool) { @objc init(node: SymbolNode, useVerticalMode: Bool) {
self.node = node self.node = node
let candidates = node.children?.map { $0.title } ?? [String]() let candidates = node.children?.map(\.title) ?? [String]()
super.init( super.init(
composingBuffer: "", cursorIndex: 0, candidates: candidates, composingBuffer: "", cursorIndex: 0, candidates: candidates,
useVerticalMode: useVerticalMode) useVerticalMode: useVerticalMode
)
} }
override var description: String { override var description: String {
"<InputState.SymbolTable, candidates:\(candidates), useVerticalMode:\(useVerticalMode), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>" "<InputState.SymbolTable, candidates:\(candidates), useVerticalMode:\(useVerticalMode), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
} }
} }
} }
class SymbolNode: NSObject { class SymbolNode: NSObject {
@ -457,7 +478,7 @@ class SymbolNode: NSObject {
@objc static let catLineSegments = String( @objc static let catLineSegments = String(
format: NSLocalizedString("catLineSegments", comment: "")) format: NSLocalizedString("catLineSegments", comment: ""))
@objc static let root: SymbolNode = SymbolNode( @objc static let root: SymbolNode = .init(
"/", "/",
[ [
SymbolNode(""), SymbolNode(""),
@ -465,18 +486,21 @@ class SymbolNode: NSObject {
SymbolNode(catHoriBrackets, symbols: "()「」〔〕{}〈〉『』《》【】﹙﹚﹝﹞﹛﹜"), SymbolNode(catHoriBrackets, symbols: "()「」〔〕{}〈〉『』《》【】﹙﹚﹝﹞﹛﹜"),
SymbolNode(catVertBrackets, symbols: "︵︶﹁﹂︹︺︷︸︿﹀﹃﹄︽︾︻︼"), SymbolNode(catVertBrackets, symbols: "︵︶﹁﹂︹︺︷︸︿﹀﹃﹄︽︾︻︼"),
SymbolNode( SymbolNode(
catGreekLetters, symbols: "αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"), catGreekLetters, symbols: "αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"
),
SymbolNode(catMathSymbols, symbols: "+-×÷=≠≒∞±√<>﹤﹥≦≧∩∪ˇ⊥∠∟⊿㏒㏑∫∮∵∴╳﹢"), SymbolNode(catMathSymbols, symbols: "+-×÷=≠≒∞±√<>﹤﹥≦≧∩∪ˇ⊥∠∟⊿㏒㏑∫∮∵∴╳﹢"),
SymbolNode(catCurrencyUnits, symbols: "$€¥¢£₽₨₩฿₺₮₱₭₴₦৲৳૱௹﷼₹₲₪₡₫៛₵₢₸₤₳₥₠₣₰₧₯₶₷"), SymbolNode(catCurrencyUnits, symbols: "$€¥¢£₽₨₩฿₺₮₱₭₴₦৲৳૱௹﷼₹₲₪₡₫៛₵₢₸₤₳₥₠₣₰₧₯₶₷"),
SymbolNode(catSpecialSymbols, symbols: "↑↓←→↖↗↙↘↺⇧⇩⇦⇨⇄⇆⇅⇵↻◎○●⊕⊙※△▲☆★◇◆□■▽▼§¥〒¢£♀♂↯"), SymbolNode(catSpecialSymbols, symbols: "↑↓←→↖↗↙↘↺⇧⇩⇦⇨⇄⇆⇅⇵↻◎○●⊕⊙※△▲☆★◇◆□■▽▼§¥〒¢£♀♂↯"),
SymbolNode(catUnicodeSymbols, symbols: "♨☀☁☂☃♠♥♣♦♩♪♫♬☺☻"), SymbolNode(catUnicodeSymbols, symbols: "♨☀☁☂☃♠♥♣♦♩♪♫♬☺☻"),
SymbolNode(catCircledKanjis, symbols: "㊟㊞㊚㊛㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗︎㊘㊙︎㊜㊝㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰🈚︎🈯︎"), SymbolNode(catCircledKanjis, symbols: "㊟㊞㊚㊛㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗︎㊘㊙︎㊜㊝㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰🈚︎🈯︎"),
SymbolNode( SymbolNode(
catCircledKataKana, symbols: "㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋾"), catCircledKataKana, symbols: "㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋾"
),
SymbolNode(catBracketKanjis, symbols: "㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃"), SymbolNode(catBracketKanjis, symbols: "㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃"),
SymbolNode(catSingleTableLines, symbols: "├─┼┴┬┤┌┐╞═╪╡│▕└┘╭╮╰╯"), SymbolNode(catSingleTableLines, symbols: "├─┼┴┬┤┌┐╞═╪╡│▕└┘╭╮╰╯"),
SymbolNode(catDoubleTableLines, symbols: "╔╦╗╠═╬╣╓╥╖╒╤╕║╚╩╝╟╫╢╙╨╜╞╪╡╘╧╛"), SymbolNode(catDoubleTableLines, symbols: "╔╦╗╠═╬╣╓╥╖╒╤╕║╚╩╝╟╫╢╙╨╜╞╪╡╘╧╛"),
SymbolNode(catFillingBlocks, symbols: "_ˍ▁▂▃▄▅▆▇█▏▎▍▌▋▊▉◢◣◥◤"), SymbolNode(catFillingBlocks, symbols: "_ˍ▁▂▃▄▅▆▇█▏▎▍▌▋▊▉◢◣◥◤"),
SymbolNode(catLineSegments, symbols: "﹣﹦≡|∣∥–︱—︳╴¯ ̄﹉﹊﹍﹎﹋﹌﹏︴∕﹨╱╲/\"), SymbolNode(catLineSegments, symbols: "﹣﹦≡|∣∥–︱—︳╴¯ ̄﹉﹊﹍﹎﹋﹌﹏︴∕﹨╱╲/\"),
]) ]
)
} }

View File

@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
// MARK: - § Handle Candidate State. // MARK: - § Handle Candidate State.
@objc extension KeyHandler { @objc extension KeyHandler {
func _handleCandidateState( func _handleCandidateState(
_ state: InputState, _ state: InputState,
@ -75,7 +76,8 @@ import Cocoa
delegate!.keyHandler( delegate!.keyHandler(
self, self,
didSelectCandidateAt: Int(ctlCandidateCurrent.selectedCandidateIndex), didSelectCandidateAt: Int(ctlCandidateCurrent.selectedCandidateIndex),
ctlCandidate: ctlCandidateCurrent) ctlCandidate: ctlCandidateCurrent
)
return true return true
} }
@ -232,18 +234,19 @@ import Cocoa
candidates = (state as! InputState.AssociatedPhrases).candidates candidates = (state as! InputState.AssociatedPhrases).candidates
} }
if candidates.isEmpty { return false } if candidates.isEmpty {
return false
if (input.isEnd || input.emacsKey == vChewingEmacsKey.end) && candidates.count > 0 { } else { // count > 0!isEmpty滿
if input.isEnd || input.emacsKey == vChewingEmacsKey.end {
if ctlCandidateCurrent.selectedCandidateIndex == UInt(candidates.count - 1) { if ctlCandidateCurrent.selectedCandidateIndex == UInt(candidates.count - 1) {
IME.prtDebugIntel("9B69AAAD") IME.prtDebugIntel("9B69AAAD")
errorCallback() errorCallback()
} else { } else {
ctlCandidateCurrent.selectedCandidateIndex = UInt(candidates.count - 1) ctlCandidateCurrent.selectedCandidateIndex = UInt(candidates.count - 1)
} }
return true return true
} }
}
if state is InputState.AssociatedPhrases { if state is InputState.AssociatedPhrases {
if !input.isShiftHold { return false } if !input.isShiftHold { return false }
@ -300,7 +303,7 @@ import Cocoa
chkKeyValidity(charCode) || ifLangModelHasUnigrams(forKey: customPunctuation) chkKeyValidity(charCode) || ifLangModelHasUnigrams(forKey: customPunctuation)
|| ifLangModelHasUnigrams(forKey: punctuation) || ifLangModelHasUnigrams(forKey: punctuation)
if !shouldAutoSelectCandidate && input.isUpperCaseASCIILetterKey { if !shouldAutoSelectCandidate, input.isUpperCaseASCIILetterKey {
let letter: String! = String(format: "%@%c", "_letter_", CChar(charCode)) let letter: String! = String(format: "%@%c", "_letter_", CChar(charCode))
if ifLangModelHasUnigrams(forKey: letter) { shouldAutoSelectCandidate = true } if ifLangModelHasUnigrams(forKey: letter) { shouldAutoSelectCandidate = true }
} }
@ -311,12 +314,14 @@ import Cocoa
delegate!.keyHandler( delegate!.keyHandler(
self, self,
didSelectCandidateAt: Int(candidateIndex), didSelectCandidateAt: Int(candidateIndex),
ctlCandidate: ctlCandidateCurrent) ctlCandidate: ctlCandidateCurrent
)
clear() clear()
let empty = InputState.EmptyIgnoringPreviousState() let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty) stateCallback(empty)
return handle( return handle(
input: input, state: empty, stateCallback: stateCallback, errorCallback: errorCallback) input: input, state: empty, stateCallback: stateCallback, errorCallback: errorCallback
)
} }
return true return true
} }

View File

@ -43,7 +43,7 @@ import Cocoa
// Ignore the input if its inputText is empty. // Ignore the input if its inputText is empty.
// Reason: such inputs may be functional key combinations. // Reason: such inputs may be functional key combinations.
if (inputText).isEmpty { if inputText.isEmpty {
return false return false
} }
@ -56,6 +56,7 @@ import Cocoa
} }
// MARK: Caps Lock processing. // MARK: Caps Lock processing.
// If Caps Lock is ON, temporarily disable bopomofo. // If Caps Lock is ON, temporarily disable bopomofo.
// Note: Alphanumerical mode processing. // Note: Alphanumerical mode processing.
if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey
@ -75,7 +76,7 @@ import Cocoa
// If ASCII but not printable, don't use insertText:replacementRange: // If ASCII but not printable, don't use insertText:replacementRange:
// Certain apps don't handle non-ASCII char insertions. // Certain apps don't handle non-ASCII char insertions.
if charCode < 0x80 && !isPrintable(charCode) { if charCode < 0x80, !isPrintable(charCode) {
return false return false
} }
@ -88,9 +89,10 @@ import Cocoa
} }
// MARK: Numeric Pad Processing. // MARK: Numeric Pad Processing.
if input.isNumericPad { if input.isNumericPad {
if !input.isLeft && !input.isRight && !input.isDown if !input.isLeft, !input.isRight, !input.isDown,
&& !input.isUp && !input.isSpace && isPrintable(charCode) !input.isUp, !input.isSpace, isPrintable(charCode)
{ {
clear() clear()
stateCallback(emptyState) stateCallback(emptyState)
@ -102,15 +104,19 @@ import Cocoa
} }
// MARK: Handle Candidates. // MARK: Handle Candidates.
if state is InputState.ChoosingCandidate { if state is InputState.ChoosingCandidate {
return _handleCandidateState( return _handleCandidateState(
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback) state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
)
} }
// MARK: Handle Associated Phrases. // MARK: Handle Associated Phrases.
if state is InputState.AssociatedPhrases { if state is InputState.AssociatedPhrases {
let result = _handleCandidateState( let result = _handleCandidateState(
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback) state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
)
if result { if result {
return true return true
} else { } else {
@ -119,13 +125,14 @@ import Cocoa
} }
// MARK: Handle Marking. // MARK: Handle Marking.
if state is InputState.Marking { if state is InputState.Marking {
let marking = state as! InputState.Marking let marking = state as! InputState.Marking
if _handleMarkingState( if _handleMarkingState(
state as! InputState.Marking, input: input, stateCallback: stateCallback, state as! InputState.Marking, input: input, stateCallback: stateCallback,
errorCallback: errorCallback) errorCallback: errorCallback
{ ) {
return true return true
} }
@ -134,7 +141,8 @@ import Cocoa
} }
// MARK: Handle BPMF Keys. // MARK: Handle BPMF Keys.
var composeReading: Bool = false
var composeReading = false
let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold
// See if Phonetic reading is valid. // See if Phonetic reading is valid.
@ -150,7 +158,6 @@ import Cocoa
stateCallback(inputting) stateCallback(inputting)
return true return true
} }
} }
// See if we have composition if Enter/Space is hit and buffer is not empty. // See if we have composition if Enter/Space is hit and buffer is not empty.
@ -187,7 +194,8 @@ import Cocoa
if mgrPrefs.useSCPCTypingMode { if mgrPrefs.useSCPCTypingMode {
let choosingCandidates: InputState.ChoosingCandidate = _buildCandidateState( let choosingCandidates: InputState.ChoosingCandidate = _buildCandidateState(
inputting, inputting,
useVerticalMode: input.useVerticalMode) useVerticalMode: input.useVerticalMode
)
if choosingCandidates.candidates.count == 1 { if choosingCandidates.candidates.count == 1 {
clear() clear()
let text: String = choosingCandidates.candidates.first ?? "" let text: String = choosingCandidates.candidates.first ?? ""
@ -200,7 +208,8 @@ import Cocoa
let associatedPhrases = let associatedPhrases =
buildAssociatePhraseState( buildAssociatePhraseState(
withKey: text, withKey: text,
useVerticalMode: input.useVerticalMode) as? InputState.AssociatedPhrases useVerticalMode: input.useVerticalMode
) as? InputState.AssociatedPhrases
if let associatedPhrases = associatedPhrases { if let associatedPhrases = associatedPhrases {
stateCallback(associatedPhrases) stateCallback(associatedPhrases)
} else { } else {
@ -247,7 +256,8 @@ import Cocoa
} }
let choosingCandidates = _buildCandidateState( let choosingCandidates = _buildCandidateState(
state as! InputState.NotEmpty, state as! InputState.NotEmpty,
useVerticalMode: input.useVerticalMode) useVerticalMode: input.useVerticalMode
)
stateCallback(choosingCandidates) stateCallback(choosingCandidates)
return true return true
} }
@ -255,59 +265,72 @@ import Cocoa
// MARK: - // MARK: -
// MARK: Esc // MARK: Esc
if input.isESC { return _handleEscWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) } if input.isESC { return _handleEscWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) }
// MARK: Cursor backward // MARK: Cursor backward
if input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward { if input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward {
return _handleBackwardWithState( return _handleBackwardWithState(
state, state,
input: input, input: input,
stateCallback: stateCallback, stateCallback: stateCallback,
errorCallback: errorCallback) errorCallback: errorCallback
)
} }
// MARK: Cursor forward // MARK: Cursor forward
if input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward { if input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward {
return _handleForwardWithState( return _handleForwardWithState(
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback) state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
)
} }
// MARK: Home // MARK: Home
if input.isHome || input.emacsKey == vChewingEmacsKey.home { if input.isHome || input.emacsKey == vChewingEmacsKey.home {
return _handleHomeWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) return _handleHomeWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
// MARK: End // MARK: End
if input.isEnd || input.emacsKey == vChewingEmacsKey.end { if input.isEnd || input.emacsKey == vChewingEmacsKey.end {
return _handleEndWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) return _handleEndWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
// MARK: Ctrl+PgLf or Shift+PgLf // MARK: Ctrl+PgLf or Shift+PgLf
if (input.isControlHold || input.isShiftHold) && (input.isOptionHold && input.isLeft) { if (input.isControlHold || input.isShiftHold) && (input.isOptionHold && input.isLeft) {
return _handleHomeWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) return _handleHomeWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
// MARK: Ctrl+PgRt or Shift+PgRt // MARK: Ctrl+PgRt or Shift+PgRt
if (input.isControlHold || input.isShiftHold) && (input.isOptionHold && input.isRight) { if (input.isControlHold || input.isShiftHold) && (input.isOptionHold && input.isRight) {
return _handleEndWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) return _handleEndWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
// MARK: AbsorbedArrowKey // MARK: AbsorbedArrowKey
if input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse { if input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse {
return _handleAbsorbedArrowKeyWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) return _handleAbsorbedArrowKeyWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
// MARK: Backspace // MARK: Backspace
if input.isBackSpace { if input.isBackSpace {
return _handleBackspaceWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) return _handleBackspaceWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
// MARK: Delete // MARK: Delete
if input.isDelete || input.emacsKey == vChewingEmacsKey.delete { if input.isDelete || input.emacsKey == vChewingEmacsKey.delete {
return _handleDeleteWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) return _handleDeleteWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
} }
// MARK: Enter // MARK: Enter
if input.isEnter { if input.isEnter {
return (input.isCommandHold && input.isControlHold) return (input.isCommandHold && input.isControlHold)
? _handleCtrlCommandEnterWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) ? _handleCtrlCommandEnterWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
@ -317,6 +340,7 @@ import Cocoa
// MARK: - // MARK: -
// MARK: Punctuation list // MARK: Punctuation list
if input.isSymbolMenuPhysicalKey && !input.isShiftHold { if input.isSymbolMenuPhysicalKey && !input.isShiftHold {
if !input.isOptionHold { if !input.isOptionHold {
if ifLangModelHasUnigrams(forKey: "_punctuation_list") { if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
@ -349,6 +373,7 @@ import Cocoa
} }
// MARK: Punctuation // MARK: Punctuation
// if nothing is matched, see if it's a punctuation key for current layout. // if nothing is matched, see if it's a punctuation key for current layout.
var punctuationNamePrefix = "" var punctuationNamePrefix = ""
@ -373,8 +398,8 @@ import Cocoa
state: state, state: state,
usingVerticalMode: input.useVerticalMode, usingVerticalMode: input.useVerticalMode,
stateCallback: stateCallback, stateCallback: stateCallback,
errorCallback: errorCallback) errorCallback: errorCallback
{ ) {
return true return true
} }
@ -387,8 +412,8 @@ import Cocoa
state: state, state: state,
usingVerticalMode: input.useVerticalMode, usingVerticalMode: input.useVerticalMode,
stateCallback: stateCallback, stateCallback: stateCallback,
errorCallback: errorCallback) errorCallback: errorCallback
{ ) {
return true return true
} }
@ -400,13 +425,14 @@ import Cocoa
state: state, state: state,
usingVerticalMode: input.useVerticalMode, usingVerticalMode: input.useVerticalMode,
stateCallback: stateCallback, stateCallback: stateCallback,
errorCallback: errorCallback) errorCallback: errorCallback
{ ) {
return true return true
} }
} }
// MARK: - Still Nothing. // MARK: - Still Nothing.
// Still nothing? Then we update the composing buffer. // Still nothing? Then we update the composing buffer.
// Note that some app has strange behavior if we don't do this, // Note that some app has strange behavior if we don't do this,
// "thinking" that the key is not actually consumed. // "thinking" that the key is not actually consumed.

View File

@ -27,10 +27,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
// MARK: - § Misc functions. // MARK: - § Misc functions.
@objc extension KeyHandler {
@objc extension KeyHandler {
func getCurrentMandarinParser() -> String { func getCurrentMandarinParser() -> String {
return (mgrPrefs.mandarinParserName + "_") mgrPrefs.mandarinParserName + "_"
} }
func _actualCandidateCursorIndex() -> Int { func _actualCandidateCursorIndex() -> Int {

View File

@ -27,9 +27,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
// MARK: - § State managements. // MARK: - § State managements.
@objc extension KeyHandler {
@objc extension KeyHandler {
// MARK: - State Building // MARK: - State Building
func buildInputtingState() -> InputState.Inputting { func buildInputtingState() -> InputState.Inputting {
// //
packageBufferStateMaterials() packageBufferStateMaterials()
@ -44,16 +45,17 @@ import Cocoa
// //
var tooltip = "" var tooltip = ""
if (resultOfBefore == "") && (resultOfAfter != "") { if resultOfBefore == "", resultOfAfter != "" {
tooltip = String(format: NSLocalizedString("Cursor is after \"%@\".", comment: ""), resultOfAfter) tooltip = String(format: NSLocalizedString("Cursor is after \"%@\".", comment: ""), resultOfAfter)
} }
if (resultOfBefore != "") && (resultOfAfter == "") { if resultOfBefore != "", resultOfAfter == "" {
tooltip = String(format: NSLocalizedString("Cursor is before \"%@\".", comment: ""), resultOfBefore) tooltip = String(format: NSLocalizedString("Cursor is before \"%@\".", comment: ""), resultOfBefore)
} }
if (resultOfBefore != "") && (resultOfAfter != "") { if resultOfBefore != "", resultOfAfter != "" {
tooltip = String( tooltip = String(
format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""), format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""),
resultOfAfter, resultOfBefore) resultOfAfter, resultOfBefore
)
} }
// //
@ -62,6 +64,7 @@ import Cocoa
} }
// MARK: - // MARK: -
func _buildCandidateState( func _buildCandidateState(
_ currentState: InputState.NotEmpty, _ currentState: InputState.NotEmpty,
useVerticalMode: Bool useVerticalMode: Bool
@ -72,18 +75,19 @@ import Cocoa
composingBuffer: currentState.composingBuffer, composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex, cursorIndex: currentState.cursorIndex,
candidates: candidatesArray, candidates: candidatesArray,
useVerticalMode: useVerticalMode) useVerticalMode: useVerticalMode
)
return state return state
} }
// MARK: - // MARK: -
func _handleMarkingState( func _handleMarkingState(
_ state: InputState.Marking, _ state: InputState.Marking,
input: keyParser, input: keyParser,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
if input.isESC { if input.isESC {
let inputting = buildInputtingState() let inputting = buildInputtingState()
stateCallback(inputting) stateCallback(inputting)
@ -106,7 +110,7 @@ import Cocoa
} }
// Shift + Left // Shift + Left
if (input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward) && (input.isShiftHold) { if input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward, input.isShiftHold {
var index = state.markerIndex var index = state.markerIndex
if index > 0 { if index > 0 {
index = UInt((state.composingBuffer as NSString).previousUtf16Position(for: Int(index))) index = UInt((state.composingBuffer as NSString).previousUtf16Position(for: Int(index)))
@ -114,7 +118,8 @@ import Cocoa
composingBuffer: state.composingBuffer, composingBuffer: state.composingBuffer,
cursorIndex: state.cursorIndex, cursorIndex: state.cursorIndex,
markerIndex: index, markerIndex: index,
readings: state.readings) readings: state.readings
)
marking.tooltipForInputting = state.tooltipForInputting marking.tooltipForInputting = state.tooltipForInputting
if marking.markedRange.length == 0 { if marking.markedRange.length == 0 {
@ -132,7 +137,7 @@ import Cocoa
} }
// Shift + Right // Shift + Right
if (input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward) && (input.isShiftHold) { if input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward, input.isShiftHold {
var index = state.markerIndex var index = state.markerIndex
// NSString Zonble NSStringUtils // NSString Zonble NSStringUtils
// 9B51408D // 9B51408D
@ -142,7 +147,8 @@ import Cocoa
composingBuffer: state.composingBuffer, composingBuffer: state.composingBuffer,
cursorIndex: state.cursorIndex, cursorIndex: state.cursorIndex,
markerIndex: index, markerIndex: index,
readings: state.readings) readings: state.readings
)
marking.tooltipForInputting = state.tooltipForInputting marking.tooltipForInputting = state.tooltipForInputting
if marking.markedRange.length == 0 { if marking.markedRange.length == 0 {
let inputting = marking.convertToInputting() let inputting = marking.convertToInputting()
@ -161,6 +167,7 @@ import Cocoa
} }
// MARK: - // MARK: -
func _handlePunctuation( func _handlePunctuation(
_ customPunctuation: String, _ customPunctuation: String,
state: InputState, state: InputState,
@ -179,9 +186,10 @@ import Cocoa
inputting.poppedText = poppedText inputting.poppedText = poppedText
stateCallback(inputting) stateCallback(inputting)
if mgrPrefs.useSCPCTypingMode && isPhoneticReadingBufferEmpty() { if mgrPrefs.useSCPCTypingMode, isPhoneticReadingBufferEmpty() {
let candidateState = _buildCandidateState( let candidateState = _buildCandidateState(
inputting, useVerticalMode: useVerticalMode) inputting, useVerticalMode: useVerticalMode
)
if candidateState.candidates.count == 1 { if candidateState.candidates.count == 1 {
clear() clear()
if let strPoppedText: String = candidateState.candidates.first { if let strPoppedText: String = candidateState.candidates.first {
@ -208,10 +216,11 @@ import Cocoa
} }
// MARK: - Enter // MARK: - Enter
@discardableResult func _handleEnterWithState( @discardableResult func _handleEnterWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback _: @escaping () -> Void
) -> Bool { ) -> Bool {
if !(state is InputState.Inputting) { if !(state is InputState.Inputting) {
return false return false
@ -230,10 +239,11 @@ import Cocoa
} }
// MARK: - CMD+Enter // MARK: - CMD+Enter
func _handleCtrlCommandEnterWithState( func _handleCtrlCommandEnterWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback _: @escaping () -> Void
) -> Bool { ) -> Bool {
if !(state is InputState.Inputting) { if !(state is InputState.Inputting) {
return false return false
@ -255,6 +265,7 @@ import Cocoa
} }
// MARK: - Backspace (macOS Delete) // MARK: - Backspace (macOS Delete)
func _handleBackspaceWithState( func _handleBackspaceWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -278,7 +289,7 @@ import Cocoa
doBackSpaceToPhoneticReadingBuffer() doBackSpaceToPhoneticReadingBuffer()
} }
if isPhoneticReadingBufferEmpty() && (getBuilderLength() == 0) { if isPhoneticReadingBufferEmpty(), getBuilderLength() == 0 {
let empty = InputState.EmptyIgnoringPreviousState() let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty) stateCallback(empty)
} else { } else {
@ -289,6 +300,7 @@ import Cocoa
} }
// MARK: - PC Delete (macOS Fn+BackSpace) // MARK: - PC Delete (macOS Fn+BackSpace)
func _handleDeleteWithState( func _handleDeleteWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -303,7 +315,8 @@ import Cocoa
deleteBuilderReadingAfterCursor() deleteBuilderReadingAfterCursor()
_walk() _walk()
let inputting = buildInputtingState() let inputting = buildInputtingState()
if inputting.composingBuffer.count == 0 { // count > 0!isEmpty滿
if !inputting.composingBuffer.isEmpty {
let empty = InputState.EmptyIgnoringPreviousState() let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty) stateCallback(empty)
} else { } else {
@ -324,6 +337,7 @@ import Cocoa
} }
// MARK: - 90 // MARK: - 90
func _handleAbsorbedArrowKeyWithState( func _handleAbsorbedArrowKeyWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -341,6 +355,7 @@ import Cocoa
} }
// MARK: - Home // MARK: - Home
func _handleHomeWithState( func _handleHomeWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -371,6 +386,7 @@ import Cocoa
} }
// MARK: - End // MARK: - End
func _handleEndWithState( func _handleEndWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
@ -401,10 +417,11 @@ import Cocoa
} }
// MARK: - Esc // MARK: - Esc
func _handleEscWithState( func _handleEscWithState(
_ state: InputState, _ state: InputState,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback _: @escaping () -> Void
) -> Bool { ) -> Bool {
if !(state is InputState.Inputting) { return false } if !(state is InputState.Inputting) { return false }
@ -433,14 +450,15 @@ import Cocoa
} }
return true return true
} }
// MARK: - // MARK: -
func _handleForwardWithState( func _handleForwardWithState(
_ state: InputState, _ state: InputState,
input: keyParser, input: keyParser,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
if !(state is InputState.Inputting) { return false } if !(state is InputState.Inputting) { return false }
if !isPhoneticReadingBufferEmpty() { if !isPhoneticReadingBufferEmpty() {
@ -461,7 +479,8 @@ import Cocoa
composingBuffer: currentState.composingBuffer, composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex, cursorIndex: currentState.cursorIndex,
markerIndex: UInt(nextPosition), markerIndex: UInt(nextPosition),
readings: _currentReadings()) readings: _currentReadings()
)
marking.tooltipForInputting = currentState.tooltip marking.tooltipForInputting = currentState.tooltip
stateCallback(marking) stateCallback(marking)
} else { } else {
@ -484,13 +503,13 @@ import Cocoa
} }
// MARK: - // MARK: -
func _handleBackwardWithState( func _handleBackwardWithState(
_ state: InputState, _ state: InputState,
input: keyParser, input: keyParser,
stateCallback: @escaping (InputState) -> Void, stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void errorCallback: @escaping () -> Void
) -> Bool { ) -> Bool {
if !(state is InputState.Inputting) { return false } if !(state is InputState.Inputting) { return false }
if !isPhoneticReadingBufferEmpty() { if !isPhoneticReadingBufferEmpty() {
@ -511,7 +530,8 @@ import Cocoa
composingBuffer: currentState.composingBuffer, composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex, cursorIndex: currentState.cursorIndex,
markerIndex: UInt(previousPosition), markerIndex: UInt(previousPosition),
readings: _currentReadings()) readings: _currentReadings()
)
marking.tooltipForInputting = currentState.tooltip marking.tooltipForInputting = currentState.tooltip
stateCallback(marking) stateCallback(marking)
} else { } else {

View File

@ -83,8 +83,8 @@ import Cocoa
} }
// CharCodes: https://theasciicode.com.ar/ascii-control-characters/horizontal-tab-ascii-code-9.html // CharCodes: https://theasciicode.com.ar/ascii-control-characters/horizontal-tab-ascii-code-9.html
enum CharCode: UInt /*16*/ { enum CharCode: UInt /* 16 */ {
case yajuusenpai = 114_514_19_19_810_893 case yajuusenpai = 114_514_191_191_810_893
// CharCode is not reliable at all. KeyCode is the most appropriate choice due to its accuracy. // CharCode is not reliable at all. KeyCode is the most appropriate choice due to its accuracy.
// KeyCode doesn't give a phuque about the character sent through macOS keyboard layouts ... // KeyCode doesn't give a phuque about the character sent through macOS keyboard layouts ...
// ... but only focuses on which physical key is pressed. // ... but only focuses on which physical key is pressed.
@ -121,7 +121,8 @@ class keyParser: NSObject {
self.keyCode = keyCode self.keyCode = keyCode
self.charCode = AppleKeyboardConverter.cnvApple2ABC(charCode) self.charCode = AppleKeyboardConverter.cnvApple2ABC(charCode)
emacsKey = EmacsKeyHelper.detect( emacsKey = EmacsKeyHelper.detect(
charCode: AppleKeyboardConverter.cnvApple2ABC(charCode), flags: flags) charCode: AppleKeyboardConverter.cnvApple2ABC(charCode), flags: flags
)
// Define Arrow Keys // Define Arrow Keys
cursorForwardKey = useVerticalMode ? .kDownArrow : .kRightArrow cursorForwardKey = useVerticalMode ? .kDownArrow : .kRightArrow
cursorBackwardKey = useVerticalMode ? .kUpArrow : .kLeftArrow cursorBackwardKey = useVerticalMode ? .kUpArrow : .kLeftArrow
@ -141,7 +142,8 @@ class keyParser: NSObject {
isFlagChanged = (event.type == .flagsChanged) ? true : false isFlagChanged = (event.type == .flagsChanged) ? true : false
useVerticalMode = isVerticalMode useVerticalMode = isVerticalMode
let charCode: UInt16 = { let charCode: UInt16 = {
guard let inputText = event.characters, inputText.count > 0 else { // count > 0!isEmpty滿
guard let inputText = event.characters, !inputText.isEmpty else {
return 0 return 0
} }
let first = inputText[inputText.startIndex].utf16.first! let first = inputText[inputText.startIndex].utf16.first!
@ -149,7 +151,8 @@ class keyParser: NSObject {
}() }()
self.charCode = AppleKeyboardConverter.cnvApple2ABC(charCode) self.charCode = AppleKeyboardConverter.cnvApple2ABC(charCode)
emacsKey = EmacsKeyHelper.detect( emacsKey = EmacsKeyHelper.detect(
charCode: AppleKeyboardConverter.cnvApple2ABC(charCode), flags: flags) charCode: AppleKeyboardConverter.cnvApple2ABC(charCode), flags: flags
)
// Define Arrow Keys in the same way above. // Define Arrow Keys in the same way above.
cursorForwardKey = useVerticalMode ? .kDownArrow : .kRightArrow cursorForwardKey = useVerticalMode ? .kDownArrow : .kRightArrow
cursorBackwardKey = useVerticalMode ? .kUpArrow : .kLeftArrow cursorBackwardKey = useVerticalMode ? .kUpArrow : .kLeftArrow
@ -303,7 +306,6 @@ class keyParser: NSObject {
// ![input isShift] 使 Shift // ![input isShift] 使 Shift
KeyCode(rawValue: keyCode) == KeyCode.kSymbolMenuPhysicalKey KeyCode(rawValue: keyCode) == KeyCode.kSymbolMenuPhysicalKey
} }
} }
@objc enum vChewingEmacsKey: UInt16 { @objc enum vChewingEmacsKey: UInt16 {

View File

@ -27,7 +27,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
extension NSString { extension NSString {
/// Converts the index in an NSString to the index in a Swift string. /// Converts the index in an NSString to the index in a Swift string.
/// ///
/// An Emoji might be compose by more than one UTF-16 code points, however /// An Emoji might be compose by more than one UTF-16 code points, however

View File

@ -26,7 +26,7 @@ import Cocoa
extension String { extension String {
fileprivate mutating func selfReplace(_ strOf: String, _ strWith: String = "") { fileprivate mutating func selfReplace(_ strOf: String, _ strWith: String = "") {
self = self.replacingOccurrences(of: strOf, with: strWith) self = replacingOccurrences(of: strOf, with: strWith)
} }
} }

View File

@ -31,7 +31,6 @@ public protocol FSEventStreamHelperDelegate: AnyObject {
} }
public class FSEventStreamHelper: NSObject { public class FSEventStreamHelper: NSObject {
public struct Event { public struct Event {
var path: String var path: String
var flags: FSEventStreamEventFlags var flags: FSEventStreamEventFlags
@ -59,7 +58,7 @@ public class FSEventStreamHelper: NSObject {
let stream = FSEventStreamCreate( let stream = FSEventStreamCreate(
nil, nil,
{ {
(_, clientCallBackInfo, eventCount, eventPaths, eventFlags, eventIds) in _, clientCallBackInfo, eventCount, eventPaths, eventFlags, eventIds in
let helper = Unmanaged<FSEventStreamHelper>.fromOpaque(clientCallBackInfo!) let helper = Unmanaged<FSEventStreamHelper>.fromOpaque(clientCallBackInfo!)
.takeUnretainedValue() .takeUnretainedValue()
let pathsBase = eventPaths.assumingMemoryBound(to: UnsafePointer<CChar>.self) let pathsBase = eventPaths.assumingMemoryBound(to: UnsafePointer<CChar>.self)
@ -70,7 +69,8 @@ public class FSEventStreamHelper: NSObject {
FSEventStreamHelper.Event( FSEventStreamHelper.Event(
path: String(cString: pathsPtr[$0]), path: String(cString: pathsPtr[$0]),
flags: flagsPtr[$0], flags: flagsPtr[$0],
id: eventIDsPtr[$0]) id: eventIDsPtr[$0]
)
} }
helper.delegate?.helper(helper, didReceive: events) helper.delegate?.helper(helper, didReceive: events)
}, },

View File

@ -30,14 +30,17 @@ public class IME: NSObject {
static let dlgOpenPath = NSOpenPanel() static let dlgOpenPath = NSOpenPanel()
// MARK: - // MARK: -
@objc static var areWeUsingOurOwnPhraseEditor: Bool = false @objc static var areWeUsingOurOwnPhraseEditor: Bool = false
// MARK: - ctlInputMethod // MARK: - ctlInputMethod
static func getInputMode() -> InputMode { static func getInputMode() -> InputMode {
ctlInputMethod.currentKeyHandler.inputMode ctlInputMethod.currentKeyHandler.inputMode
} }
// MARK: - Print debug information to the console. // MARK: - Print debug information to the console.
@objc static func prtDebugIntel(_ strPrint: String) { @objc static func prtDebugIntel(_ strPrint: String) {
if mgrPrefs.isDebugModeEnabled { if mgrPrefs.isDebugModeEnabled {
NSLog("vChewingErrorCallback: %@", strPrint) NSLog("vChewingErrorCallback: %@", strPrint)
@ -45,11 +48,13 @@ public class IME: NSObject {
} }
// MARK: - Tell whether this IME is running with Root privileges. // MARK: - Tell whether this IME is running with Root privileges.
@objc static var isSudoMode: Bool { @objc static var isSudoMode: Bool {
NSUserName() == "root" NSUserName() == "root"
} }
// MARK: - Initializing Language Models. // MARK: - Initializing Language Models.
@objc static func initLangModels(userOnly: Bool) { @objc static func initLangModels(userOnly: Bool) {
if !userOnly { if !userOnly {
mgrLangModel.loadDataModels() // mgrLangModel.loadDataModels() //
@ -63,6 +68,7 @@ public class IME: NSObject {
} }
// MARK: - System Dark Mode Status Detector. // MARK: - System Dark Mode Status Detector.
@objc static func isDarkMode() -> Bool { @objc static func isDarkMode() -> Bool {
if #available(macOS 10.15, *) { if #available(macOS 10.15, *) {
let appearanceDescription = NSApplication.shared.effectiveAppearance.debugDescription let appearanceDescription = NSApplication.shared.effectiveAppearance.debugDescription
@ -83,6 +89,7 @@ public class IME: NSObject {
} }
// MARK: - Open a phrase data file. // MARK: - Open a phrase data file.
static func openPhraseFile(userFileAt path: String) { static func openPhraseFile(userFileAt path: String) {
func checkIfUserFilesExist() -> Bool { func checkIfUserFilesExist() -> Bool {
if !mgrLangModel.chkUserLMFilesExist(InputMode.imeModeCHS) if !mgrLangModel.chkUserLMFilesExist(InputMode.imeModeCHS)
@ -90,12 +97,15 @@ public class IME: NSObject {
{ {
let content = String( let content = String(
format: NSLocalizedString( format: NSLocalizedString(
"Please check the permission at \"%@\".", comment: ""), "Please check the permission at \"%@\".", comment: ""
mgrLangModel.dataFolderPath(isDefaultFolder: false)) ),
mgrLangModel.dataFolderPath(isDefaultFolder: false)
)
ctlNonModalAlertWindow.shared.show( ctlNonModalAlertWindow.shared.show(
title: NSLocalizedString("Unable to create the user phrase file.", comment: ""), title: NSLocalizedString("Unable to create the user phrase file.", comment: ""),
content: content, confirmButtonTitle: NSLocalizedString("OK", comment: ""), content: content, confirmButtonTitle: NSLocalizedString("OK", comment: ""),
cancelButtonTitle: nil, cancelAsDefault: false, delegate: nil) cancelButtonTitle: nil, cancelAsDefault: false, delegate: nil
)
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
return false return false
} }
@ -109,12 +119,14 @@ public class IME: NSObject {
} }
// MARK: - Trash a file if it exists. // MARK: - Trash a file if it exists.
@discardableResult static func trashTargetIfExists(_ path: String) -> Bool { @discardableResult static func trashTargetIfExists(_ path: String) -> Bool {
do { do {
if FileManager.default.fileExists(atPath: path) { if FileManager.default.fileExists(atPath: path) {
// //
try FileManager.default.trashItem( try FileManager.default.trashItem(
at: URL(fileURLWithPath: path), resultingItemURL: nil) at: URL(fileURLWithPath: path), resultingItemURL: nil
)
} else { } else {
NSLog("Item doesn't exist: \(path)") NSLog("Item doesn't exist: \(path)")
} }
@ -126,6 +138,7 @@ public class IME: NSObject {
} }
// MARK: - Uninstall the input method. // MARK: - Uninstall the input method.
@discardableResult static func uninstall(isSudo: Bool = false, selfKill: Bool = true) -> Int32 { @discardableResult static func uninstall(isSudo: Bool = false, selfKill: Bool = true) -> Int32 {
// Bundle.main.bundleURL便使 sudo // Bundle.main.bundleURL便使 sudo
guard let bundleID = Bundle.main.bundleIdentifier else { guard let bundleID = Bundle.main.bundleIdentifier else {
@ -155,8 +168,8 @@ public class IME: NSObject {
let objFullPath = pathLibrary + pathUnitKeyboardLayouts + objPath let objFullPath = pathLibrary + pathUnitKeyboardLayouts + objPath
if !IME.trashTargetIfExists(objFullPath) { return -1 } if !IME.trashTargetIfExists(objFullPath) { return -1 }
} }
if CommandLine.arguments.count > 2 && CommandLine.arguments[2] == "--all" if CommandLine.arguments.count > 2, CommandLine.arguments[2] == "--all",
&& CommandLine.arguments[1] == "uninstall" CommandLine.arguments[1] == "uninstall"
{ {
// 使 // 使
// 使 symbol link // 使 symbol link
@ -177,6 +190,7 @@ public class IME: NSObject {
} }
// MARK: - Registering the input method. // MARK: - Registering the input method.
@discardableResult static func registerInputMethod() -> Int32 { @discardableResult static func registerInputMethod() -> Int32 {
guard let bundleID = Bundle.main.bundleIdentifier else { guard let bundleID = Bundle.main.bundleIdentifier else {
return -1 return -1
@ -217,7 +231,7 @@ public class IME: NSObject {
} }
} }
if CommandLine.arguments.count > 2 && CommandLine.arguments[2] == "--all" { if CommandLine.arguments.count > 2, CommandLine.arguments[2] == "--all" {
let enabled = InputSourceHelper.enableAllInputMode(for: bundleID) let enabled = InputSourceHelper.enableAllInputMode(for: bundleID)
NSLog( NSLog(
enabled enabled
@ -228,10 +242,12 @@ public class IME: NSObject {
} }
// MARK: - ASCII // MARK: - ASCII
struct CarbonKeyboardLayout { struct CarbonKeyboardLayout {
var strName: String = "" var strName: String = ""
var strValue: String = "" var strValue: String = ""
} }
static let arrWhitelistedKeyLayoutsASCII: [String] = [ static let arrWhitelistedKeyLayoutsASCII: [String] = [
"com.apple.keylayout.ABC", "com.apple.keylayout.ABC",
"com.apple.keylayout.ABC-AZERTY", "com.apple.keylayout.ABC-AZERTY",
@ -247,12 +263,14 @@ public class IME: NSObject {
// macOS // macOS
var arrKeyLayouts: [IME.CarbonKeyboardLayout] = [] var arrKeyLayouts: [IME.CarbonKeyboardLayout] = []
arrKeyLayouts += [ arrKeyLayouts += [
IME.CarbonKeyboardLayout.init( IME.CarbonKeyboardLayout(
strName: NSLocalizedString("Apple Chewing - Dachen", comment: ""), strName: NSLocalizedString("Apple Chewing - Dachen", comment: ""),
strValue: "com.apple.keylayout.ZhuyinBopomofo"), strValue: "com.apple.keylayout.ZhuyinBopomofo"
IME.CarbonKeyboardLayout.init( ),
IME.CarbonKeyboardLayout(
strName: NSLocalizedString("Apple Chewing - Eten Traditional", comment: ""), strName: NSLocalizedString("Apple Chewing - Eten Traditional", comment: ""),
strValue: "com.apple.keylayout.ZhuyinEten"), strValue: "com.apple.keylayout.ZhuyinEten"
),
] ]
// ASCII // ASCII
@ -270,8 +288,8 @@ public class IME: NSObject {
} }
if let ptrASCIICapable = TISGetInputSourceProperty( if let ptrASCIICapable = TISGetInputSourceProperty(
source, kTISPropertyInputSourceIsASCIICapable) source, kTISPropertyInputSourceIsASCIICapable
{ ) {
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(ptrASCIICapable) let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(ptrASCIICapable)
.takeUnretainedValue() .takeUnretainedValue()
if asciiCapable != kCFBooleanTrue { if asciiCapable != kCFBooleanTrue {
@ -302,13 +320,13 @@ public class IME: NSObject {
if sourceID.contains("vChewing") { if sourceID.contains("vChewing") {
arrKeyLayoutsMACV += [ arrKeyLayoutsMACV += [
IME.CarbonKeyboardLayout.init(strName: localizedName, strValue: sourceID) IME.CarbonKeyboardLayout(strName: localizedName, strValue: sourceID)
] ]
} }
if IME.arrWhitelistedKeyLayoutsASCII.contains(sourceID) { if IME.arrWhitelistedKeyLayoutsASCII.contains(sourceID) {
arrKeyLayoutsASCII += [ arrKeyLayoutsASCII += [
IME.CarbonKeyboardLayout.init(strName: localizedName, strValue: sourceID) IME.CarbonKeyboardLayout(strName: localizedName, strValue: sourceID)
] ]
} }
} }
@ -316,7 +334,6 @@ public class IME: NSObject {
arrKeyLayouts += arrKeyLayoutsASCII arrKeyLayouts += arrKeyLayoutsASCII
return arrKeyLayouts return arrKeyLayouts
} }
} }
// MARK: - Root Extensions // MARK: - Root Extensions
@ -331,6 +348,7 @@ extension RangeReplaceableCollection where Element: Hashable {
} }
// MARK: - Error Extension // MARK: - Error Extension
extension String: Error {} extension String: Error {}
extension String: LocalizedError { extension String: LocalizedError {
public var errorDescription: String? { public var errorDescription: String? {
@ -339,6 +357,7 @@ extension String: LocalizedError {
} }
// MARK: - Ensuring trailing slash of a string: // MARK: - Ensuring trailing slash of a string:
extension String { extension String {
mutating func ensureTrailingSlash() { mutating func ensureTrailingSlash() {
if !hasSuffix("/") { if !hasSuffix("/") {

View File

@ -28,9 +28,8 @@ import Carbon
import Cocoa import Cocoa
public class InputSourceHelper: NSObject { public class InputSourceHelper: NSObject {
@available(*, unavailable) @available(*, unavailable)
public override init() { override public init() {
super.init() super.init()
} }
@ -89,7 +88,7 @@ public class InputSourceHelper: NSObject {
} }
let bundleID = Unmanaged<CFString>.fromOpaque(bundleIDPtr).takeUnretainedValue() let bundleID = Unmanaged<CFString>.fromOpaque(bundleIDPtr).takeUnretainedValue()
if String(bundleID) == inputSourceBundleD { if String(bundleID) == inputSourceBundleD {
let modeEnabled = self.enable(inputSource: source) let modeEnabled = enable(inputSource: source)
if !modeEnabled { if !modeEnabled {
return false return false
} }
@ -111,18 +110,16 @@ public class InputSourceHelper: NSObject {
let inputsSourceBundleID = Unmanaged<CFString>.fromOpaque(bundleIDPtr) let inputsSourceBundleID = Unmanaged<CFString>.fromOpaque(bundleIDPtr)
.takeUnretainedValue() .takeUnretainedValue()
let inputsSourceModeID = Unmanaged<CFString>.fromOpaque(modePtr).takeUnretainedValue() let inputsSourceModeID = Unmanaged<CFString>.fromOpaque(modePtr).takeUnretainedValue()
if modeID == String(inputsSourceModeID) && bundleID == String(inputsSourceBundleID) { if modeID == String(inputsSourceModeID), bundleID == String(inputsSourceBundleID) {
let enabled = enable(inputSource: source) let enabled = enable(inputSource: source)
print( print(
"Attempt to enable input source of mode: \(modeID), bundle ID: \(bundleID), result: \(enabled)" "Attempt to enable input source of mode: \(modeID), bundle ID: \(bundleID), result: \(enabled)"
) )
return enabled return enabled
} }
} }
print("Failed to find any matching input source of mode: \(modeID), bundle ID: \(bundleID)") print("Failed to find any matching input source of mode: \(modeID), bundle ID: \(bundleID)")
return false return false
} }
@objc(disableInputSource:) @objc(disableInputSource:)
@ -136,5 +133,4 @@ public class InputSourceHelper: NSObject {
let status = TISRegisterInputSource(url as CFURL) let status = TISRegisterInputSource(url as CFURL)
return status == noErr return status == noErr
} }
} }

View File

@ -50,12 +50,14 @@ enum VersionUpdateApiError: Error, LocalizedError {
return String( return String(
format: NSLocalizedString( format: NSLocalizedString(
"There may be no internet connection or the server failed to respond.\n\nError message: %@", "There may be no internet connection or the server failed to respond.\n\nError message: %@",
comment: ""), message) comment: ""
), message
)
} }
} }
} }
struct VersionUpdateApi { enum VersionUpdateApi {
static let kCheckUpdateAutomatically = "CheckUpdateAutomatically" static let kCheckUpdateAutomatically = "CheckUpdateAutomatically"
static let kNextUpdateCheckDateKey = "NextUpdateCheckDate" static let kNextUpdateCheckDateKey = "NextUpdateCheckDate"
static let kUpdateInfoEndpointKey = "UpdateInfoEndpoint" static let kUpdateInfoEndpointKey = "UpdateInfoEndpoint"
@ -75,7 +77,8 @@ struct VersionUpdateApi {
let request = URLRequest( let request = URLRequest(
url: updateInfoURL, cachePolicy: .reloadIgnoringLocalCacheData, url: updateInfoURL, cachePolicy: .reloadIgnoringLocalCacheData,
timeoutInterval: kTimeoutInterval) timeoutInterval: kTimeoutInterval
)
let task = URLSession.shared.dataTask(with: request) { data, _, error in let task = URLSession.shared.dataTask(with: request) { data, _, error in
if let error = error { if let error = error {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -92,7 +95,8 @@ struct VersionUpdateApi {
do { do {
guard guard
let plist = try PropertyListSerialization.propertyList( let plist = try PropertyListSerialization.propertyList(
from: data ?? Data(), options: [], format: nil) as? [AnyHashable: Any], from: data ?? Data(), options: [], format: nil
) as? [AnyHashable: Any],
let remoteVersion = plist[kCFBundleVersionKey] as? String, let remoteVersion = plist[kCFBundleVersionKey] as? String,
let infoDict = Bundle.main.infoDictionary let infoDict = Bundle.main.infoDictionary
else { else {
@ -109,7 +113,8 @@ struct VersionUpdateApi {
let currentVersion = infoDict[kCFBundleVersionKey as String] as? String ?? "" let currentVersion = infoDict[kCFBundleVersionKey as String] as? String ?? ""
let result = currentVersion.compare( let result = currentVersion.compare(
remoteVersion, options: .numeric, range: nil, locale: nil) remoteVersion, options: .numeric, range: nil, locale: nil
)
if result != .orderedAscending { if result != .orderedAscending {
DispatchQueue.main.async { DispatchQueue.main.async {

View File

@ -38,7 +38,6 @@ extension ctlCandidate {
@objc(ctlInputMethod) @objc(ctlInputMethod)
class ctlInputMethod: IMKInputController { class ctlInputMethod: IMKInputController {
@objc static let kIMEModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS" @objc static let kIMEModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
@objc static let kIMEModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT" @objc static let kIMEModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
@objc static let kIMEModeNULL = "org.atelierInmu.inputmethod.vChewing.IMENULL" @objc static let kIMEModeNULL = "org.atelierInmu.inputmethod.vChewing.IMENULL"
@ -51,15 +50,15 @@ class ctlInputMethod: IMKInputController {
private var currentClient: Any? private var currentClient: Any?
private var keyHandler: KeyHandler = KeyHandler() private var keyHandler: KeyHandler = .init()
private var state: InputState = InputState.Empty() private var state: InputState = .Empty()
// keyHandler 調 keyHandler // keyHandler 調 keyHandler
// InputState ctlInputMethod // InputState ctlInputMethod
// keyHandler keyParser // keyHandler keyParser
// currentKeyHandler keyHandler // currentKeyHandler keyHandler
// currentKeyHandler // currentKeyHandler
static var currentKeyHandler: KeyHandler = KeyHandler() static var currentKeyHandler: KeyHandler = .init()
@objc static var currentInputMode = mgrPrefs.mostRecentInputMode @objc static var currentInputMode = mgrPrefs.mostRecentInputMode
// MARK: - Keyboard Layout Specifier // MARK: - Keyboard Layout Specifier
@ -85,7 +84,7 @@ class ctlInputMethod: IMKInputController {
func resetKeyHandler() { func resetKeyHandler() {
if let currentClient = currentClient { if let currentClient = currentClient {
keyHandler.clear() keyHandler.clear()
self.handle(state: InputState.Empty(), client: currentClient) handle(state: InputState.Empty(), client: currentClient)
} }
} }
@ -103,7 +102,7 @@ class ctlInputMethod: IMKInputController {
if bundleCheckID != Bundle.main.bundleIdentifier { if bundleCheckID != Bundle.main.bundleIdentifier {
// Override the keyboard layout to the basic one. // Override the keyboard layout to the basic one.
setKeyLayout() setKeyLayout()
self.handle(state: .Empty(), client: client) handle(state: .Empty(), client: client)
} }
} }
(NSApp.delegate as? AppDelegate)?.checkForUpdate() (NSApp.delegate as? AppDelegate)?.checkForUpdate()
@ -112,11 +111,11 @@ class ctlInputMethod: IMKInputController {
override func deactivateServer(_ client: Any!) { override func deactivateServer(_ client: Any!) {
keyHandler.clear() keyHandler.clear()
currentClient = nil currentClient = nil
self.handle(state: .Empty(), client: client) handle(state: .Empty(), client: client)
self.handle(state: .Deactivated(), client: client) handle(state: .Deactivated(), client: client)
} }
override func setValue(_ value: Any!, forTag tag: Int, client: Any!) { override func setValue(_ value: Any!, forTag _: Int, client: Any!) {
var newInputMode = InputMode(rawValue: value as? String ?? InputMode.imeModeNULL.rawValue) var newInputMode = InputMode(rawValue: value as? String ?? InputMode.imeModeNULL.rawValue)
switch newInputMode { switch newInputMode {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
@ -129,7 +128,6 @@ class ctlInputMethod: IMKInputController {
mgrLangModel.loadDataModel(newInputMode) mgrLangModel.loadDataModel(newInputMode)
if keyHandler.inputMode != newInputMode { if keyHandler.inputMode != newInputMode {
UserDefaults.standard.synchronize() UserDefaults.standard.synchronize()
keyHandler.clear() keyHandler.clear()
keyHandler.inputMode = newInputMode keyHandler.inputMode = newInputMode
@ -137,7 +135,7 @@ class ctlInputMethod: IMKInputController {
if bundleCheckID != Bundle.main.bundleIdentifier { if bundleCheckID != Bundle.main.bundleIdentifier {
// Remember to override the keyboard layout again -- treat this as an activate event. // Remember to override the keyboard layout again -- treat this as an activate event.
setKeyLayout() setKeyLayout()
self.handle(state: .Empty(), client: client) handle(state: .Empty(), client: client)
} }
} }
} }
@ -148,13 +146,12 @@ class ctlInputMethod: IMKInputController {
// MARK: - IMKServerInput protocol methods // MARK: - IMKServerInput protocol methods
override func recognizedEvents(_ sender: Any!) -> Int { override func recognizedEvents(_: Any!) -> Int {
let events: NSEvent.EventTypeMask = [.keyDown, .flagsChanged] let events: NSEvent.EventTypeMask = [.keyDown, .flagsChanged]
return Int(events.rawValue) return Int(events.rawValue)
} }
override func handle(_ event: NSEvent!, client: Any!) -> Bool { override func handle(_ event: NSEvent!, client: Any!) -> Bool {
// flags使 KeyHandler // flags使 KeyHandler
// flags // flags
// event.type == .flagsChanged return false // event.type == .flagsChanged return false
@ -168,7 +165,8 @@ class ctlInputMethod: IMKInputController {
var textFrame = NSRect.zero var textFrame = NSRect.zero
let attributes: [AnyHashable: Any]? = (client as? IMKTextInput)?.attributes( let attributes: [AnyHashable: Any]? = (client as? IMKTextInput)?.attributes(
forCharacterIndex: 0, lineHeightRectangle: &textFrame) forCharacterIndex: 0, lineHeightRectangle: &textFrame
)
let useVerticalMode = let useVerticalMode =
(attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false (attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false
@ -194,7 +192,6 @@ class ctlInputMethod: IMKInputController {
// MARK: - State Handling // MARK: - State Handling
extension ctlInputMethod { extension ctlInputMethod {
private func handle(state newState: InputState, client: Any?) { private func handle(state newState: InputState, client: Any?) {
let previous = state let previous = state
state = newState state = newState
@ -219,17 +216,16 @@ extension ctlInputMethod {
} }
private func commit(text: String, client: Any!) { private func commit(text: String, client: Any!) {
func kanjiConversionIfRequired(_ text: String) -> String { func kanjiConversionIfRequired(_ text: String) -> String {
if keyHandler.inputMode == InputMode.imeModeCHT { if keyHandler.inputMode == InputMode.imeModeCHT {
if !mgrPrefs.chineseConversionEnabled && mgrPrefs.shiftJISShinjitaiOutputEnabled { if !mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled {
return vChewingKanjiConverter.cnvTradToJIS(text) return vChewingKanjiConverter.cnvTradToJIS(text)
} }
if mgrPrefs.chineseConversionEnabled && !mgrPrefs.shiftJISShinjitaiOutputEnabled { if mgrPrefs.chineseConversionEnabled, !mgrPrefs.shiftJISShinjitaiOutputEnabled {
return vChewingKanjiConverter.cnvTradToKangXi(text) return vChewingKanjiConverter.cnvTradToKangXi(text)
} }
// //
if mgrPrefs.chineseConversionEnabled && mgrPrefs.shiftJISShinjitaiOutputEnabled { if mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled {
return vChewingKanjiConverter.cnvTradToJIS(text) return vChewingKanjiConverter.cnvTradToJIS(text)
} }
// if (!mgrPrefs.chineseConversionEnabled && !mgrPrefs.shiftJISShinjitaiOutputEnabled) || (keyHandler.inputMode != InputMode.imeModeCHT); // if (!mgrPrefs.chineseConversionEnabled && !mgrPrefs.shiftJISShinjitaiOutputEnabled) || (keyHandler.inputMode != InputMode.imeModeCHT);
@ -243,10 +239,11 @@ extension ctlInputMethod {
return return
} }
(client as? IMKTextInput)?.insertText( (client as? IMKTextInput)?.insertText(
buffer, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) buffer, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle(state: InputState.Deactivated, previous: InputState, client: Any?) { private func handle(state _: InputState.Deactivated, previous: InputState, client: Any?) {
currentClient = nil currentClient = nil
ctlCandidateCurrent?.delegate = nil ctlCandidateCurrent?.delegate = nil
@ -258,10 +255,11 @@ extension ctlInputMethod {
} }
(client as? IMKTextInput)?.setMarkedText( (client as? IMKTextInput)?.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0), "", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle(state: InputState.Empty, previous: InputState, client: Any?) { private func handle(state _: InputState.Empty, previous: InputState, client: Any?) {
ctlCandidateCurrent?.visible = false ctlCandidateCurrent?.visible = false
hideTooltip() hideTooltip()
@ -274,11 +272,12 @@ extension ctlInputMethod {
} }
client.setMarkedText( client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0), "", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle( private func handle(
state: InputState.EmptyIgnoringPreviousState, previous: InputState, client: Any! state _: InputState.EmptyIgnoringPreviousState, previous _: InputState, client: Any!
) { ) {
ctlCandidateCurrent?.visible = false ctlCandidateCurrent?.visible = false
hideTooltip() hideTooltip()
@ -289,10 +288,11 @@ extension ctlInputMethod {
client.setMarkedText( client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0), "", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle(state: InputState.Committing, previous: InputState, client: Any?) { private func handle(state: InputState.Committing, previous _: InputState, client: Any?) {
ctlCandidateCurrent?.visible = false ctlCandidateCurrent?.visible = false
hideTooltip() hideTooltip()
@ -306,10 +306,11 @@ extension ctlInputMethod {
} }
client.setMarkedText( client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0), "", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
} }
private func handle(state: InputState.Inputting, previous: InputState, client: Any?) { private func handle(state: InputState.Inputting, previous _: InputState, client: Any?) {
ctlCandidateCurrent?.visible = false ctlCandidateCurrent?.visible = false
hideTooltip() hideTooltip()
@ -326,15 +327,17 @@ extension ctlInputMethod {
// i.e. the client app needs to take care of where to put this composing buffer // i.e. the client app needs to take care of where to put this composing buffer
client.setMarkedText( client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0), state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
if !state.tooltip.isEmpty { if !state.tooltip.isEmpty {
show( show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer, tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.cursorIndex, client: client) cursorIndex: state.cursorIndex, client: client
)
} }
} }
private func handle(state: InputState.Marking, previous: InputState, client: Any?) { private func handle(state: InputState.Marking, previous _: InputState, client: Any?) {
ctlCandidateCurrent?.visible = false ctlCandidateCurrent?.visible = false
guard let client = client as? IMKTextInput else { guard let client = client as? IMKTextInput else {
hideTooltip() hideTooltip()
@ -345,18 +348,20 @@ extension ctlInputMethod {
// i.e. the client app needs to take care of where to put this composing buffer // i.e. the client app needs to take care of where to put this composing buffer
client.setMarkedText( client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0), state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
if state.tooltip.isEmpty { if state.tooltip.isEmpty {
hideTooltip() hideTooltip()
} else { } else {
show( show(
tooltip: state.tooltip, composingBuffer: state.composingBuffer, tooltip: state.tooltip, composingBuffer: state.composingBuffer,
cursorIndex: state.markerIndex, client: client) cursorIndex: state.markerIndex, client: client
)
} }
} }
private func handle(state: InputState.ChoosingCandidate, previous: InputState, client: Any?) { private func handle(state: InputState.ChoosingCandidate, previous _: InputState, client: Any?) {
hideTooltip() hideTooltip()
guard let client = client as? IMKTextInput else { guard let client = client as? IMKTextInput else {
ctlCandidateCurrent?.visible = false ctlCandidateCurrent?.visible = false
@ -367,11 +372,12 @@ extension ctlInputMethod {
// i.e. the client app needs to take care of where to put this composing buffer // i.e. the client app needs to take care of where to put this composing buffer
client.setMarkedText( client.setMarkedText(
state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0), state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
show(candidateWindowWith: state, client: client) show(candidateWindowWith: state, client: client)
} }
private func handle(state: InputState.AssociatedPhrases, previous: InputState, client: Any?) { private func handle(state: InputState.AssociatedPhrases, previous _: InputState, client: Any?) {
hideTooltip() hideTooltip()
guard let client = client as? IMKTextInput else { guard let client = client as? IMKTextInput else {
ctlCandidateCurrent?.visible = false ctlCandidateCurrent?.visible = false
@ -379,7 +385,8 @@ extension ctlInputMethod {
} }
client.setMarkedText( client.setMarkedText(
"", selectionRange: NSRange(location: 0, length: 0), "", selectionRange: NSRange(location: 0, length: 0),
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
)
show(candidateWindowWith: state, client: client) show(candidateWindowWith: state, client: client)
} }
} }
@ -387,7 +394,6 @@ extension ctlInputMethod {
// MARK: - // MARK: -
extension ctlInputMethod { extension ctlInputMethod {
private func show(candidateWindowWith state: InputState, client: Any!) { private func show(candidateWindowWith state: InputState, client: Any!) {
let useVerticalMode: Bool = { let useVerticalMode: Bool = {
var useVerticalMode = false var useVerticalMode = false
@ -449,9 +455,11 @@ extension ctlInputMethod {
} }
ctlCandidateCurrent?.keyLabelFont = labelFont( ctlCandidateCurrent?.keyLabelFont = labelFont(
name: mgrPrefs.candidateKeyLabelFontName, size: keyLabelSize) name: mgrPrefs.candidateKeyLabelFontName, size: keyLabelSize
)
ctlCandidateCurrent?.candidateFont = candidateFont( ctlCandidateCurrent?.candidateFont = candidateFont(
name: mgrPrefs.candidateTextFontName, size: textSize) name: mgrPrefs.candidateTextFontName, size: textSize
)
let candidateKeys = mgrPrefs.candidateKeys let candidateKeys = mgrPrefs.candidateKeys
let keyLabels = let keyLabels =
@ -468,42 +476,47 @@ extension ctlInputMethod {
ctlCandidateCurrent?.visible = true ctlCandidateCurrent?.visible = true
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
var cursor: Int = 0 var cursor = 0
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
cursor = Int(state.cursorIndex) cursor = Int(state.cursorIndex)
if cursor == state.composingBuffer.count && cursor != 0 { if cursor == state.composingBuffer.count, cursor != 0 {
cursor -= 1 cursor -= 1
} }
} }
while lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor >= 0 { while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
(client as? IMKTextInput)?.attributes( (client as? IMKTextInput)?.attributes(
forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect) forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect
)
cursor -= 1 cursor -= 1
} }
if useVerticalMode { if useVerticalMode {
ctlCandidateCurrent?.set( ctlCandidateCurrent?.set(
windowTopLeftPoint: NSPoint( windowTopLeftPoint: NSPoint(
x: lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, y: lineHeightRect.origin.y - 4.0), x: lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, y: lineHeightRect.origin.y - 4.0
bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0) ),
bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0
)
} else { } else {
ctlCandidateCurrent?.set( ctlCandidateCurrent?.set(
windowTopLeftPoint: NSPoint(x: lineHeightRect.origin.x, y: lineHeightRect.origin.y - 4.0), windowTopLeftPoint: NSPoint(x: lineHeightRect.origin.x, y: lineHeightRect.origin.y - 4.0),
bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0) bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0
)
} }
} }
private func show(tooltip: String, composingBuffer: String, cursorIndex: UInt, client: Any!) { private func show(tooltip: String, composingBuffer: String, cursorIndex: UInt, client: Any!) {
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
var cursor: Int = Int(cursorIndex) var cursor = Int(cursorIndex)
if cursor == composingBuffer.count && cursor != 0 { if cursor == composingBuffer.count, cursor != 0 {
cursor -= 1 cursor -= 1
} }
while lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor >= 0 { while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 {
(client as? IMKTextInput)?.attributes( (client as? IMKTextInput)?.attributes(
forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect) forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect
)
cursor -= 1 cursor -= 1
} }
ctlInputMethod.tooltipController.show(tooltip: tooltip, at: lineHeightRect.origin) ctlInputMethod.tooltipController.show(tooltip: tooltip, at: lineHeightRect.origin)
@ -517,16 +530,16 @@ extension ctlInputMethod {
// MARK: - // MARK: -
extension ctlInputMethod: KeyHandlerDelegate { extension ctlInputMethod: KeyHandlerDelegate {
func ctlCandidate(for keyHandler: KeyHandler) -> Any { func ctlCandidate(for _: KeyHandler) -> Any {
ctlCandidateCurrent ?? .vertical ctlCandidateCurrent ?? .vertical
} }
func keyHandler( func keyHandler(
_ keyHandler: KeyHandler, didSelectCandidateAt index: Int, _: KeyHandler, didSelectCandidateAt index: Int,
ctlCandidate controller: Any ctlCandidate controller: Any
) { ) {
if let controller = controller as? ctlCandidate { if let controller = controller as? ctlCandidate {
self.ctlCandidate(controller, didSelectCandidateAtIndex: UInt(index)) ctlCandidate(controller, didSelectCandidateAtIndex: UInt(index))
} }
} }
@ -545,11 +558,13 @@ extension ctlInputMethod: KeyHandlerDelegate {
if !mgrLangModel.writeUserPhrase( if !mgrLangModel.writeUserPhrase(
state.userPhrase, inputMode: keyHandler.inputMode, state.userPhrase, inputMode: keyHandler.inputMode,
areWeDuplicating: state.chkIfUserPhraseExists, areWeDuplicating: state.chkIfUserPhraseExists,
areWeDeleting: ctlInputMethod.areWeDeleting) areWeDeleting: ctlInputMethod.areWeDeleting
)
|| !mgrLangModel.writeUserPhrase( || !mgrLangModel.writeUserPhrase(
state.userPhraseConverted, inputMode: refInputModeReversed, state.userPhraseConverted, inputMode: refInputModeReversed,
areWeDuplicating: false, areWeDuplicating: false,
areWeDeleting: ctlInputMethod.areWeDeleting) areWeDeleting: ctlInputMethod.areWeDeleting
)
{ {
return false return false
} }
@ -560,7 +575,7 @@ extension ctlInputMethod: KeyHandlerDelegate {
// MARK: - // MARK: -
extension ctlInputMethod: ctlCandidateDelegate { extension ctlInputMethod: ctlCandidateDelegate {
func candidateCountForController(_ controller: ctlCandidate) -> UInt { func candidateCountForController(_: ctlCandidate) -> UInt {
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
return UInt(state.candidates.count) return UInt(state.candidates.count)
} else if let state = state as? InputState.AssociatedPhrases { } else if let state = state as? InputState.AssociatedPhrases {
@ -569,7 +584,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
return 0 return 0
} }
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: UInt) func ctlCandidate(_: ctlCandidate, candidateAtIndex index: UInt)
-> String -> String
{ {
if let state = state as? InputState.ChoosingCandidate { if let state = state as? InputState.ChoosingCandidate {
@ -580,19 +595,20 @@ extension ctlInputMethod: ctlCandidateDelegate {
return "" return ""
} }
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: UInt) { func ctlCandidate(_: ctlCandidate, didSelectCandidateAtIndex index: UInt) {
let client = currentClient let client = currentClient
if let state = state as? InputState.SymbolTable, if let state = state as? InputState.SymbolTable,
let node = state.node.children?[Int(index)] let node = state.node.children?[Int(index)]
{ {
if let children = node.children, !children.isEmpty { if let children = node.children, !children.isEmpty {
self.handle( handle(
state: .SymbolTable(node: node, useVerticalMode: state.useVerticalMode), state: .SymbolTable(node: node, useVerticalMode: state.useVerticalMode),
client: currentClient) client: currentClient
)
} else { } else {
self.handle(state: .Committing(poppedText: node.title), client: client) handle(state: .Committing(poppedText: node.title), client: client)
self.handle(state: .Empty(), client: client) handle(state: .Empty(), client: client)
} }
return return
} }
@ -609,10 +625,11 @@ extension ctlInputMethod: ctlCandidateDelegate {
handle(state: .Committing(poppedText: composingBuffer), client: client) handle(state: .Committing(poppedText: composingBuffer), client: client)
if mgrPrefs.associatedPhrasesEnabled, if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState( let associatePhrases = keyHandler.buildAssociatePhraseState(
withKey: composingBuffer, useVerticalMode: state.useVerticalMode) withKey: composingBuffer, useVerticalMode: state.useVerticalMode
)
as? InputState.AssociatedPhrases as? InputState.AssociatedPhrases
{ {
self.handle(state: associatePhrases, client: client) handle(state: associatePhrases, client: client)
} else { } else {
handle(state: .Empty(), client: client) handle(state: .Empty(), client: client)
} }
@ -627,10 +644,11 @@ extension ctlInputMethod: ctlCandidateDelegate {
handle(state: .Committing(poppedText: selectedValue), client: currentClient) handle(state: .Committing(poppedText: selectedValue), client: currentClient)
if mgrPrefs.associatedPhrasesEnabled, if mgrPrefs.associatedPhrasesEnabled,
let associatePhrases = keyHandler.buildAssociatePhraseState( let associatePhrases = keyHandler.buildAssociatePhraseState(
withKey: selectedValue, useVerticalMode: state.useVerticalMode) withKey: selectedValue, useVerticalMode: state.useVerticalMode
)
as? InputState.AssociatedPhrases as? InputState.AssociatedPhrases
{ {
self.handle(state: associatePhrases, client: client) handle(state: associatePhrases, client: client)
} else { } else {
handle(state: .Empty(), client: client) handle(state: .Empty(), client: client)
} }

View File

@ -33,6 +33,7 @@ extension Bool {
} }
// MARK: - IME Menu Manager // MARK: - IME Menu Manager
// //
extension ctlInputMethod { extension ctlInputMethod {
@ -43,51 +44,59 @@ extension ctlInputMethod {
let useSCPCTypingModeItem = menu.addItem( let useSCPCTypingModeItem = menu.addItem(
withTitle: NSLocalizedString("Per-Char Select Mode", comment: ""), withTitle: NSLocalizedString("Per-Char Select Mode", comment: ""),
action: #selector(toggleSCPCTypingMode(_:)), keyEquivalent: "P") action: #selector(toggleSCPCTypingMode(_:)), keyEquivalent: "P"
)
useSCPCTypingModeItem.keyEquivalentModifierMask = [.command, .control] useSCPCTypingModeItem.keyEquivalentModifierMask = [.command, .control]
useSCPCTypingModeItem.state = mgrPrefs.useSCPCTypingMode.state useSCPCTypingModeItem.state = mgrPrefs.useSCPCTypingMode.state
let userAssociatedPhrasesItem = menu.addItem( let userAssociatedPhrasesItem = menu.addItem(
withTitle: NSLocalizedString("Per-Char Associated Phrases", comment: ""), withTitle: NSLocalizedString("Per-Char Associated Phrases", comment: ""),
action: #selector(toggleAssociatedPhrasesEnabled(_:)), keyEquivalent: "O") action: #selector(toggleAssociatedPhrasesEnabled(_:)), keyEquivalent: "O"
)
userAssociatedPhrasesItem.keyEquivalentModifierMask = [.command, .control] userAssociatedPhrasesItem.keyEquivalentModifierMask = [.command, .control]
userAssociatedPhrasesItem.state = mgrPrefs.associatedPhrasesEnabled.state userAssociatedPhrasesItem.state = mgrPrefs.associatedPhrasesEnabled.state
let useCNS11643SupportItem = menu.addItem( let useCNS11643SupportItem = menu.addItem(
withTitle: NSLocalizedString("CNS11643 Mode", comment: ""), withTitle: NSLocalizedString("CNS11643 Mode", comment: ""),
action: #selector(toggleCNS11643Enabled(_:)), keyEquivalent: "L") action: #selector(toggleCNS11643Enabled(_:)), keyEquivalent: "L"
)
useCNS11643SupportItem.keyEquivalentModifierMask = [.command, .control] useCNS11643SupportItem.keyEquivalentModifierMask = [.command, .control]
useCNS11643SupportItem.state = mgrPrefs.cns11643Enabled.state useCNS11643SupportItem.state = mgrPrefs.cns11643Enabled.state
if IME.getInputMode() == InputMode.imeModeCHT { if IME.getInputMode() == InputMode.imeModeCHT {
let chineseConversionItem = menu.addItem( let chineseConversionItem = menu.addItem(
withTitle: NSLocalizedString("Force KangXi Writing", comment: ""), withTitle: NSLocalizedString("Force KangXi Writing", comment: ""),
action: #selector(toggleChineseConverter(_:)), keyEquivalent: "K") action: #selector(toggleChineseConverter(_:)), keyEquivalent: "K"
)
chineseConversionItem.keyEquivalentModifierMask = [.command, .control] chineseConversionItem.keyEquivalentModifierMask = [.command, .control]
chineseConversionItem.state = mgrPrefs.chineseConversionEnabled.state chineseConversionItem.state = mgrPrefs.chineseConversionEnabled.state
let shiftJISConversionItem = menu.addItem( let shiftJISConversionItem = menu.addItem(
withTitle: NSLocalizedString("JIS Shinjitai Output", comment: ""), withTitle: NSLocalizedString("JIS Shinjitai Output", comment: ""),
action: #selector(toggleShiftJISShinjitaiOutput(_:)), keyEquivalent: "J") action: #selector(toggleShiftJISShinjitaiOutput(_:)), keyEquivalent: "J"
)
shiftJISConversionItem.keyEquivalentModifierMask = [.command, .control] shiftJISConversionItem.keyEquivalentModifierMask = [.command, .control]
shiftJISConversionItem.state = mgrPrefs.shiftJISShinjitaiOutputEnabled.state shiftJISConversionItem.state = mgrPrefs.shiftJISShinjitaiOutputEnabled.state
} }
let halfWidthPunctuationItem = menu.addItem( let halfWidthPunctuationItem = menu.addItem(
withTitle: NSLocalizedString("Half-Width Punctuation Mode", comment: ""), withTitle: NSLocalizedString("Half-Width Punctuation Mode", comment: ""),
action: #selector(toggleHalfWidthPunctuation(_:)), keyEquivalent: "H") action: #selector(toggleHalfWidthPunctuation(_:)), keyEquivalent: "H"
)
halfWidthPunctuationItem.keyEquivalentModifierMask = [.command, .control] halfWidthPunctuationItem.keyEquivalentModifierMask = [.command, .control]
halfWidthPunctuationItem.state = mgrPrefs.halfWidthPunctuationEnabled.state halfWidthPunctuationItem.state = mgrPrefs.halfWidthPunctuationEnabled.state
if optionKeyPressed { if optionKeyPressed {
let phaseReplacementItem = menu.addItem( let phaseReplacementItem = menu.addItem(
withTitle: NSLocalizedString("Use Phrase Replacement", comment: ""), withTitle: NSLocalizedString("Use Phrase Replacement", comment: ""),
action: #selector(togglePhraseReplacement(_:)), keyEquivalent: "") action: #selector(togglePhraseReplacement(_:)), keyEquivalent: ""
)
phaseReplacementItem.state = mgrPrefs.phraseReplacementEnabled.state phaseReplacementItem.state = mgrPrefs.phraseReplacementEnabled.state
let toggleSymbolInputItem = menu.addItem( let toggleSymbolInputItem = menu.addItem(
withTitle: NSLocalizedString("Symbol & Emoji Input", comment: ""), withTitle: NSLocalizedString("Symbol & Emoji Input", comment: ""),
action: #selector(toggleSymbolEnabled(_:)), keyEquivalent: "") action: #selector(toggleSymbolEnabled(_:)), keyEquivalent: ""
)
toggleSymbolInputItem.state = mgrPrefs.symbolInputEnabled.state toggleSymbolInputItem.state = mgrPrefs.symbolInputEnabled.state
} }
@ -95,30 +104,37 @@ extension ctlInputMethod {
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Open User Data Folder", comment: ""), withTitle: NSLocalizedString("Open User Data Folder", comment: ""),
action: #selector(openUserDataFolder(_:)), keyEquivalent: "") action: #selector(openUserDataFolder(_:)), keyEquivalent: ""
)
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Edit User Phrases…", comment: ""), withTitle: NSLocalizedString("Edit User Phrases…", comment: ""),
action: #selector(openUserPhrases(_:)), keyEquivalent: "") action: #selector(openUserPhrases(_:)), keyEquivalent: ""
)
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Edit Excluded Phrases…", comment: ""), withTitle: NSLocalizedString("Edit Excluded Phrases…", comment: ""),
action: #selector(openExcludedPhrases(_:)), keyEquivalent: "") action: #selector(openExcludedPhrases(_:)), keyEquivalent: ""
)
if optionKeyPressed { if optionKeyPressed {
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Edit Phrase Replacement Table…", comment: ""), withTitle: NSLocalizedString("Edit Phrase Replacement Table…", comment: ""),
action: #selector(openPhraseReplacement(_:)), keyEquivalent: "") action: #selector(openPhraseReplacement(_:)), keyEquivalent: ""
)
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Edit Associated Phrases…", comment: ""), withTitle: NSLocalizedString("Edit Associated Phrases…", comment: ""),
action: #selector(openAssociatedPhrases(_:)), keyEquivalent: "") action: #selector(openAssociatedPhrases(_:)), keyEquivalent: ""
)
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Edit User Symbol & Emoji Data…", comment: ""), withTitle: NSLocalizedString("Edit User Symbol & Emoji Data…", comment: ""),
action: #selector(openUserSymbols(_:)), keyEquivalent: "") action: #selector(openUserSymbols(_:)), keyEquivalent: ""
)
} }
if optionKeyPressed || !mgrPrefs.shouldAutoReloadUserDataFiles { if optionKeyPressed || !mgrPrefs.shouldAutoReloadUserDataFiles {
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Reload User Phrases", comment: ""), withTitle: NSLocalizedString("Reload User Phrases", comment: ""),
action: #selector(reloadUserPhrases(_:)), keyEquivalent: "") action: #selector(reloadUserPhrases(_:)), keyEquivalent: ""
)
} }
menu.addItem(NSMenuItem.separator()) // --------------------- menu.addItem(NSMenuItem.separator()) // ---------------------
@ -126,25 +142,31 @@ extension ctlInputMethod {
if optionKeyPressed { if optionKeyPressed {
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("vChewing Preferences…", comment: ""), withTitle: NSLocalizedString("vChewing Preferences…", comment: ""),
action: #selector(showLegacyPreferences(_:)), keyEquivalent: "") action: #selector(showLegacyPreferences(_:)), keyEquivalent: ""
)
} else { } else {
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("vChewing Preferences…", comment: ""), withTitle: NSLocalizedString("vChewing Preferences…", comment: ""),
action: #selector(showPreferences(_:)), keyEquivalent: "") action: #selector(showPreferences(_:)), keyEquivalent: ""
)
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Check for Updates…", comment: ""), withTitle: NSLocalizedString("Check for Updates…", comment: ""),
action: #selector(checkForUpdate(_:)), keyEquivalent: "") action: #selector(checkForUpdate(_:)), keyEquivalent: ""
)
} }
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Reboot vChewing…", comment: ""), withTitle: NSLocalizedString("Reboot vChewing…", comment: ""),
action: #selector(selfTerminate(_:)), keyEquivalent: "") action: #selector(selfTerminate(_:)), keyEquivalent: ""
)
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("About vChewing…", comment: ""), withTitle: NSLocalizedString("About vChewing…", comment: ""),
action: #selector(showAbout(_:)), keyEquivalent: "") action: #selector(showAbout(_:)), keyEquivalent: ""
)
if optionKeyPressed { if optionKeyPressed {
menu.addItem( menu.addItem(
withTitle: NSLocalizedString("Uninstall vChewing…", comment: ""), withTitle: NSLocalizedString("Uninstall vChewing…", comment: ""),
action: #selector(selfUninstall(_:)), keyEquivalent: "") action: #selector(selfUninstall(_:)), keyEquivalent: ""
)
} }
// NSMenu modified key // NSMenu modified key
@ -155,7 +177,7 @@ extension ctlInputMethod {
// MARK: - IME Menu Items // MARK: - IME Menu Items
@objc override func showPreferences(_ sender: Any?) { @objc override func showPreferences(_: Any?) {
if #available(macOS 11.0, *) { if #available(macOS 11.0, *) {
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
ctlPrefUI.shared.controller.show(preferencePane: Preferences.PaneIdentifier(rawValue: "General")) ctlPrefUI.shared.controller.show(preferencePane: Preferences.PaneIdentifier(rawValue: "General"))
@ -165,7 +187,7 @@ extension ctlInputMethod {
} }
} }
@objc func showLegacyPreferences(_ sender: Any?) { @objc func showLegacyPreferences(_: Any?) {
showPrefWindowTraditional() showPrefWindowTraditional()
} }
@ -174,135 +196,144 @@ extension ctlInputMethod {
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
@objc func toggleSCPCTypingMode(_ sender: Any?) { @objc func toggleSCPCTypingMode(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Per-Char Select Mode", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Per-Char Select Mode", comment: ""), "\n",
mgrPrefs.toggleSCPCTypingModeEnabled() mgrPrefs.toggleSCPCTypingModeEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func toggleChineseConverter(_ sender: Any?) { @objc func toggleChineseConverter(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Force KangXi Writing", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Force KangXi Writing", comment: ""), "\n",
mgrPrefs.toggleChineseConversionEnabled() mgrPrefs.toggleChineseConversionEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func toggleShiftJISShinjitaiOutput(_ sender: Any?) { @objc func toggleShiftJISShinjitaiOutput(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("JIS Shinjitai Output", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("JIS Shinjitai Output", comment: ""), "\n",
mgrPrefs.toggleShiftJISShinjitaiOutputEnabled() mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func toggleHalfWidthPunctuation(_ sender: Any?) { @objc func toggleHalfWidthPunctuation(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Half-Width Punctuation Mode", comment: ""), format: "%@%@%@", NSLocalizedString("Half-Width Punctuation Mode", comment: ""),
"\n", "\n",
mgrPrefs.toggleHalfWidthPunctuationEnabled() mgrPrefs.toggleHalfWidthPunctuationEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func toggleCNS11643Enabled(_ sender: Any?) { @objc func toggleCNS11643Enabled(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("CNS11643 Mode", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("CNS11643 Mode", comment: ""), "\n",
mgrPrefs.toggleCNS11643Enabled() mgrPrefs.toggleCNS11643Enabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func toggleSymbolEnabled(_ sender: Any?) { @objc func toggleSymbolEnabled(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Symbol & Emoji Input", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Symbol & Emoji Input", comment: ""), "\n",
mgrPrefs.toggleSymbolInputEnabled() mgrPrefs.toggleSymbolInputEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func toggleAssociatedPhrasesEnabled(_ sender: Any?) { @objc func toggleAssociatedPhrasesEnabled(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Per-Char Associated Phrases", comment: ""), format: "%@%@%@", NSLocalizedString("Per-Char Associated Phrases", comment: ""),
"\n", "\n",
mgrPrefs.toggleAssociatedPhrasesEnabled() mgrPrefs.toggleAssociatedPhrasesEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func togglePhraseReplacement(_ sender: Any?) { @objc func togglePhraseReplacement(_: Any?) {
NotifierController.notify( NotifierController.notify(
message: String( message: String(
format: "%@%@%@", NSLocalizedString("Use Phrase Replacement", comment: ""), "\n", format: "%@%@%@", NSLocalizedString("Use Phrase Replacement", comment: ""), "\n",
mgrPrefs.togglePhraseReplacementEnabled() mgrPrefs.togglePhraseReplacementEnabled()
? NSLocalizedString("NotificationSwitchON", comment: "") ? NSLocalizedString("NotificationSwitchON", comment: "")
: NSLocalizedString("NotificationSwitchOFF", comment: ""))) : NSLocalizedString("NotificationSwitchOFF", comment: "")
))
resetKeyHandler() resetKeyHandler()
} }
@objc func selfUninstall(_ sender: Any?) { @objc func selfUninstall(_: Any?) {
(NSApp.delegate as? AppDelegate)?.selfUninstall() (NSApp.delegate as? AppDelegate)?.selfUninstall()
} }
@objc func selfTerminate(_ sender: Any?) { @objc func selfTerminate(_: Any?) {
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
NSApp.terminate(nil) NSApp.terminate(nil)
} }
@objc func checkForUpdate(_ sender: Any?) { @objc func checkForUpdate(_: Any?) {
(NSApp.delegate as? AppDelegate)?.checkForUpdate(forced: true) (NSApp.delegate as? AppDelegate)?.checkForUpdate(forced: true)
} }
@objc func openUserPhrases(_ sender: Any?) { @objc func openUserPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userPhrasesDataPath(IME.getInputMode())) IME.openPhraseFile(userFileAt: mgrLangModel.userPhrasesDataPath(IME.getInputMode()))
} }
@objc func openUserDataFolder(_ sender: Any?) { @objc func openUserDataFolder(_: Any?) {
if !mgrLangModel.checkIfUserDataFolderExists() { if !mgrLangModel.checkIfUserDataFolderExists() {
return return
} }
NSWorkspace.shared.openFile( NSWorkspace.shared.openFile(
mgrLangModel.dataFolderPath(isDefaultFolder: false), withApplication: "Finder") mgrLangModel.dataFolderPath(isDefaultFolder: false), withApplication: "Finder"
)
} }
@objc func openExcludedPhrases(_ sender: Any?) { @objc func openExcludedPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.excludedPhrasesDataPath(IME.getInputMode())) IME.openPhraseFile(userFileAt: mgrLangModel.excludedPhrasesDataPath(IME.getInputMode()))
} }
@objc func openUserSymbols(_ sender: Any?) { @objc func openUserSymbols(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userSymbolDataPath(IME.getInputMode())) IME.openPhraseFile(userFileAt: mgrLangModel.userSymbolDataPath(IME.getInputMode()))
} }
@objc func openPhraseReplacement(_ sender: Any?) { @objc func openPhraseReplacement(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.phraseReplacementDataPath(IME.getInputMode())) IME.openPhraseFile(userFileAt: mgrLangModel.phraseReplacementDataPath(IME.getInputMode()))
} }
@objc func openAssociatedPhrases(_ sender: Any?) { @objc func openAssociatedPhrases(_: Any?) {
IME.openPhraseFile(userFileAt: mgrLangModel.userAssociatedPhrasesDataPath(IME.getInputMode())) IME.openPhraseFile(userFileAt: mgrLangModel.userAssociatedPhrasesDataPath(IME.getInputMode()))
} }
@objc func reloadUserPhrases(_ sender: Any?) { @objc func reloadUserPhrases(_: Any?) {
mgrLangModel.loadUserPhrases() mgrLangModel.loadUserPhrases()
mgrLangModel.loadUserPhraseReplacement() mgrLangModel.loadUserPhraseReplacement()
} }
@objc func showAbout(_ sender: Any?) { @objc func showAbout(_: Any?) {
(NSApp.delegate as? AppDelegate)?.showAbout() (NSApp.delegate as? AppDelegate)?.showAbout()
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }

View File

@ -83,7 +83,7 @@ private let kDefaultKeys = "123456789"
@objc extension UserDefaults { @objc extension UserDefaults {
func setDefault(_ value: Any?, forKey defaultName: String) { func setDefault(_ value: Any?, forKey defaultName: String) {
if object(forKey: defaultName) == nil { if object(forKey: defaultName) == nil {
self.set(value, forKey: defaultName) set(value, forKey: defaultName)
} }
} }
} }
@ -110,9 +110,7 @@ struct UserDefault<Value> {
struct CandidateListTextSize { struct CandidateListTextSize {
let key: String let key: String
let defaultValue: CGFloat = kDefaultCandidateListTextSize let defaultValue: CGFloat = kDefaultCandidateListTextSize
lazy var container: UserDefault = { lazy var container: UserDefault = .init(key: key, defaultValue: defaultValue)
UserDefault(key: key, defaultValue: defaultValue)
}()
var wrappedValue: CGFloat { var wrappedValue: CGFloat {
mutating get { mutating get {
@ -140,9 +138,7 @@ struct CandidateListTextSize {
struct ComposingBufferSize { struct ComposingBufferSize {
let key: String let key: String
let defaultValue: Int = kDefaultComposingBufferSize let defaultValue: Int = kDefaultComposingBufferSize
lazy var container: UserDefault = { lazy var container: UserDefault = .init(key: key, defaultValue: defaultValue)
UserDefault(key: key, defaultValue: defaultValue)
}()
var wrappedValue: Int { var wrappedValue: Int {
mutating get { mutating get {
@ -201,6 +197,7 @@ struct ComposingBufferSize {
} }
// MARK: - // MARK: -
public class mgrPrefs: NSObject { public class mgrPrefs: NSObject {
static var allKeys: [String] { static var allKeys: [String] {
[ [
@ -238,33 +235,42 @@ public class mgrPrefs: NSObject {
} }
// MARK: - Preferences Module plist // MARK: - Preferences Module plist
@objc public static func setMissingDefaults() { @objc public static func setMissingDefaults() {
UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled) UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled)
UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode) UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode)
UserDefaults.standard.setDefault(mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically) UserDefaults.standard.setDefault(mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.showPageButtonsInCandidateWindow, forKey: UserDef.kShowPageButtonsInCandidateWindow) mgrPrefs.showPageButtonsInCandidateWindow, forKey: UserDef.kShowPageButtonsInCandidateWindow
)
UserDefaults.standard.setDefault(mgrPrefs.symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled) UserDefaults.standard.setDefault(mgrPrefs.symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled)
UserDefaults.standard.setDefault(mgrPrefs.candidateListTextSize, forKey: UserDef.kCandidateListTextSize) UserDefaults.standard.setDefault(mgrPrefs.candidateListTextSize, forKey: UserDef.kCandidateListTextSize)
UserDefaults.standard.setDefault(mgrPrefs.chooseCandidateUsingSpace, forKey: UserDef.kChooseCandidateUsingSpace) UserDefaults.standard.setDefault(mgrPrefs.chooseCandidateUsingSpace, forKey: UserDef.kChooseCandidateUsingSpace)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.shouldAutoReloadUserDataFiles, forKey: UserDef.kShouldAutoReloadUserDataFiles) mgrPrefs.shouldAutoReloadUserDataFiles, forKey: UserDef.kShouldAutoReloadUserDataFiles
)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.specifyShiftTabKeyBehavior, forKey: UserDef.kSpecifyShiftTabKeyBehavior) mgrPrefs.specifyShiftTabKeyBehavior, forKey: UserDef.kSpecifyShiftTabKeyBehavior
)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.specifyShiftSpaceKeyBehavior, forKey: UserDef.kSpecifyShiftSpaceKeyBehavior) mgrPrefs.specifyShiftSpaceKeyBehavior, forKey: UserDef.kSpecifyShiftSpaceKeyBehavior
)
UserDefaults.standard.setDefault(mgrPrefs.useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode) UserDefaults.standard.setDefault(mgrPrefs.useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode)
UserDefaults.standard.setDefault(mgrPrefs.associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled) UserDefaults.standard.setDefault(mgrPrefs.associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.selectPhraseAfterCursorAsCandidate, forKey: UserDef.kSelectPhraseAfterCursorAsCandidate) mgrPrefs.selectPhraseAfterCursorAsCandidate, forKey: UserDef.kSelectPhraseAfterCursorAsCandidate
)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.moveCursorAfterSelectingCandidate, forKey: UserDef.kMoveCursorAfterSelectingCandidate) mgrPrefs.moveCursorAfterSelectingCandidate, forKey: UserDef.kMoveCursorAfterSelectingCandidate
)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.useHorizontalCandidateList, forKey: UserDef.kUseHorizontalCandidateList) mgrPrefs.useHorizontalCandidateList, forKey: UserDef.kUseHorizontalCandidateList
)
UserDefaults.standard.setDefault(mgrPrefs.cns11643Enabled, forKey: UserDef.kCNS11643Enabled) UserDefaults.standard.setDefault(mgrPrefs.cns11643Enabled, forKey: UserDef.kCNS11643Enabled)
UserDefaults.standard.setDefault(mgrPrefs.chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled) UserDefaults.standard.setDefault(mgrPrefs.chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled)
UserDefaults.standard.setDefault( UserDefaults.standard.setDefault(
mgrPrefs.shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled) mgrPrefs.shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled
)
UserDefaults.standard.setDefault(mgrPrefs.phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled) UserDefaults.standard.setDefault(mgrPrefs.phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled)
UserDefaults.standard.setDefault(mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) UserDefaults.standard.setDefault(mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep)
@ -303,7 +309,8 @@ public class mgrPrefs: NSObject {
} }
@UserDefault( @UserDefault(
key: UserDef.kBasicKeyboardLayout, defaultValue: "com.apple.keylayout.ZhuyinBopomofo") key: UserDef.kBasicKeyboardLayout, defaultValue: "com.apple.keylayout.ZhuyinBopomofo"
)
@objc static var basicKeyboardLayout: String @objc static var basicKeyboardLayout: String
@UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow, defaultValue: true) @UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow, defaultValue: true)
@ -377,10 +384,11 @@ public class mgrPrefs: NSObject {
@objc @discardableResult static func toggleChineseConversionEnabled() -> Bool { @objc @discardableResult static func toggleChineseConversionEnabled() -> Bool {
chineseConversionEnabled = !chineseConversionEnabled chineseConversionEnabled = !chineseConversionEnabled
// JIS // JIS
if chineseConversionEnabled && shiftJISShinjitaiOutputEnabled { if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled {
toggleShiftJISShinjitaiOutputEnabled() toggleShiftJISShinjitaiOutputEnabled()
UserDefaults.standard.set( UserDefaults.standard.set(
shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled) shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled
)
} }
UserDefaults.standard.set(chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled) UserDefaults.standard.set(chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled)
return chineseConversionEnabled return chineseConversionEnabled
@ -392,11 +400,12 @@ public class mgrPrefs: NSObject {
@objc @discardableResult static func toggleShiftJISShinjitaiOutputEnabled() -> Bool { @objc @discardableResult static func toggleShiftJISShinjitaiOutputEnabled() -> Bool {
shiftJISShinjitaiOutputEnabled = !shiftJISShinjitaiOutputEnabled shiftJISShinjitaiOutputEnabled = !shiftJISShinjitaiOutputEnabled
// JIS // JIS
if shiftJISShinjitaiOutputEnabled && chineseConversionEnabled { if shiftJISShinjitaiOutputEnabled, chineseConversionEnabled {
toggleChineseConversionEnabled() toggleChineseConversionEnabled()
} }
UserDefaults.standard.set( UserDefaults.standard.set(
shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled) shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled
)
return shiftJISShinjitaiOutputEnabled return shiftJISShinjitaiOutputEnabled
} }
@ -418,6 +427,7 @@ public class mgrPrefs: NSObject {
@objc static var specifyShiftSpaceKeyBehavior: Bool @objc static var specifyShiftSpaceKeyBehavior: Bool
// MARK: - Optional settings // MARK: - Optional settings
@UserDefault(key: UserDef.kCandidateTextFontName, defaultValue: nil) @UserDefault(key: UserDef.kCandidateTextFontName, defaultValue: nil)
@objc static var candidateTextFontName: String? @objc static var candidateTextFontName: String?
@ -430,6 +440,7 @@ public class mgrPrefs: NSObject {
@objc static var defaultCandidateKeys: String { @objc static var defaultCandidateKeys: String {
kDefaultKeys kDefaultKeys
} }
@objc static var suggestedCandidateKeys: [String] { @objc static var suggestedCandidateKeys: [String] {
[kDefaultKeys, "234567890", "QWERTYUIO", "QWERTASDF", "ASDFGHJKL", "ASDFZXCVB"] [kDefaultKeys, "234567890", "QWERTYUIO", "QWERTASDF", "ASDFGHJKL", "ASDFZXCVB"]
} }
@ -472,19 +483,20 @@ public class mgrPrefs: NSObject {
case .invalidCharacters: case .invalidCharacters:
return NSLocalizedString( return NSLocalizedString(
"Candidate keys can only contain ASCII characters like alphanumericals.", "Candidate keys can only contain ASCII characters like alphanumericals.",
comment: "") comment: ""
)
case .containSpace: case .containSpace:
return NSLocalizedString("Candidate keys cannot contain space.", comment: "") return NSLocalizedString("Candidate keys cannot contain space.", comment: "")
case .duplicatedCharacters: case .duplicatedCharacters:
return NSLocalizedString("There should not be duplicated keys.", comment: "") return NSLocalizedString("There should not be duplicated keys.", comment: "")
case .tooShort: case .tooShort:
return NSLocalizedString( return NSLocalizedString(
"Please specify at least 4 candidate keys.", comment: "") "Please specify at least 4 candidate keys.", comment: ""
)
case .tooLong: case .tooLong:
return NSLocalizedString("Maximum 15 candidate keys allowed.", comment: "") return NSLocalizedString("Maximum 15 candidate keys allowed.", comment: "")
} }
} }
} }
@UserDefault(key: UserDef.kPhraseReplacementEnabled, defaultValue: false) @UserDefault(key: UserDef.kPhraseReplacementEnabled, defaultValue: false)
@ -505,5 +517,4 @@ public class mgrPrefs: NSObject {
UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled) UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled)
return associatedPhrasesEnabled return associatedPhrasesEnabled
} }
} }

View File

@ -25,13 +25,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
@objc extension mgrLangModel { @objc extension mgrLangModel {
// MARK: - // MARK: -
static func getBundleDataPath(_ filenameSansExt: String) -> String { static func getBundleDataPath(_ filenameSansExt: String) -> String {
Bundle.main.path(forResource: filenameSansExt, ofType: "txt")! Bundle.main.path(forResource: filenameSansExt, ofType: "txt")!
} }
// MARK: - 使 // MARK: - 使
// Swift appendingPathComponent URL .path // Swift appendingPathComponent URL .path
static func userPhrasesDataPath(_ mode: InputMode) -> String { static func userPhrasesDataPath(_ mode: InputMode) -> String {
@ -134,12 +135,12 @@ import Cocoa
// //
// //
// //
if folderExist && !isFolder.boolValue { if folderExist, !isFolder.boolValue {
do { do {
if dataFolderPath(isDefaultFolder: false) if dataFolderPath(isDefaultFolder: false)
== dataFolderPath(isDefaultFolder: true) == dataFolderPath(isDefaultFolder: true)
{ {
let formatter = DateFormatter.init() let formatter = DateFormatter()
formatter.dateFormat = "YYYYMMDD-HHMM'Hrs'-ss's'" formatter.dateFormat = "YYYYMMDD-HHMM'Hrs'-ss's'"
let dirAlternative = folderPath + formatter.string(from: Date()) let dirAlternative = folderPath + formatter.string(from: Date())
try FileManager.default.moveItem(atPath: folderPath, toPath: dirAlternative) try FileManager.default.moveItem(atPath: folderPath, toPath: dirAlternative)
@ -157,7 +158,8 @@ import Cocoa
try FileManager.default.createDirectory( try FileManager.default.createDirectory(
atPath: folderPath, atPath: folderPath,
withIntermediateDirectories: true, withIntermediateDirectories: true,
attributes: nil) attributes: nil
)
} catch { } catch {
print("Failed to create folder: \(error)") print("Failed to create folder: \(error)")
return false return false
@ -167,6 +169,7 @@ import Cocoa
} }
// MARK: - 使 mgrPrefs // MARK: - 使 mgrPrefs
// mgrPrefs // mgrPrefs
static func dataFolderPath(isDefaultFolder: Bool) -> String { static func dataFolderPath(isDefaultFolder: Bool) -> String {
@ -195,6 +198,7 @@ import Cocoa
} }
// MARK: - 使 // MARK: - 使
static func writeUserPhrase( static func writeUserPhrase(
_ userPhrase: String?, inputMode mode: InputMode, areWeDuplicating: Bool, areWeDeleting: Bool _ userPhrase: String?, inputMode mode: InputMode, areWeDuplicating: Bool, areWeDeleting: Bool
) -> Bool { ) -> Bool {
@ -207,7 +211,7 @@ import Cocoa
let path = areWeDeleting ? excludedPhrasesDataPath(mode) : userPhrasesDataPath(mode) let path = areWeDeleting ? excludedPhrasesDataPath(mode) : userPhrasesDataPath(mode)
if areWeDuplicating && !areWeDeleting { if areWeDuplicating, !areWeDeleting {
// Do not use ASCII characters to comment here. // Do not use ASCII characters to comment here.
// Otherwise, it will be scrambled by cnvHYPYtoBPMF // Otherwise, it will be scrambled by cnvHYPYtoBPMF
// module shipped in the vChewing Phrase Editor. // module shipped in the vChewing Phrase Editor.
@ -238,5 +242,4 @@ import Cocoa
} }
return false return false
} }
} }

View File

@ -28,9 +28,10 @@ import Cocoa
public class clsSFX: NSObject, NSSoundDelegate { public class clsSFX: NSObject, NSSoundDelegate {
private static let shared = clsSFX() private static let shared = clsSFX()
private override init() { override private init() {
super.init() super.init()
} }
private var currentBeep: NSSound? private var currentBeep: NSSound?
private func beep() { private func beep() {
// Stop existing beep // Stop existing beep
@ -57,9 +58,11 @@ public class clsSFX: NSObject, NSSoundDelegate {
beep.play() beep.play()
currentBeep = beep currentBeep = beep
} }
@objc public func sound(_ sound: NSSound, didFinishPlaying flag: Bool) {
@objc public func sound(_: NSSound, didFinishPlaying _: Bool) {
currentBeep = nil currentBeep = nil
} }
@objc static func beep() { @objc static func beep() {
shared.beep() shared.beep()
} }

View File

@ -44,7 +44,8 @@ public protocol ctlCandidateDelegate: AnyObject {
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: UInt) func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: UInt)
-> String -> String
func ctlCandidate( func ctlCandidate(
_ controller: ctlCandidate, didSelectCandidateAtIndex index: UInt) _ controller: ctlCandidate, didSelectCandidateAtIndex index: UInt
)
} }
@objc(ctlCandidate) @objc(ctlCandidate)
@ -54,7 +55,8 @@ public class ctlCandidate: NSWindowController {
reloadData() reloadData()
} }
} }
@objc public var selectedCandidateIndex: UInt = UInt.max
@objc public var selectedCandidateIndex: UInt = .max
@objc public var visible: Bool = false { @objc public var visible: Bool = false {
didSet { didSet {
NSObject.cancelPreviousPerformRequests(withTarget: self) NSObject.cancelPreviousPerformRequests(withTarget: self)
@ -65,6 +67,7 @@ public class ctlCandidate: NSWindowController {
} }
} }
} }
@objc public var windowTopLeftPoint: NSPoint { @objc public var windowTopLeftPoint: NSPoint {
get { get {
guard let frameRect = window?.frame else { guard let frameRect = window?.frame else {
@ -83,13 +86,14 @@ public class ctlCandidate: NSWindowController {
.map { .map {
CandidateKeyLabel(key: $0, displayedText: $0) CandidateKeyLabel(key: $0, displayedText: $0)
} }
@objc public var keyLabelFont: NSFont = NSFont.monospacedDigitSystemFont( @objc public var keyLabelFont: NSFont = NSFont.monospacedDigitSystemFont(
ofSize: 14, weight: .medium) ofSize: 14, weight: .medium
)
@objc public var candidateFont: NSFont = NSFont.systemFont(ofSize: 18) @objc public var candidateFont: NSFont = NSFont.systemFont(ofSize: 18)
@objc public var tooltip: String = "" @objc public var tooltip: String = ""
@objc public func reloadData() { @objc public func reloadData() {}
}
@objc public func showNextPage() -> Bool { @objc public func showNextPage() -> Bool {
false false
@ -107,7 +111,7 @@ public class ctlCandidate: NSWindowController {
false false
} }
@objc public func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt { @objc public func candidateIndexAtKeyLabelIndex(_: UInt) -> UInt {
UInt.max UInt.max
} }
@ -125,7 +129,8 @@ public class ctlCandidate: NSWindowController {
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) { public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
self.doSet( self.doSet(
windowTopLeftPoint: windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: height) windowTopLeftPoint: windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: height
)
} }
} }
@ -136,8 +141,8 @@ public class ctlCandidate: NSWindowController {
var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero
for screen in NSScreen.screens { for screen in NSScreen.screens {
let frame = screen.visibleFrame let frame = screen.visibleFrame
if windowTopLeftPoint.x >= frame.minX && windowTopLeftPoint.x <= frame.maxX if windowTopLeftPoint.x >= frame.minX, windowTopLeftPoint.x <= frame.maxX,
&& windowTopLeftPoint.y >= frame.minY && windowTopLeftPoint.y <= frame.maxY windowTopLeftPoint.y >= frame.minY, windowTopLeftPoint.y <= frame.maxY
{ {
screenFrame = frame screenFrame = frame
break break
@ -172,5 +177,4 @@ public class ctlCandidate: NSWindowController {
window?.setFrameTopLeftPoint(adjustedPoint) window?.setFrameTopLeftPoint(adjustedPoint)
} }
} }

View File

@ -42,7 +42,7 @@ private class HorizontalCandidateView: NSView {
private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:] private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:]
private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:] private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
private var elementWidths: [CGFloat] = [] private var elementWidths: [CGFloat] = []
private var trackingHighlightedIndex: UInt = UInt.max private var trackingHighlightedIndex: UInt = .max
override var isFlipped: Bool { override var isFlipped: Bool {
true true
@ -71,7 +71,8 @@ private class HorizontalCandidateView: NSView {
for index in 0..<count { for index in 0..<count {
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect( let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
with: baseSize, options: .usesLineFragmentOrigin, with: baseSize, options: .usesLineFragmentOrigin,
attributes: candidateWithLabelAttrDict) attributes: candidateWithLabelAttrDict
)
var cellWidth = rctCandidate.size.width + cellPadding var cellWidth = rctCandidate.size.width + cellPadding
let cellHeight = rctCandidate.size.height + cellPadding let cellHeight = rctCandidate.size.height + cellPadding
if cellWidth < cellHeight * 1.35 { if cellWidth < cellHeight * 1.35 {
@ -115,7 +116,7 @@ private class HorizontalCandidateView: NSView {
cellPadding = ceil(biggestSize / 2.0) cellPadding = ceil(biggestSize / 2.0)
} }
override func draw(_ dirtyRect: NSRect) { override func draw(_: NSRect) {
let bounds = bounds let bounds = bounds
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
NSBezierPath.fill(bounds) NSBezierPath.fill(bounds)
@ -124,21 +125,25 @@ private class HorizontalCandidateView: NSView {
NSBezierPath.strokeLine( NSBezierPath.strokeLine(
from: NSPoint(x: bounds.size.width, y: 0.0), from: NSPoint(x: bounds.size.width, y: 0.0),
to: NSPoint(x: bounds.size.width, y: bounds.size.height)) to: NSPoint(x: bounds.size.width, y: bounds.size.height)
)
var accuWidth: CGFloat = 0 var accuWidth: CGFloat = 0
for index in 0..<elementWidths.count { for index in 0..<elementWidths.count {
let currentWidth = elementWidths[index] let currentWidth = elementWidths[index]
let rctCandidateArea = NSRect( let rctCandidateArea = NSRect(
x: accuWidth, y: 0.0, width: currentWidth + 1.0, x: accuWidth, y: 0.0, width: currentWidth + 1.0,
height: candidateTextHeight + cellPadding) height: candidateTextHeight + cellPadding
)
let rctLabel = NSRect( let rctLabel = NSRect(
x: accuWidth + cellPadding / 2 - 1, y: cellPadding / 2, width: keyLabelWidth, x: accuWidth + cellPadding / 2 - 1, y: cellPadding / 2, width: keyLabelWidth,
height: keyLabelHeight * 2.0) height: keyLabelHeight * 2.0
)
let rctCandidatePhrase = NSRect( let rctCandidatePhrase = NSRect(
x: accuWidth + keyLabelWidth - 1, y: cellPadding / 2, x: accuWidth + keyLabelWidth - 1, y: cellPadding / 2,
width: currentWidth - keyLabelWidth, width: currentWidth - keyLabelWidth,
height: candidateTextHeight) height: candidateTextHeight
)
var activeCandidateIndexAttr = keyLabelAttrDict var activeCandidateIndexAttr = keyLabelAttrDict
var activeCandidateAttr = candidateAttrDict var activeCandidateAttr = candidateAttrDict
@ -149,12 +154,14 @@ private class HorizontalCandidateView: NSView {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
NSColor.systemRed.blended( NSColor.systemRed.blended(
withFraction: colorBlendAmount, withFraction: colorBlendAmount,
of: NSColor.controlBackgroundColor)! of: NSColor.controlBackgroundColor
)!
.setFill() .setFill()
case InputMode.imeModeCHT: case InputMode.imeModeCHT:
NSColor.systemBlue.blended( NSColor.systemBlue.blended(
withFraction: colorBlendAmount, withFraction: colorBlendAmount,
of: NSColor.controlBackgroundColor)! of: NSColor.controlBackgroundColor
)!
.setFill() .setFill()
default: default:
NSColor.alternateSelectedControlColor.setFill() NSColor.alternateSelectedControlColor.setFill()
@ -181,9 +188,11 @@ private class HorizontalCandidateView: NSView {
} }
NSBezierPath.fill(rctCandidateArea) NSBezierPath.fill(rctCandidateArea)
(keyLabels[index] as NSString).draw( (keyLabels[index] as NSString).draw(
in: rctLabel, withAttributes: activeCandidateIndexAttr) in: rctLabel, withAttributes: activeCandidateIndexAttr
)
(displayedCandidates[index] as NSString).draw( (displayedCandidates[index] as NSString).draw(
in: rctCandidatePhrase, withAttributes: activeCandidateAttr) in: rctCandidatePhrase, withAttributes: activeCandidateAttr
)
accuWidth += currentWidth + 1.0 accuWidth += currentWidth + 1.0
} }
} }
@ -197,13 +206,12 @@ private class HorizontalCandidateView: NSView {
for index in 0..<elementWidths.count { for index in 0..<elementWidths.count {
let currentWidth = elementWidths[index] let currentWidth = elementWidths[index]
if location.x >= accuWidth && location.x <= accuWidth + currentWidth { if location.x >= accuWidth, location.x <= accuWidth + currentWidth {
return UInt(index) return UInt(index)
} }
accuWidth += currentWidth + 1.0 accuWidth += currentWidth + 1.0
} }
return nil return nil
} }
override func mouseUp(with event: NSEvent) { override func mouseUp(with event: NSEvent) {
@ -246,7 +254,8 @@ public class ctlCandidateHorizontal: ctlCandidate {
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel] let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
let panel = NSPanel( let panel = NSPanel(
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
)
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
panel.hasShadow = true panel.hasShadow = true
panel.isOpaque = false panel.isOpaque = false
@ -279,7 +288,8 @@ public class ctlCandidateHorizontal: ctlCandidate {
nextPageButton.bezelStyle = .disclosure nextPageButton.bezelStyle = .disclosure
nextPageButton.userInterfaceLayoutDirection = .leftToRight nextPageButton.userInterfaceLayoutDirection = .leftToRight
nextPageButton.attributedTitle = NSMutableAttributedString( nextPageButton.attributedTitle = NSMutableAttributedString(
string: " ", attributes: buttonAttribute) // Next Page Arrow string: " ", attributes: buttonAttribute
) // Next Page Arrow
prevPageButton = NSButton(frame: contentRect) prevPageButton = NSButton(frame: contentRect)
NSColor.controlBackgroundColor.setFill() NSColor.controlBackgroundColor.setFill()
NSBezierPath.fill(prevPageButton.bounds) NSBezierPath.fill(prevPageButton.bounds)
@ -291,7 +301,8 @@ public class ctlCandidateHorizontal: ctlCandidate {
prevPageButton.bezelStyle = .disclosure prevPageButton.bezelStyle = .disclosure
prevPageButton.userInterfaceLayoutDirection = .rightToLeft prevPageButton.userInterfaceLayoutDirection = .rightToLeft
prevPageButton.attributedTitle = NSMutableAttributedString( prevPageButton.attributedTitle = NSMutableAttributedString(
string: " ", attributes: buttonAttribute) // Previous Page Arrow string: " ", attributes: buttonAttribute
) // Previous Page Arrow
panel.contentView?.addSubview(nextPageButton) panel.contentView?.addSubview(nextPageButton)
panel.contentView?.addSubview(prevPageButton) panel.contentView?.addSubview(prevPageButton)
@ -307,17 +318,18 @@ public class ctlCandidateHorizontal: ctlCandidate {
prevPageButton.action = #selector(pageButtonAction(_:)) prevPageButton.action = #selector(pageButtonAction(_:))
} }
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
public override func reloadData() { override public func reloadData() {
candidateView.highlightedIndex = 0 candidateView.highlightedIndex = 0
currentPage = 0 currentPage = 0
layoutCandidateView() layoutCandidateView()
} }
public override func showNextPage() -> Bool { override public func showNextPage() -> Bool {
guard delegate != nil else { return false } guard delegate != nil else { return false }
if pageCount == 1 { return highlightNextCandidate() } if pageCount == 1 { return highlightNextCandidate() }
currentPage = (currentPage + 1 >= pageCount) ? 0 : currentPage + 1 currentPage = (currentPage + 1 >= pageCount) ? 0 : currentPage + 1
@ -326,7 +338,7 @@ public class ctlCandidateHorizontal: ctlCandidate {
return true return true
} }
public override func showPreviousPage() -> Bool { override public func showPreviousPage() -> Bool {
guard delegate != nil else { return false } guard delegate != nil else { return false }
if pageCount == 1 { return highlightPreviousCandidate() } if pageCount == 1 { return highlightPreviousCandidate() }
currentPage = (currentPage == 0) ? pageCount - 1 : currentPage - 1 currentPage = (currentPage == 0) ? pageCount - 1 : currentPage - 1
@ -335,7 +347,7 @@ public class ctlCandidateHorizontal: ctlCandidate {
return true return true
} }
public override func highlightNextCandidate() -> Bool { override public func highlightNextCandidate() -> Bool {
guard let delegate = delegate else { return false } guard let delegate = delegate else { return false }
selectedCandidateIndex = selectedCandidateIndex =
(selectedCandidateIndex + 1 >= delegate.candidateCountForController(self)) (selectedCandidateIndex + 1 >= delegate.candidateCountForController(self))
@ -343,7 +355,7 @@ public class ctlCandidateHorizontal: ctlCandidate {
return true return true
} }
public override func highlightPreviousCandidate() -> Bool { override public func highlightPreviousCandidate() -> Bool {
guard let delegate = delegate else { return false } guard let delegate = delegate else { return false }
selectedCandidateIndex = selectedCandidateIndex =
(selectedCandidateIndex == 0) (selectedCandidateIndex == 0)
@ -351,7 +363,7 @@ public class ctlCandidateHorizontal: ctlCandidate {
return true return true
} }
public override func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt { override public func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt {
guard let delegate = delegate else { guard let delegate = delegate else {
return UInt.max return UInt.max
} }
@ -360,7 +372,7 @@ public class ctlCandidateHorizontal: ctlCandidate {
return result < delegate.candidateCountForController(self) ? result : UInt.max return result < delegate.candidateCountForController(self) ? result : UInt.max
} }
public override var selectedCandidateIndex: UInt { override public var selectedCandidateIndex: UInt {
get { get {
currentPage * UInt(keyLabels.count) + candidateView.highlightedIndex currentPage * UInt(keyLabels.count) + candidateView.highlightedIndex
} }
@ -379,7 +391,6 @@ public class ctlCandidateHorizontal: ctlCandidate {
} }
extension ctlCandidateHorizontal { extension ctlCandidateHorizontal {
private var pageCount: UInt { private var pageCount: UInt {
guard let delegate = delegate else { guard let delegate = delegate else {
return 0 return 0
@ -405,13 +416,14 @@ extension ctlCandidateHorizontal {
candidates.append(candidate) candidates.append(candidate)
} }
candidateView.set( candidateView.set(
keyLabels: keyLabels.map { $0.displayedText }, displayedCandidates: candidates) keyLabels: keyLabels.map(\.displayedText), displayedCandidates: candidates
)
var newSize = candidateView.sizeForView var newSize = candidateView.sizeForView
var frameRect = candidateView.frame var frameRect = candidateView.frame
frameRect.size = newSize frameRect.size = newSize
candidateView.frame = frameRect candidateView.frame = frameRect
if pageCount > 1 && mgrPrefs.showPageButtonsInCandidateWindow { if pageCount > 1, mgrPrefs.showPageButtonsInCandidateWindow {
var buttonRect = nextPageButton.frame var buttonRect = nextPageButton.frame
let spacing: CGFloat = 0.0 let spacing: CGFloat = 0.0
@ -422,7 +434,8 @@ extension ctlCandidateHorizontal {
nextPageButton.frame = buttonRect nextPageButton.frame = buttonRect
buttonRect.origin = NSPoint( buttonRect.origin = NSPoint(
x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing) x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing
)
prevPageButton.frame = buttonRect prevPageButton.frame = buttonRect
newSize.width += 20 newSize.width += 20
@ -442,7 +455,7 @@ extension ctlCandidateHorizontal {
candidateView.setNeedsDisplay(candidateView.bounds) candidateView.setNeedsDisplay(candidateView.bounds)
} }
@objc fileprivate func pageButtonAction(_ sender: Any) { @objc private func pageButtonAction(_ sender: Any) {
guard let sender = sender as? NSButton else { guard let sender = sender as? NSButton else {
return return
} }
@ -453,8 +466,7 @@ extension ctlCandidateHorizontal {
} }
} }
@objc fileprivate func candidateViewMouseDidClick(_ sender: Any) { @objc private func candidateViewMouseDidClick(_: Any) {
delegate?.ctlCandidate(self, didSelectCandidateAtIndex: selectedCandidateIndex) delegate?.ctlCandidate(self, didSelectCandidateAtIndex: selectedCandidateIndex)
} }
} }

View File

@ -44,7 +44,7 @@ private class VerticalCandidateView: NSView {
private var windowWidth: CGFloat = 0 private var windowWidth: CGFloat = 0
private var elementWidths: [CGFloat] = [] private var elementWidths: [CGFloat] = []
private var elementHeights: [CGFloat] = [] private var elementHeights: [CGFloat] = []
private var trackingHighlightedIndex: UInt = UInt.max private var trackingHighlightedIndex: UInt = .max
override var isFlipped: Bool { override var isFlipped: Bool {
true true
@ -74,7 +74,8 @@ private class VerticalCandidateView: NSView {
for index in 0..<count { for index in 0..<count {
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect( let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
with: baseSize, options: .usesLineFragmentOrigin, with: baseSize, options: .usesLineFragmentOrigin,
attributes: candidateWithLabelAttrDict) attributes: candidateWithLabelAttrDict
)
let cellWidth = rctCandidate.size.width + cellPadding let cellWidth = rctCandidate.size.width + cellPadding
let cellHeight = rctCandidate.size.height + cellPadding let cellHeight = rctCandidate.size.height + cellPadding
if calculatedWindowWidth < rctCandidate.size.width { if calculatedWindowWidth < rctCandidate.size.width {
@ -121,7 +122,7 @@ private class VerticalCandidateView: NSView {
cellPadding = ceil(biggestSize / 2.0) cellPadding = ceil(biggestSize / 2.0)
} }
override func draw(_ dirtyRect: NSRect) { override func draw(_: NSRect) {
let bounds = bounds let bounds = bounds
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
NSBezierPath.fill(bounds) NSBezierPath.fill(bounds)
@ -130,7 +131,8 @@ private class VerticalCandidateView: NSView {
NSBezierPath.strokeLine( NSBezierPath.strokeLine(
from: NSPoint(x: bounds.size.width, y: 0.0), from: NSPoint(x: bounds.size.width, y: 0.0),
to: NSPoint(x: bounds.size.width, y: bounds.size.height)) to: NSPoint(x: bounds.size.width, y: bounds.size.height)
)
var accuHeight: CGFloat = 0 var accuHeight: CGFloat = 0
for index in 0..<elementHeights.count { for index in 0..<elementHeights.count {
@ -140,10 +142,12 @@ private class VerticalCandidateView: NSView {
) )
let rctLabel = NSRect( let rctLabel = NSRect(
x: cellPadding / 2 - 1, y: accuHeight + cellPadding / 2, width: keyLabelWidth, x: cellPadding / 2 - 1, y: accuHeight + cellPadding / 2, width: keyLabelWidth,
height: keyLabelHeight * 2.0) height: keyLabelHeight * 2.0
)
let rctCandidatePhrase = NSRect( let rctCandidatePhrase = NSRect(
x: cellPadding / 2 - 1 + keyLabelWidth, y: accuHeight + cellPadding / 2 - 1, x: cellPadding / 2 - 1 + keyLabelWidth, y: accuHeight + cellPadding / 2 - 1,
width: windowWidth - keyLabelWidth, height: candidateTextHeight) width: windowWidth - keyLabelWidth, height: candidateTextHeight
)
var activeCandidateIndexAttr = keyLabelAttrDict var activeCandidateIndexAttr = keyLabelAttrDict
var activeCandidateAttr = candidateAttrDict var activeCandidateAttr = candidateAttrDict
@ -154,12 +158,14 @@ private class VerticalCandidateView: NSView {
case InputMode.imeModeCHS: case InputMode.imeModeCHS:
NSColor.systemRed.blended( NSColor.systemRed.blended(
withFraction: colorBlendAmount, withFraction: colorBlendAmount,
of: NSColor.controlBackgroundColor)! of: NSColor.controlBackgroundColor
)!
.setFill() .setFill()
case InputMode.imeModeCHT: case InputMode.imeModeCHT:
NSColor.systemBlue.blended( NSColor.systemBlue.blended(
withFraction: colorBlendAmount, withFraction: colorBlendAmount,
of: NSColor.controlBackgroundColor)! of: NSColor.controlBackgroundColor
)!
.setFill() .setFill()
default: default:
NSColor.alternateSelectedControlColor.setFill() NSColor.alternateSelectedControlColor.setFill()
@ -186,9 +192,11 @@ private class VerticalCandidateView: NSView {
} }
NSBezierPath.fill(rctCandidateArea) NSBezierPath.fill(rctCandidateArea)
(keyLabels[index] as NSString).draw( (keyLabels[index] as NSString).draw(
in: rctLabel, withAttributes: activeCandidateIndexAttr) in: rctLabel, withAttributes: activeCandidateIndexAttr
)
(displayedCandidates[index] as NSString).draw( (displayedCandidates[index] as NSString).draw(
in: rctCandidatePhrase, withAttributes: activeCandidateAttr) in: rctCandidatePhrase, withAttributes: activeCandidateAttr
)
accuHeight += currentHeight accuHeight += currentHeight
} }
} }
@ -202,13 +210,12 @@ private class VerticalCandidateView: NSView {
for index in 0..<elementHeights.count { for index in 0..<elementHeights.count {
let currentHeight = elementHeights[index] let currentHeight = elementHeights[index]
if location.y >= accuHeight && location.y <= accuHeight + currentHeight { if location.y >= accuHeight, location.y <= accuHeight + currentHeight {
return UInt(index) return UInt(index)
} }
accuHeight += currentHeight accuHeight += currentHeight
} }
return nil return nil
} }
override func mouseUp(with event: NSEvent) { override func mouseUp(with event: NSEvent) {
@ -251,7 +258,8 @@ public class ctlCandidateVertical: ctlCandidate {
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel] let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
let panel = NSPanel( let panel = NSPanel(
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
)
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
panel.hasShadow = true panel.hasShadow = true
panel.isOpaque = false panel.isOpaque = false
@ -284,7 +292,8 @@ public class ctlCandidateVertical: ctlCandidate {
nextPageButton.bezelStyle = .disclosure nextPageButton.bezelStyle = .disclosure
nextPageButton.userInterfaceLayoutDirection = .leftToRight nextPageButton.userInterfaceLayoutDirection = .leftToRight
nextPageButton.attributedTitle = NSMutableAttributedString( nextPageButton.attributedTitle = NSMutableAttributedString(
string: " ", attributes: buttonAttribute) // Next Page Arrow string: " ", attributes: buttonAttribute
) // Next Page Arrow
prevPageButton = NSButton(frame: contentRect) prevPageButton = NSButton(frame: contentRect)
NSColor.controlBackgroundColor.setFill() NSColor.controlBackgroundColor.setFill()
NSBezierPath.fill(prevPageButton.bounds) NSBezierPath.fill(prevPageButton.bounds)
@ -296,7 +305,8 @@ public class ctlCandidateVertical: ctlCandidate {
prevPageButton.bezelStyle = .disclosure prevPageButton.bezelStyle = .disclosure
prevPageButton.userInterfaceLayoutDirection = .rightToLeft prevPageButton.userInterfaceLayoutDirection = .rightToLeft
prevPageButton.attributedTitle = NSMutableAttributedString( prevPageButton.attributedTitle = NSMutableAttributedString(
string: " ", attributes: buttonAttribute) // Previous Page Arrow string: " ", attributes: buttonAttribute
) // Previous Page Arrow
panel.contentView?.addSubview(nextPageButton) panel.contentView?.addSubview(nextPageButton)
panel.contentView?.addSubview(prevPageButton) panel.contentView?.addSubview(prevPageButton)
@ -312,17 +322,18 @@ public class ctlCandidateVertical: ctlCandidate {
prevPageButton.action = #selector(pageButtonAction(_:)) prevPageButton.action = #selector(pageButtonAction(_:))
} }
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
public override func reloadData() { override public func reloadData() {
candidateView.highlightedIndex = 0 candidateView.highlightedIndex = 0
currentPage = 0 currentPage = 0
layoutCandidateView() layoutCandidateView()
} }
public override func showNextPage() -> Bool { override public func showNextPage() -> Bool {
guard delegate != nil else { return false } guard delegate != nil else { return false }
if pageCount == 1 { return highlightNextCandidate() } if pageCount == 1 { return highlightNextCandidate() }
currentPage = (currentPage + 1 >= pageCount) ? 0 : currentPage + 1 currentPage = (currentPage + 1 >= pageCount) ? 0 : currentPage + 1
@ -331,7 +342,7 @@ public class ctlCandidateVertical: ctlCandidate {
return true return true
} }
public override func showPreviousPage() -> Bool { override public func showPreviousPage() -> Bool {
guard delegate != nil else { return false } guard delegate != nil else { return false }
if pageCount == 1 { return highlightPreviousCandidate() } if pageCount == 1 { return highlightPreviousCandidate() }
currentPage = (currentPage == 0) ? pageCount - 1 : currentPage - 1 currentPage = (currentPage == 0) ? pageCount - 1 : currentPage - 1
@ -340,7 +351,7 @@ public class ctlCandidateVertical: ctlCandidate {
return true return true
} }
public override func highlightNextCandidate() -> Bool { override public func highlightNextCandidate() -> Bool {
guard let delegate = delegate else { return false } guard let delegate = delegate else { return false }
selectedCandidateIndex = selectedCandidateIndex =
(selectedCandidateIndex + 1 >= delegate.candidateCountForController(self)) (selectedCandidateIndex + 1 >= delegate.candidateCountForController(self))
@ -348,7 +359,7 @@ public class ctlCandidateVertical: ctlCandidate {
return true return true
} }
public override func highlightPreviousCandidate() -> Bool { override public func highlightPreviousCandidate() -> Bool {
guard let delegate = delegate else { return false } guard let delegate = delegate else { return false }
selectedCandidateIndex = selectedCandidateIndex =
(selectedCandidateIndex == 0) (selectedCandidateIndex == 0)
@ -356,7 +367,7 @@ public class ctlCandidateVertical: ctlCandidate {
return true return true
} }
public override func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt { override public func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt {
guard let delegate = delegate else { guard let delegate = delegate else {
return UInt.max return UInt.max
} }
@ -365,7 +376,7 @@ public class ctlCandidateVertical: ctlCandidate {
return result < delegate.candidateCountForController(self) ? result : UInt.max return result < delegate.candidateCountForController(self) ? result : UInt.max
} }
public override var selectedCandidateIndex: UInt { override public var selectedCandidateIndex: UInt {
get { get {
currentPage * UInt(keyLabels.count) + candidateView.highlightedIndex currentPage * UInt(keyLabels.count) + candidateView.highlightedIndex
} }
@ -384,7 +395,6 @@ public class ctlCandidateVertical: ctlCandidate {
} }
extension ctlCandidateVertical { extension ctlCandidateVertical {
private var pageCount: UInt { private var pageCount: UInt {
guard let delegate = delegate else { guard let delegate = delegate else {
return 0 return 0
@ -410,13 +420,14 @@ extension ctlCandidateVertical {
candidates.append(candidate) candidates.append(candidate)
} }
candidateView.set( candidateView.set(
keyLabels: keyLabels.map { $0.displayedText }, displayedCandidates: candidates) keyLabels: keyLabels.map(\.displayedText), displayedCandidates: candidates
)
var newSize = candidateView.sizeForView var newSize = candidateView.sizeForView
var frameRect = candidateView.frame var frameRect = candidateView.frame
frameRect.size = newSize frameRect.size = newSize
candidateView.frame = frameRect candidateView.frame = frameRect
if pageCount > 1 && mgrPrefs.showPageButtonsInCandidateWindow { if pageCount > 1, mgrPrefs.showPageButtonsInCandidateWindow {
var buttonRect = nextPageButton.frame var buttonRect = nextPageButton.frame
let spacing: CGFloat = 0.0 let spacing: CGFloat = 0.0
@ -427,7 +438,8 @@ extension ctlCandidateVertical {
nextPageButton.frame = buttonRect nextPageButton.frame = buttonRect
buttonRect.origin = NSPoint( buttonRect.origin = NSPoint(
x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing) x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing
)
prevPageButton.frame = buttonRect prevPageButton.frame = buttonRect
newSize.width += 20 newSize.width += 20
@ -447,7 +459,7 @@ extension ctlCandidateVertical {
candidateView.setNeedsDisplay(candidateView.bounds) candidateView.setNeedsDisplay(candidateView.bounds)
} }
@objc fileprivate func pageButtonAction(_ sender: Any) { @objc private func pageButtonAction(_ sender: Any) {
guard let sender = sender as? NSButton else { guard let sender = sender as? NSButton else {
return return
} }
@ -458,8 +470,7 @@ extension ctlCandidateVertical {
} }
} }
@objc fileprivate func candidateViewMouseDidClick(_ sender: Any) { @objc private func candidateViewMouseDidClick(_: Any) {
delegate?.ctlCandidate(self, didSelectCandidateAtIndex: selectedCandidateIndex) delegate?.ctlCandidate(self, didSelectCandidateAtIndex: selectedCandidateIndex)
} }
} }

View File

@ -33,7 +33,7 @@ private protocol NotifierWindowDelegate: AnyObject {
private class NotifierWindow: NSWindow { private class NotifierWindow: NSWindow {
weak var clickDelegate: NotifierWindowDelegate? weak var clickDelegate: NotifierWindowDelegate?
override func mouseDown(with event: NSEvent) { override func mouseDown(with _: NSEvent) {
clickDelegate?.windowDidBecomeClicked(self) clickDelegate?.windowDidBecomeClicked(self)
} }
} }
@ -58,7 +58,8 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
messageTextField.attributedStringValue = attrString messageTextField.attributedStringValue = attrString
let width = window?.frame.width ?? kWindowWidth let width = window?.frame.width ?? kWindowWidth
let rect = attrString.boundingRect( let rect = attrString.boundingRect(
with: NSSize(width: width, height: 1600), options: .usesLineFragmentOrigin) with: NSSize(width: width, height: 1600), options: .usesLineFragmentOrigin
)
let height = rect.height let height = rect.height
let x = messageTextField.frame.origin.x let x = messageTextField.frame.origin.x
let y = ((window?.frame.height ?? kWindowHeight) - height) / 2 let y = ((window?.frame.height ?? kWindowHeight) - height) / 2
@ -66,17 +67,20 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
messageTextField.frame = newFrame messageTextField.frame = newFrame
} }
} }
private var shouldStay: Bool = false private var shouldStay: Bool = false
private var backgroundColor: NSColor = .textBackgroundColor { private var backgroundColor: NSColor = .textBackgroundColor {
didSet { didSet {
window?.backgroundColor = backgroundColor window?.backgroundColor = backgroundColor
} }
} }
private var foregroundColor: NSColor = .controlTextColor { private var foregroundColor: NSColor = .controlTextColor {
didSet { didSet {
messageTextField.textColor = foregroundColor messageTextField.textColor = foregroundColor
} }
} }
private var waitTimer: Timer? private var waitTimer: Timer?
private var fadeTimer: Timer? private var fadeTimer: Timer?
@ -114,7 +118,8 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
transparentVisualEffect.state = .active transparentVisualEffect.state = .active
let panel = NotifierWindow( let panel = NotifierWindow(
contentRect: windowRect, styleMask: styleMask, backing: .buffered, defer: false) contentRect: windowRect, styleMask: styleMask, backing: .buffered, defer: false
)
panel.contentView = transparentVisualEffect panel.contentView = transparentVisualEffect
panel.isMovableByWindowBackground = true panel.isMovableByWindowBackground = true
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel)) panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel))
@ -144,7 +149,8 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
panel.clickDelegate = self panel.clickDelegate = self
} }
required init?(coder: NSCoder) { @available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -182,10 +188,11 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
waitTimer = Timer.scheduledTimer( waitTimer = Timer.scheduledTimer(
timeInterval: shouldStay ? 5 : 1, target: self, selector: #selector(fadeOut), timeInterval: shouldStay ? 5 : 1, target: self, selector: #selector(fadeOut),
userInfo: nil, userInfo: nil,
repeats: false) repeats: false
)
} }
@objc private func doFadeOut(_ timer: Timer) { @objc private func doFadeOut(_: Timer) {
let opacity = window?.alphaValue ?? 0 let opacity = window?.alphaValue ?? 0
if opacity <= 0 { if opacity <= 0 {
close() close()
@ -200,10 +207,11 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
NotifierController.decreaseInstanceCount() NotifierController.decreaseInstanceCount()
fadeTimer = Timer.scheduledTimer( fadeTimer = Timer.scheduledTimer(
timeInterval: 0.01, target: self, selector: #selector(doFadeOut(_:)), userInfo: nil, timeInterval: 0.01, target: self, selector: #selector(doFadeOut(_:)), userInfo: nil,
repeats: true) repeats: true
)
} }
public override func close() { override public func close() {
waitTimer?.invalidate() waitTimer?.invalidate()
waitTimer = nil waitTimer = nil
fadeTimer?.invalidate() fadeTimer?.invalidate()
@ -211,7 +219,7 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate {
super.close() super.close()
} }
fileprivate func windowDidBecomeClicked(_ window: NotifierWindow) { fileprivate func windowDidBecomeClicked(_: NotifierWindow) {
fadeOut() fadeOut()
} }
} }

View File

@ -32,7 +32,8 @@ class ctlPrefUI {
identifier: Preferences.PaneIdentifier(rawValue: "General"), identifier: Preferences.PaneIdentifier(rawValue: "General"),
title: NSLocalizedString("General", comment: ""), title: NSLocalizedString("General", comment: ""),
toolbarIcon: NSImage( toolbarIcon: NSImage(
systemSymbolName: "wrench.and.screwdriver.fill", accessibilityDescription: "General Preferences") systemSymbolName: "wrench.and.screwdriver.fill", accessibilityDescription: "General Preferences"
)
?? NSImage(named: NSImage.homeTemplateName)! ?? NSImage(named: NSImage.homeTemplateName)!
) { ) {
suiPrefPaneGeneral() suiPrefPaneGeneral()
@ -41,7 +42,8 @@ class ctlPrefUI {
identifier: Preferences.PaneIdentifier(rawValue: "Experiences"), identifier: Preferences.PaneIdentifier(rawValue: "Experiences"),
title: NSLocalizedString("Experience", comment: ""), title: NSLocalizedString("Experience", comment: ""),
toolbarIcon: NSImage( toolbarIcon: NSImage(
systemSymbolName: "person.fill.questionmark", accessibilityDescription: "Experiences Preferences") systemSymbolName: "person.fill.questionmark", accessibilityDescription: "Experiences Preferences"
)
?? NSImage(named: NSImage.listViewTemplateName)! ?? NSImage(named: NSImage.listViewTemplateName)!
) { ) {
suiPrefPaneExperience() suiPrefPaneExperience()
@ -50,7 +52,8 @@ class ctlPrefUI {
identifier: Preferences.PaneIdentifier(rawValue: "Dictionary"), identifier: Preferences.PaneIdentifier(rawValue: "Dictionary"),
title: NSLocalizedString("Dictionary", comment: ""), title: NSLocalizedString("Dictionary", comment: ""),
toolbarIcon: NSImage( toolbarIcon: NSImage(
systemSymbolName: "character.book.closed.fill", accessibilityDescription: "Dictionary Preferences") systemSymbolName: "character.book.closed.fill", accessibilityDescription: "Dictionary Preferences"
)
?? NSImage(named: NSImage.bookmarksTemplateName)! ?? NSImage(named: NSImage.bookmarksTemplateName)!
) { ) {
suiPrefPaneDictionary() suiPrefPaneDictionary()
@ -59,7 +62,8 @@ class ctlPrefUI {
identifier: Preferences.PaneIdentifier(rawValue: "Keyboard"), identifier: Preferences.PaneIdentifier(rawValue: "Keyboard"),
title: NSLocalizedString("Keyboard", comment: ""), title: NSLocalizedString("Keyboard", comment: ""),
toolbarIcon: NSImage( toolbarIcon: NSImage(
systemSymbolName: "keyboard.macwindow", accessibilityDescription: "Keyboard Preferences") systemSymbolName: "keyboard.macwindow", accessibilityDescription: "Keyboard Preferences"
)
?? NSImage(named: NSImage.actionTemplateName)! ?? NSImage(named: NSImage.actionTemplateName)!
) { ) {
suiPrefPaneKeyboard() suiPrefPaneKeyboard()

View File

@ -57,7 +57,8 @@ struct suiPrefPaneDictionary: View {
.help(tbxUserDataPathSpecified) .help(tbxUserDataPathSpecified)
Button { Button {
IME.dlgOpenPath.title = NSLocalizedString( IME.dlgOpenPath.title = NSLocalizedString(
"Choose your desired user data folder.", comment: "") "Choose your desired user data folder.", comment: ""
)
IME.dlgOpenPath.showsResizeIndicator = true IME.dlgOpenPath.showsResizeIndicator = true
IME.dlgOpenPath.showsHiddenFiles = true IME.dlgOpenPath.showsHiddenFiles = true
IME.dlgOpenPath.canChooseFiles = false IME.dlgOpenPath.canChooseFiles = false
@ -103,27 +104,25 @@ struct suiPrefPaneDictionary: View {
} label: { } label: {
Text("") Text("")
} }
} }
Toggle( Toggle(
LocalizedStringKey("Automatically reload user data files if changes detected"), LocalizedStringKey("Automatically reload user data files if changes detected"),
isOn: $selAutoReloadUserData isOn: $selAutoReloadUserData
).controlSize(.small).onChange(of: selAutoReloadUserData) { (value) in ).controlSize(.small).onChange(of: selAutoReloadUserData) { value in
mgrPrefs.shouldAutoReloadUserDataFiles = value mgrPrefs.shouldAutoReloadUserDataFiles = value
} }
Divider() Divider()
Toggle(LocalizedStringKey("Enable CNS11643 Support (2022-01-27)"), isOn: $selEnableCNS11643) Toggle(LocalizedStringKey("Enable CNS11643 Support (2022-01-27)"), isOn: $selEnableCNS11643)
.onChange(of: selEnableCNS11643) { (value) in .onChange(of: selEnableCNS11643) { value in
mgrPrefs.cns11643Enabled = value mgrPrefs.cns11643Enabled = value
} }
Toggle( Toggle(
LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"), LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"),
isOn: $selEnableSymbolInputSupport isOn: $selEnableSymbolInputSupport
) )
.onChange(of: selEnableSymbolInputSupport) { (value) in .onChange(of: selEnableSymbolInputSupport) { value in
mgrPrefs.symbolInputEnabled = value mgrPrefs.symbolInputEnabled = value
} }
} }
} }
} }

View File

@ -63,7 +63,7 @@ struct suiPrefPaneExperience: View {
Preferences.Section(bottomDivider: true, label: { Text(LocalizedStringKey("Selection Keys:")) }) { Preferences.Section(bottomDivider: true, label: { Text(LocalizedStringKey("Selection Keys:")) }) {
ComboBox(items: mgrPrefs.suggestedCandidateKeys, text: $selSelectionKeys).frame(width: 180).onChange( ComboBox(items: mgrPrefs.suggestedCandidateKeys, text: $selSelectionKeys).frame(width: 180).onChange(
of: selSelectionKeys of: selSelectionKeys
) { (value) in ) { value in
let keys: String = (value.trimmingCharacters(in: .whitespacesAndNewlines) as String).charDeDuplicate let keys: String = (value.trimmingCharacters(in: .whitespacesAndNewlines) as String).charDeDuplicate
do { do {
try mgrPrefs.validate(candidateKeys: keys) try mgrPrefs.validate(candidateKeys: keys)
@ -92,7 +92,7 @@ struct suiPrefPaneExperience: View {
Picker("", selection: $selCursorPosition) { Picker("", selection: $selCursorPosition) {
Text(LocalizedStringKey("to the front of the phrase (like Matsushita Hanin IME)")).tag(0) Text(LocalizedStringKey("to the front of the phrase (like Matsushita Hanin IME)")).tag(0)
Text(LocalizedStringKey("to the rear of the phrase (like MS New-Phonetic IME)")).tag(1) Text(LocalizedStringKey("to the rear of the phrase (like MS New-Phonetic IME)")).tag(1)
}.onChange(of: selCursorPosition) { (value) in }.onChange(of: selCursorPosition) { value in
mgrPrefs.selectPhraseAfterCursorAsCandidate = (value == 1) ? true : false mgrPrefs.selectPhraseAfterCursorAsCandidate = (value == 1) ? true : false
} }
.labelsHidden() .labelsHidden()
@ -102,7 +102,7 @@ struct suiPrefPaneExperience: View {
Toggle( Toggle(
LocalizedStringKey("Push the cursor to the front of the phrase after selection"), LocalizedStringKey("Push the cursor to the front of the phrase after selection"),
isOn: $selPushCursorAfterSelection isOn: $selPushCursorAfterSelection
).onChange(of: selPushCursorAfterSelection) { (value) in ).onChange(of: selPushCursorAfterSelection) { value in
mgrPrefs.moveCursorAfterSelectingCandidate = value mgrPrefs.moveCursorAfterSelectingCandidate = value
}.controlSize(.small) }.controlSize(.small)
} }
@ -110,7 +110,7 @@ struct suiPrefPaneExperience: View {
Picker("", selection: $selKeyBehaviorShiftTab) { Picker("", selection: $selKeyBehaviorShiftTab) {
Text(LocalizedStringKey("for cycling candidates")).tag(0) Text(LocalizedStringKey("for cycling candidates")).tag(0)
Text(LocalizedStringKey("for cycling pages")).tag(1) Text(LocalizedStringKey("for cycling pages")).tag(1)
}.onChange(of: selKeyBehaviorShiftTab) { (value) in }.onChange(of: selKeyBehaviorShiftTab) { value in
mgrPrefs.specifyShiftTabKeyBehavior = (value == 1) ? true : false mgrPrefs.specifyShiftTabKeyBehavior = (value == 1) ? true : false
} }
.labelsHidden() .labelsHidden()
@ -123,7 +123,7 @@ struct suiPrefPaneExperience: View {
Picker("", selection: $selKeyBehaviorShiftSpace) { Picker("", selection: $selKeyBehaviorShiftSpace) {
Text(LocalizedStringKey("Space to +cycle candidates, Shift+Space to +cycle pages")).tag(0) Text(LocalizedStringKey("Space to +cycle candidates, Shift+Space to +cycle pages")).tag(0)
Text(LocalizedStringKey("Space to +cycle pages, Shift+Space to +cycle candidates")).tag(1) Text(LocalizedStringKey("Space to +cycle pages, Shift+Space to +cycle candidates")).tag(1)
}.onChange(of: selKeyBehaviorShiftSpace) { (value) in }.onChange(of: selKeyBehaviorShiftSpace) { value in
mgrPrefs.specifyShiftSpaceKeyBehavior = (value == 1) ? true : false mgrPrefs.specifyShiftSpaceKeyBehavior = (value == 1) ? true : false
} }
.labelsHidden() .labelsHidden()
@ -135,20 +135,20 @@ struct suiPrefPaneExperience: View {
Toggle( Toggle(
LocalizedStringKey("Enable Space key for calling candidate window"), LocalizedStringKey("Enable Space key for calling candidate window"),
isOn: $selKeyBehaviorSpaceForCallingCandidate isOn: $selKeyBehaviorSpaceForCallingCandidate
).onChange(of: selKeyBehaviorSpaceForCallingCandidate) { (value) in ).onChange(of: selKeyBehaviorSpaceForCallingCandidate) { value in
mgrPrefs.chooseCandidateUsingSpace = value mgrPrefs.chooseCandidateUsingSpace = value
} }
Toggle( Toggle(
LocalizedStringKey("Use ESC key to clear the entire input buffer"), LocalizedStringKey("Use ESC key to clear the entire input buffer"),
isOn: $selKeyBehaviorESCForClearingTheBuffer isOn: $selKeyBehaviorESCForClearingTheBuffer
).onChange(of: selKeyBehaviorESCForClearingTheBuffer) { (value) in ).onChange(of: selKeyBehaviorESCForClearingTheBuffer) { value in
mgrPrefs.escToCleanInputBuffer = value mgrPrefs.escToCleanInputBuffer = value
} }
} }
Preferences.Section(label: { Text(LocalizedStringKey("Typing Style:")) }) { Preferences.Section(label: { Text(LocalizedStringKey("Typing Style:")) }) {
Toggle( Toggle(
LocalizedStringKey("Emulating select-candidate-per-character mode"), isOn: $selEnableSCPCTypingMode LocalizedStringKey("Emulating select-candidate-per-character mode"), isOn: $selEnableSCPCTypingMode
).onChange(of: selEnableSCPCTypingMode) { (value) in ).onChange(of: selEnableSCPCTypingMode) { value in
mgrPrefs.useSCPCTypingMode = value mgrPrefs.useSCPCTypingMode = value
} }
Text(LocalizedStringKey("An accomodation for elder computer users.")) Text(LocalizedStringKey("An accomodation for elder computer users."))

View File

@ -71,7 +71,7 @@ struct suiPrefPaneGeneral: View {
Text("32").tag(32) Text("32").tag(32)
Text("64").tag(64) Text("64").tag(64)
Text("96").tag(96) Text("96").tag(96)
}.onChange(of: selCandidateUIFontSize) { (value) in }.onChange(of: selCandidateUIFontSize) { value in
mgrPrefs.candidateListTextSize = CGFloat(value) mgrPrefs.candidateListTextSize = CGFloat(value)
} }
.labelsHidden() .labelsHidden()
@ -86,7 +86,7 @@ struct suiPrefPaneGeneral: View {
Text(LocalizedStringKey("Traditional Chinese")).tag(["zh-Hant"]) Text(LocalizedStringKey("Traditional Chinese")).tag(["zh-Hant"])
Text(LocalizedStringKey("Japanese")).tag(["ja"]) Text(LocalizedStringKey("Japanese")).tag(["ja"])
Text(LocalizedStringKey("English")).tag(["en"]) Text(LocalizedStringKey("English")).tag(["en"])
}.onChange(of: selUILanguage) { (value) in }.onChange(of: selUILanguage) { value in
IME.prtDebugIntel(value[0]) IME.prtDebugIntel(value[0])
if selUILanguage == mgrPrefs.appleLanguages if selUILanguage == mgrPrefs.appleLanguages
|| (selUILanguage[0] == "auto" || (selUILanguage[0] == "auto"
@ -112,7 +112,7 @@ struct suiPrefPaneGeneral: View {
Picker("", selection: $selEnableHorizontalCandidateLayout) { Picker("", selection: $selEnableHorizontalCandidateLayout) {
Text(LocalizedStringKey("Vertical")).tag(false) Text(LocalizedStringKey("Vertical")).tag(false)
Text(LocalizedStringKey("Horizontal")).tag(true) Text(LocalizedStringKey("Horizontal")).tag(true)
}.onChange(of: selEnableHorizontalCandidateLayout) { (value) in }.onChange(of: selEnableHorizontalCandidateLayout) { value in
mgrPrefs.useHorizontalCandidateList = value mgrPrefs.useHorizontalCandidateList = value
} }
.labelsHidden() .labelsHidden()
@ -128,31 +128,31 @@ struct suiPrefPaneGeneral: View {
Toggle( Toggle(
LocalizedStringKey("Auto-convert traditional Chinese glyphs to KangXi characters"), LocalizedStringKey("Auto-convert traditional Chinese glyphs to KangXi characters"),
isOn: $selEnableKanjiConvToKangXi isOn: $selEnableKanjiConvToKangXi
).onChange(of: selEnableKanjiConvToKangXi) { (value) in ).onChange(of: selEnableKanjiConvToKangXi) { value in
mgrPrefs.chineseConversionEnabled = value mgrPrefs.chineseConversionEnabled = value
} }
Toggle( Toggle(
LocalizedStringKey("Auto-convert traditional Chinese glyphs to JIS Shinjitai characters"), LocalizedStringKey("Auto-convert traditional Chinese glyphs to JIS Shinjitai characters"),
isOn: $selEnableKanjiConvToJIS isOn: $selEnableKanjiConvToJIS
).onChange(of: selEnableKanjiConvToJIS) { (value) in ).onChange(of: selEnableKanjiConvToJIS) { value in
mgrPrefs.shiftJISShinjitaiOutputEnabled = value mgrPrefs.shiftJISShinjitaiOutputEnabled = value
} }
Toggle( Toggle(
LocalizedStringKey("Stop farting (when typed phonetic combination is invalid, etc.)"), LocalizedStringKey("Stop farting (when typed phonetic combination is invalid, etc.)"),
isOn: $selEnableFartSuppressor isOn: $selEnableFartSuppressor
).onChange(of: selEnableFartSuppressor) { (value) in ).onChange(of: selEnableFartSuppressor) { value in
mgrPrefs.shouldNotFartInLieuOfBeep = value mgrPrefs.shouldNotFartInLieuOfBeep = value
clsSFX.beep() clsSFX.beep()
} }
} }
Preferences.Section(label: { Text(LocalizedStringKey("Misc Settings:")).controlSize(.small) }) { Preferences.Section(label: { Text(LocalizedStringKey("Misc Settings:")).controlSize(.small) }) {
Toggle(LocalizedStringKey("Check for updates automatically"), isOn: $selEnableAutoUpdateCheck) Toggle(LocalizedStringKey("Check for updates automatically"), isOn: $selEnableAutoUpdateCheck)
.onChange(of: selEnableAutoUpdateCheck) { (value) in .onChange(of: selEnableAutoUpdateCheck) { value in
mgrPrefs.checkUpdateAutomatically = value mgrPrefs.checkUpdateAutomatically = value
} }
.controlSize(.small) .controlSize(.small)
Toggle(LocalizedStringKey("Debug Mode"), isOn: $selEnableDebugMode).controlSize(.small) Toggle(LocalizedStringKey("Debug Mode"), isOn: $selEnableDebugMode).controlSize(.small)
.onChange(of: selEnableDebugMode) { (value) in .onChange(of: selEnableDebugMode) { value in
mgrPrefs.isDebugModeEnabled = value mgrPrefs.isDebugModeEnabled = value
} }
} }

View File

@ -54,7 +54,7 @@ struct suiPrefPaneKeyboard: View {
Text(LocalizedStringKey("MiTAC")).tag(5) Text(LocalizedStringKey("MiTAC")).tag(5)
Text(LocalizedStringKey("Fake Seigyou")).tag(6) Text(LocalizedStringKey("Fake Seigyou")).tag(6)
Text(LocalizedStringKey("Hanyu Pinyin with Numeral Intonation")).tag(10) Text(LocalizedStringKey("Hanyu Pinyin with Numeral Intonation")).tag(10)
}.onChange(of: selMandarinParser) { (value) in }.onChange(of: selMandarinParser) { value in
mgrPrefs.mandarinParser = value mgrPrefs.mandarinParser = value
} }
.labelsHidden() .labelsHidden()
@ -69,7 +69,7 @@ struct suiPrefPaneKeyboard: View {
Text(IME.arrEnumerateSystemKeyboardLayouts[id].strName).tag( Text(IME.arrEnumerateSystemKeyboardLayouts[id].strName).tag(
IME.arrEnumerateSystemKeyboardLayouts[id].strValue) IME.arrEnumerateSystemKeyboardLayouts[id].strValue)
}.id(UUID()) }.id(UUID())
}.onChange(of: selBasicKeyboardLayout) { (value) in }.onChange(of: selBasicKeyboardLayout) { value in
mgrPrefs.basicKeyboardLayout = value mgrPrefs.basicKeyboardLayout = value
} }
.labelsHidden() .labelsHidden()

View File

@ -41,7 +41,8 @@ public class TooltipController: NSWindowController {
let contentRect = NSRect(x: 128.0, y: 128.0, width: 300.0, height: 20.0) let contentRect = NSRect(x: 128.0, y: 128.0, width: 300.0, height: 20.0)
let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel] let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel]
let panel = NSPanel( let panel = NSPanel(
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
)
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
panel.hasShadow = true panel.hasShadow = true
@ -58,7 +59,8 @@ public class TooltipController: NSWindowController {
super.init(window: panel) super.init(window: panel)
} }
public required init?(coder: NSCoder) { @available(*, unavailable)
public required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -77,15 +79,14 @@ public class TooltipController: NSWindowController {
} }
private func set(windowLocation windowTopLeftPoint: NSPoint) { private func set(windowLocation windowTopLeftPoint: NSPoint) {
var adjustedPoint = windowTopLeftPoint var adjustedPoint = windowTopLeftPoint
adjustedPoint.y -= 5 adjustedPoint.y -= 5
var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero
for screen in NSScreen.screens { for screen in NSScreen.screens {
let frame = screen.visibleFrame let frame = screen.visibleFrame
if windowTopLeftPoint.x >= frame.minX && windowTopLeftPoint.x <= frame.maxX if windowTopLeftPoint.x >= frame.minX, windowTopLeftPoint.x <= frame.maxX,
&& windowTopLeftPoint.y >= frame.minY && windowTopLeftPoint.y <= frame.maxY windowTopLeftPoint.y >= frame.minY, windowTopLeftPoint.y <= frame.maxY
{ {
screenFrame = frame screenFrame = frame
break break
@ -115,16 +116,15 @@ public class TooltipController: NSWindowController {
} }
window?.setFrameTopLeftPoint(adjustedPoint) window?.setFrameTopLeftPoint(adjustedPoint)
} }
private func adjustSize() { private func adjustSize() {
let attrString = messageTextField.attributedStringValue let attrString = messageTextField.attributedStringValue
var rect = attrString.boundingRect( var rect = attrString.boundingRect(
with: NSSize(width: 1600.0, height: 1600.0), options: .usesLineFragmentOrigin) with: NSSize(width: 1600.0, height: 1600.0), options: .usesLineFragmentOrigin
)
rect.size.width += 10 rect.size.width += 10
messageTextField.frame = rect messageTextField.frame = rect
window?.setFrame(rect, display: true) window?.setFrame(rect, display: true)
} }
} }

View File

@ -27,8 +27,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
@objc(AboutWindow) class ctlAboutWindow: NSWindowController { @objc(AboutWindow) class ctlAboutWindow: NSWindowController {
@IBOutlet weak var appVersionLabel: NSTextField! @IBOutlet var appVersionLabel: NSTextField!
@IBOutlet weak var appCopyrightLabel: NSTextField! @IBOutlet var appCopyrightLabel: NSTextField!
@IBOutlet var appEULAContent: NSTextView! @IBOutlet var appEULAContent: NSTextView!
override func windowDidLoad() { override func windowDidLoad() {
@ -53,10 +53,11 @@ import Cocoa
appEULAContent.string = eulaContent appEULAContent.string = eulaContent
} }
appVersionLabel.stringValue = String( appVersionLabel.stringValue = String(
format: "%@ Build %@", versionString, installingVersion) format: "%@ Build %@", versionString, installingVersion
)
} }
@IBAction func btnWiki(_ sender: NSButton) { @IBAction func btnWiki(_: NSButton) {
if let url = URL(string: "https://gitee.com/vchewing/vChewing-macOS/wikis") { if let url = URL(string: "https://gitee.com/vchewing/vChewing-macOS/wikis") {
NSWorkspace.shared.open(url) NSWorkspace.shared.open(url)
} }

View File

@ -35,10 +35,10 @@ class ctlNonModalAlertWindow: NSWindowController {
@objc(sharedInstance) @objc(sharedInstance)
static let shared = ctlNonModalAlertWindow(windowNibName: "frmNonModalAlertWindow") static let shared = ctlNonModalAlertWindow(windowNibName: "frmNonModalAlertWindow")
@IBOutlet weak var titleTextField: NSTextField! @IBOutlet var titleTextField: NSTextField!
@IBOutlet weak var contentTextField: NSTextField! @IBOutlet var contentTextField: NSTextField!
@IBOutlet weak var confirmButton: NSButton! @IBOutlet var confirmButton: NSButton!
@IBOutlet weak var cancelButton: NSButton! @IBOutlet var cancelButton: NSButton!
weak var delegate: ctlNonModalAlertWindowDelegate? weak var delegate: ctlNonModalAlertWindowDelegate?
@objc func show( @objc func show(
@ -96,7 +96,8 @@ class ctlNonModalAlertWindow: NSWindowController {
infiniteHeightFrame.size.height = 10240 infiniteHeightFrame.size.height = 10240
newFrame = (content as NSString).boundingRect( newFrame = (content as NSString).boundingRect(
with: infiniteHeightFrame.size, options: [.usesLineFragmentOrigin], with: infiniteHeightFrame.size, options: [.usesLineFragmentOrigin],
attributes: [.font: contentTextField.font!]) attributes: [.font: contentTextField.font!]
)
newFrame.size.width = max(newFrame.size.width, oldFrame.size.width) newFrame.size.width = max(newFrame.size.width, oldFrame.size.width)
newFrame.size.height += 4.0 newFrame.size.height += 4.0
newFrame.origin = oldFrame.origin newFrame.origin = oldFrame.origin
@ -112,7 +113,7 @@ class ctlNonModalAlertWindow: NSWindowController {
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
@IBAction func confirmButtonAction(_ sender: Any) { @IBAction func confirmButtonAction(_: Any) {
delegate?.ctlNonModalAlertWindowDidConfirm(self) delegate?.ctlNonModalAlertWindowDidConfirm(self)
window?.orderOut(self) window?.orderOut(self)
} }
@ -121,10 +122,9 @@ class ctlNonModalAlertWindow: NSWindowController {
cancel(sender) cancel(sender)
} }
func cancel(_ sender: Any) { func cancel(_: Any) {
delegate?.ctlNonModalAlertWindowDidCancel(self) delegate?.ctlNonModalAlertWindowDidCancel(self)
delegate = nil delegate = nil
window?.orderOut(self) window?.orderOut(self)
} }
} }

View File

@ -31,13 +31,13 @@ import Cocoa
// in Objective-C in order to let IMK to see the same class name as // in Objective-C in order to let IMK to see the same class name as
// the "InputMethodServerPreferencesWindowControllerClass" in Info.plist. // the "InputMethodServerPreferencesWindowControllerClass" in Info.plist.
@objc(ctlPrefWindow) class ctlPrefWindow: NSWindowController { @objc(ctlPrefWindow) class ctlPrefWindow: NSWindowController {
@IBOutlet weak var fontSizePopUpButton: NSPopUpButton! @IBOutlet var fontSizePopUpButton: NSPopUpButton!
@IBOutlet weak var uiLanguageButton: NSPopUpButton! @IBOutlet var uiLanguageButton: NSPopUpButton!
@IBOutlet weak var basicKeyboardLayoutButton: NSPopUpButton! @IBOutlet var basicKeyboardLayoutButton: NSPopUpButton!
@IBOutlet weak var selectionKeyComboBox: NSComboBox! @IBOutlet var selectionKeyComboBox: NSComboBox!
@IBOutlet weak var chkTrad2KangXi: NSButton! @IBOutlet var chkTrad2KangXi: NSButton!
@IBOutlet weak var chkTrad2JISShinjitai: NSButton! @IBOutlet var chkTrad2JISShinjitai: NSButton!
@IBOutlet weak var lblCurrentlySpecifiedUserDataFolder: NSTextFieldCell! @IBOutlet var lblCurrentlySpecifiedUserDataFolder: NSTextFieldCell!
var currentLanguageSelectItem: NSMenuItem? var currentLanguageSelectItem: NSMenuItem?
@ -55,7 +55,7 @@ import Cocoa
let appleLanguages = mgrPrefs.appleLanguages let appleLanguages = mgrPrefs.appleLanguages
for language in languages { for language in languages {
let menuItem = NSMenuItem() let menuItem = NSMenuItem()
menuItem.title = NSLocalizedString(language, comment: "") menuItem.title = NSLocalizedString(language, comment: language)
menuItem.representedObject = language menuItem.representedObject = language
if language == "auto" { if language == "auto" {
@ -105,8 +105,8 @@ import Cocoa
} }
if let asciiCapablePtr = TISGetInputSourceProperty( if let asciiCapablePtr = TISGetInputSourceProperty(
source, kTISPropertyInputSourceIsASCIICapable) source, kTISPropertyInputSourceIsASCIICapable
{ ) {
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(asciiCapablePtr) let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(asciiCapablePtr)
.takeUnretainedValue() .takeUnretainedValue()
if asciiCapable != kCFBooleanTrue { if asciiCapable != kCFBooleanTrue {
@ -175,33 +175,33 @@ import Cocoa
// CNS // CNS
// //
@IBAction func toggleCNSSupport(_ sender: Any) { @IBAction func toggleCNSSupport(_: Any) {
mgrLangModel.setCNSEnabled(mgrPrefs.cns11643Enabled) mgrLangModel.setCNSEnabled(mgrPrefs.cns11643Enabled)
} }
@IBAction func toggleSymbolInputEnabled(_ sender: Any) { @IBAction func toggleSymbolInputEnabled(_: Any) {
mgrLangModel.setSymbolEnabled(mgrPrefs.symbolInputEnabled) mgrLangModel.setSymbolEnabled(mgrPrefs.symbolInputEnabled)
} }
@IBAction func toggleTrad2KangXiAction(_ sender: Any) { @IBAction func toggleTrad2KangXiAction(_: Any) {
if chkTrad2KangXi.state == .on && chkTrad2JISShinjitai.state == .on { if chkTrad2KangXi.state == .on, chkTrad2JISShinjitai.state == .on {
mgrPrefs.toggleShiftJISShinjitaiOutputEnabled() mgrPrefs.toggleShiftJISShinjitaiOutputEnabled()
} }
} }
@IBAction func toggleTrad2JISShinjitaiAction(_ sender: Any) { @IBAction func toggleTrad2JISShinjitaiAction(_: Any) {
if chkTrad2KangXi.state == .on && chkTrad2JISShinjitai.state == .on { if chkTrad2KangXi.state == .on, chkTrad2JISShinjitai.state == .on {
mgrPrefs.toggleChineseConversionEnabled() mgrPrefs.toggleChineseConversionEnabled()
} }
} }
@IBAction func updateBasicKeyboardLayoutAction(_ sender: Any) { @IBAction func updateBasicKeyboardLayoutAction(_: Any) {
if let sourceID = basicKeyboardLayoutButton.selectedItem?.representedObject as? String { if let sourceID = basicKeyboardLayoutButton.selectedItem?.representedObject as? String {
mgrPrefs.basicKeyboardLayout = sourceID mgrPrefs.basicKeyboardLayout = sourceID
} }
} }
@IBAction func updateUiLanguageAction(_ sender: Any) { @IBAction func updateUiLanguageAction(_: Any) {
if let selectItem = uiLanguageButton.selectedItem { if let selectItem = uiLanguageButton.selectedItem {
if currentLanguageSelectItem == selectItem { if currentLanguageSelectItem == selectItem {
return return
@ -219,7 +219,7 @@ import Cocoa
} }
} }
@IBAction func clickedWhetherIMEShouldNotFartToggleAction(_ sender: Any) { @IBAction func clickedWhetherIMEShouldNotFartToggleAction(_: Any) {
clsSFX.beep() clsSFX.beep()
} }
@ -249,13 +249,14 @@ import Cocoa
} }
} }
@IBAction func resetSpecifiedUserDataFolder(_ sender: Any) { @IBAction func resetSpecifiedUserDataFolder(_: Any) {
mgrPrefs.resetSpecifiedUserDataFolder() mgrPrefs.resetSpecifiedUserDataFolder()
} }
@IBAction func chooseUserDataFolderToSpecify(_ sender: Any) { @IBAction func chooseUserDataFolderToSpecify(_: Any) {
IME.dlgOpenPath.title = NSLocalizedString( IME.dlgOpenPath.title = NSLocalizedString(
"Choose your desired user data folder.", comment: "") "Choose your desired user data folder.", comment: ""
)
IME.dlgOpenPath.showsResizeIndicator = true IME.dlgOpenPath.showsResizeIndicator = true
IME.dlgOpenPath.showsHiddenFiles = true IME.dlgOpenPath.showsHiddenFiles = true
IME.dlgOpenPath.canChooseFiles = false IME.dlgOpenPath.canChooseFiles = false
@ -292,5 +293,4 @@ import Cocoa
} }
} // End If self.window != nil } // End If self.window != nil
} // End IBAction } // End IBAction
} }

View File

@ -26,20 +26,20 @@ import Cocoa
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window
func applicationDidFinishLaunching(_ aNotification: Notification) { func applicationDidFinishLaunching(_: Notification) {
// Insert code here to initialize your application // Insert code here to initialize your application
} }
func applicationWillTerminate(_ aNotification: Notification) { func applicationWillTerminate(_: Notification) {
// Insert code here to tear down your application // Insert code here to tear down your application
} }
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { func applicationShouldTerminate(_: NSApplication) -> NSApplication.TerminateReply {
.terminateNow .terminateNow
} }
// New About Window // New About Window
@objc func showAbout() { @objc func showAbout() {
if ctlAboutWindowInstance == nil { if ctlAboutWindowInstance == nil {
@ -49,8 +49,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
ctlAboutWindowInstance?.window?.orderFrontRegardless() // ctlAboutWindowInstance?.window?.orderFrontRegardless() //
ctlAboutWindowInstance?.window?.level = .statusBar ctlAboutWindowInstance?.window?.level = .statusBar
} }
// Call the New About Window // Call the New About Window
@IBAction func about(_ sender: Any) { @IBAction func about(_: Any) {
(NSApp.delegate as? AppDelegate)?.showAbout() (NSApp.delegate as? AppDelegate)?.showAbout()
NSApplication.shared.activate(ignoringOtherApps: true) NSApplication.shared.activate(ignoringOtherApps: true)
} }

View File

@ -31,11 +31,9 @@ class Content: NSObject {
public init(contentString: String) { public init(contentString: String) {
self.contentString = contentString self.contentString = contentString
} }
} }
extension Content { extension Content {
func read(from data: Data) { func read(from data: Data) {
contentString = String(bytes: data, encoding: .utf8)! contentString = String(bytes: data, encoding: .utf8)!
} }
@ -43,5 +41,4 @@ extension Content {
func data() -> Data? { func data() -> Data? {
contentString.data(using: .utf8) contentString.data(using: .utf8)
} }
} }

View File

@ -25,7 +25,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
class Document: NSDocument { class Document: NSDocument {
@objc var content = Content(contentString: "") @objc var content = Content(contentString: "")
var contentViewController: ViewController! var contentViewController: ViewController!
@ -43,7 +42,7 @@ class Document: NSDocument {
// This enables asynchronous-writing. // This enables asynchronous-writing.
override func canAsynchronouslyWrite( override func canAsynchronouslyWrite(
to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType to _: URL, ofType _: String, for _: NSDocument.SaveOperationType
) -> Bool { ) -> Bool {
true true
} }
@ -77,7 +76,7 @@ class Document: NSDocument {
// MARK: - Reading and Writing // MARK: - Reading and Writing
/// - Tag: readExample /// - Tag: readExample
override func read(from data: Data, ofType typeName: String) throws { override func read(from data: Data, ofType _: String) throws {
var strToDealWith = String(decoding: data, as: UTF8.self) var strToDealWith = String(decoding: data, as: UTF8.self)
strToDealWith.formatConsolidate(cnvHYPYtoBPMF: false) strToDealWith.formatConsolidate(cnvHYPYtoBPMF: false)
let processedIncomingData = Data(strToDealWith.utf8) let processedIncomingData = Data(strToDealWith.utf8)
@ -85,7 +84,7 @@ class Document: NSDocument {
} }
/// - Tag: writeExample /// - Tag: writeExample
override func data(ofType typeName: String) throws -> Data { override func data(ofType _: String) throws -> Data {
var strToDealWith = content.contentString var strToDealWith = content.contentString
strToDealWith.formatConsolidate(cnvHYPYtoBPMF: true) strToDealWith.formatConsolidate(cnvHYPYtoBPMF: true)
let outputData = Data(strToDealWith.utf8) let outputData = Data(strToDealWith.utf8)
@ -108,24 +107,26 @@ class Document: NSDocument {
printInfo.dictionary().setObject( printInfo.dictionary().setObject(
NSNumber(value: true), NSNumber(value: true),
forKey: NSPrintInfo.AttributeKey.headerAndFooter as NSCopying) forKey: NSPrintInfo.AttributeKey.headerAndFooter as NSCopying
)
return thePrintInfo return thePrintInfo
} }
@objc @objc
func printOperationDidRun( func printOperationDidRun(
_ printOperation: NSPrintOperation, success: Bool, contextInfo: UnsafeMutableRawPointer? _: NSPrintOperation, success _: Bool, contextInfo _: UnsafeMutableRawPointer?
) { ) {
// Printing finished... // Printing finished...
} }
@IBAction override func printDocument(_ sender: Any?) { @IBAction override func printDocument(_: Any?) {
// Print the NSTextView. // Print the NSTextView.
// Create a copy to manipulate for printing. // Create a copy to manipulate for printing.
let pageSize = NSSize( let pageSize = NSSize(
width: (printInfo.paperSize.width), height: (printInfo.paperSize.height)) width: printInfo.paperSize.width, height: printInfo.paperSize.height
)
let textView = NSTextView( let textView = NSTextView(
frame: NSRect(x: 0.0, y: 0.0, width: pageSize.width, height: pageSize.height)) frame: NSRect(x: 0.0, y: 0.0, width: pageSize.width, height: pageSize.height))
@ -139,7 +140,7 @@ class Document: NSDocument {
printOperation.runModal( printOperation.runModal(
for: windowControllers[0].window!, for: windowControllers[0].window!,
delegate: self, delegate: self,
didRun: #selector(printOperationDidRun(_:success:contextInfo:)), contextInfo: nil) didRun: #selector(printOperationDidRun(_:success:contextInfo:)), contextInfo: nil
)
} }
} }

View File

@ -29,15 +29,19 @@ extension String {
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
do { do {
let regex = try NSRegularExpression( let regex = try NSRegularExpression(
pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]) pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]
)
let range = NSRange(startIndex..., in: self) let range = NSRange(startIndex..., in: self)
self = regex.stringByReplacingMatches( self = regex.stringByReplacingMatches(
in: self, options: [], range: range, withTemplate: replaceWith) in: self, options: [], range: range, withTemplate: replaceWith
)
} catch { return } } catch { return }
} }
mutating func selfReplace(_ strOf: String, _ strWith: String = "") { mutating func selfReplace(_ strOf: String, _ strWith: String = "") {
self = self.replacingOccurrences(of: strOf, with: strWith) self = replacingOccurrences(of: strOf, with: strWith)
} }
mutating func formatConsolidate(cnvHYPYtoBPMF: Bool) { mutating func formatConsolidate(cnvHYPYtoBPMF: Bool) {
// Step 1: Consolidating formats per line. // Step 1: Consolidating formats per line.
var strProcessed = self var strProcessed = self

View File

@ -25,7 +25,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
class ViewController: NSViewController, NSTextViewDelegate { class ViewController: NSViewController, NSTextViewDelegate {
/// - Tag: setRepresentedObjectExample /// - Tag: setRepresentedObjectExample
override var representedObject: Any? { override var representedObject: Any? {
didSet { didSet {
@ -54,12 +53,11 @@ class ViewController: NSViewController, NSTextViewDelegate {
// MARK: - NSTextViewDelegate // MARK: - NSTextViewDelegate
func textDidBeginEditing(_ notification: Notification) { func textDidBeginEditing(_: Notification) {
document?.objectDidBeginEditing(self) document?.objectDidBeginEditing(self)
} }
func textDidEndEditing(_ notification: Notification) { func textDidEndEditing(_: Notification) {
document?.objectDidEndEditing(self) document?.objectDidEndEditing(self)
} }
} }

View File

@ -25,7 +25,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
class WindowController: NSWindowController, NSWindowDelegate { class WindowController: NSWindowController, NSWindowDelegate {
override func windowDidLoad() { override func windowDidLoad() {
super.windowDidLoad() super.windowDidLoad()
} }
@ -37,5 +36,4 @@ class WindowController: NSWindowController, NSWindowDelegate {
*/ */
shouldCascadeWindows = true shouldCascadeWindows = true
} }
} }

View File

@ -27,8 +27,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa import Cocoa
@objc(AboutWindow) class ctlAboutWindow: NSWindowController { @objc(AboutWindow) class ctlAboutWindow: NSWindowController {
@IBOutlet weak var appVersionLabel: NSTextField! @IBOutlet var appVersionLabel: NSTextField!
@IBOutlet weak var appCopyrightLabel: NSTextField! @IBOutlet var appCopyrightLabel: NSTextField!
@IBOutlet var appEULAContent: NSTextView! @IBOutlet var appEULAContent: NSTextView!
override func windowDidLoad() { override func windowDidLoad() {
@ -53,10 +53,11 @@ import Cocoa
appEULAContent.string = eulaContent appEULAContent.string = eulaContent
} }
appVersionLabel.stringValue = String( appVersionLabel.stringValue = String(
format: "%@ Build %@", versionString, installingVersion) format: "%@ Build %@", versionString, installingVersion
)
} }
@IBAction func btnWiki(_ sender: NSButton) { @IBAction func btnWiki(_: NSButton) {
if let url = URL(string: "https://gitee.com/vchewing/vChewing-macOS/wikis") { if let url = URL(string: "https://gitee.com/vchewing/vChewing-macOS/wikis") {
NSWorkspace.shared.open(url) NSWorkspace.shared.open(url)
} }