Repo // Introducing IMKHelper.
This commit is contained in:
parent
e85e3382fe
commit
790463d640
|
@ -7,23 +7,8 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
enum AppleKeyboardConverter {
|
||||
static let arrDynamicBasicKeyLayout: [String] = [
|
||||
"com.apple.keylayout.ZhuyinBopomofo",
|
||||
"com.apple.keylayout.ZhuyinEten",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingdachen",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingmitac",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingibm",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingseigyou",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingeten",
|
||||
"org.unknown.keylayout.vChewingDachen",
|
||||
"org.unknown.keylayout.vChewingFakeSeigyou",
|
||||
"org.unknown.keylayout.vChewingETen",
|
||||
"org.unknown.keylayout.vChewingIBM",
|
||||
"org.unknown.keylayout.vChewingMiTAC",
|
||||
]
|
||||
|
||||
static var isDynamicBasicKeyboardLayoutEnabled: Bool {
|
||||
AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout)
|
||||
IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout)
|
||||
}
|
||||
|
||||
static func cnvStringApple2ABC(_ strProcessed: String) -> String {
|
||||
|
|
|
@ -134,7 +134,8 @@ extension IMEState {
|
|||
return result
|
||||
}
|
||||
|
||||
public static func ofCandidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int) -> IMEState
|
||||
public static func ofCandidates(candidates: [(String, String)], displayTextSegments: [String], cursor: Int)
|
||||
-> IMEState
|
||||
{
|
||||
var result = IMEState.ofNotEmpty(displayTextSegments: displayTextSegments, cursor: cursor)
|
||||
result.type = .ofCandidates
|
||||
|
|
|
@ -13,7 +13,7 @@ import InputMethodKit
|
|||
public enum vChewing {}
|
||||
|
||||
// The type of input modes.
|
||||
public enum InputMode: String {
|
||||
public enum InputMode: String, CaseIterable {
|
||||
case imeModeCHS = "org.atelierInmu.inputmethod.vChewing.IMECHS"
|
||||
case imeModeCHT = "org.atelierInmu.inputmethod.vChewing.IMECHT"
|
||||
case imeModeNULL = ""
|
||||
|
@ -223,152 +223,6 @@ public enum IME {
|
|||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// MARK: - Registering the input method.
|
||||
|
||||
@discardableResult static func registerInputMethod() -> Int32 {
|
||||
guard let bundleID = Bundle.main.bundleIdentifier else {
|
||||
return -1
|
||||
}
|
||||
let bundleUrl = Bundle.main.bundleURL
|
||||
var maybeInputSource = InputSourceHelper.inputSource(for: bundleID)
|
||||
|
||||
if maybeInputSource == nil {
|
||||
NSLog("Registering input source \(bundleID) at \(bundleUrl.absoluteString)")
|
||||
// then register
|
||||
let status = InputSourceHelper.registerTnputSource(at: bundleUrl)
|
||||
|
||||
if !status {
|
||||
NSLog(
|
||||
"Fatal error: Cannot register input source \(bundleID) at \(bundleUrl.absoluteString)."
|
||||
)
|
||||
return -1
|
||||
}
|
||||
|
||||
maybeInputSource = InputSourceHelper.inputSource(for: bundleID)
|
||||
}
|
||||
|
||||
guard let inputSource = maybeInputSource else {
|
||||
NSLog("Fatal error: Cannot find input source \(bundleID) after registration.")
|
||||
return -1
|
||||
}
|
||||
|
||||
if !InputSourceHelper.inputSourceEnabled(for: inputSource) {
|
||||
NSLog("Enabling input source \(bundleID) at \(bundleUrl.absoluteString).")
|
||||
let status = InputSourceHelper.enable(inputSource: inputSource)
|
||||
if !status {
|
||||
NSLog("Fatal error: Cannot enable input source \(bundleID).")
|
||||
return -1
|
||||
}
|
||||
if !InputSourceHelper.inputSourceEnabled(for: inputSource) {
|
||||
NSLog("Fatal error: Cannot enable input source \(bundleID).")
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
if CommandLine.arguments.count > 2, CommandLine.arguments[2] == "--all" {
|
||||
let enabled = InputSourceHelper.enableAllInputMode(for: bundleID)
|
||||
NSLog(
|
||||
enabled
|
||||
? "All input sources enabled for \(bundleID)"
|
||||
: "Cannot enable all input sources for \(bundleID), but this is ignored")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// MARK: - 準備枚舉系統內所有的 ASCII 鍵盤佈局
|
||||
|
||||
struct CarbonKeyboardLayout {
|
||||
var strName: String = ""
|
||||
var strValue: String = ""
|
||||
}
|
||||
|
||||
static let arrWhitelistedKeyLayoutsASCII: [String] = [
|
||||
"com.apple.keylayout.ABC",
|
||||
"com.apple.keylayout.ABC-AZERTY",
|
||||
"com.apple.keylayout.ABC-QWERTZ",
|
||||
"com.apple.keylayout.British",
|
||||
"com.apple.keylayout.Colemak",
|
||||
"com.apple.keylayout.Dvorak",
|
||||
"com.apple.keylayout.Dvorak-Left",
|
||||
"com.apple.keylayout.DVORAK-QWERTYCMD",
|
||||
"com.apple.keylayout.Dvorak-Right",
|
||||
]
|
||||
static var arrEnumerateSystemKeyboardLayouts: [IME.CarbonKeyboardLayout] {
|
||||
// 提前塞入 macOS 內建的兩款動態鍵盤佈局
|
||||
var arrKeyLayouts: [IME.CarbonKeyboardLayout] = []
|
||||
arrKeyLayouts += [
|
||||
IME.CarbonKeyboardLayout(
|
||||
strName: NSLocalizedString("Apple Chewing - Dachen", comment: ""),
|
||||
strValue: "com.apple.keylayout.ZhuyinBopomofo"
|
||||
),
|
||||
IME.CarbonKeyboardLayout(
|
||||
strName: NSLocalizedString("Apple Chewing - Eten Traditional", comment: ""),
|
||||
strValue: "com.apple.keylayout.ZhuyinEten"
|
||||
),
|
||||
]
|
||||
|
||||
// 準備枚舉系統內所有的 ASCII 鍵盤佈局
|
||||
var arrKeyLayoutsMACV: [IME.CarbonKeyboardLayout] = []
|
||||
var arrKeyLayoutsASCII: [IME.CarbonKeyboardLayout] = []
|
||||
let list = TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource]
|
||||
for source in list {
|
||||
if let ptrCategory = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) {
|
||||
let category = Unmanaged<CFString>.fromOpaque(ptrCategory).takeUnretainedValue()
|
||||
if category != kTISCategoryKeyboardInputSource {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if let ptrASCIICapable = TISGetInputSourceProperty(
|
||||
source, kTISPropertyInputSourceIsASCIICapable
|
||||
) {
|
||||
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(ptrASCIICapable)
|
||||
.takeUnretainedValue()
|
||||
if asciiCapable != kCFBooleanTrue {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if let ptrSourceType = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) {
|
||||
let sourceType = Unmanaged<CFString>.fromOpaque(ptrSourceType).takeUnretainedValue()
|
||||
if sourceType != kTISTypeKeyboardLayout {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
guard let ptrSourceID = TISGetInputSourceProperty(source, kTISPropertyInputSourceID),
|
||||
let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName)
|
||||
else {
|
||||
continue
|
||||
}
|
||||
|
||||
let sourceID = String(Unmanaged<CFString>.fromOpaque(ptrSourceID).takeUnretainedValue())
|
||||
let localizedName = String(
|
||||
Unmanaged<CFString>.fromOpaque(localizedNamePtr).takeUnretainedValue())
|
||||
|
||||
if sourceID.contains("vChewing") {
|
||||
arrKeyLayoutsMACV += [
|
||||
IME.CarbonKeyboardLayout(strName: localizedName, strValue: sourceID)
|
||||
]
|
||||
}
|
||||
|
||||
if IME.arrWhitelistedKeyLayoutsASCII.contains(sourceID) {
|
||||
arrKeyLayoutsASCII += [
|
||||
IME.CarbonKeyboardLayout(strName: localizedName, strValue: sourceID)
|
||||
]
|
||||
}
|
||||
}
|
||||
arrKeyLayouts += arrKeyLayoutsMACV
|
||||
arrKeyLayouts += arrKeyLayoutsASCII
|
||||
return arrKeyLayouts
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Root Extensions
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||
// ====================
|
||||
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||
// ... with NTL restriction stating that:
|
||||
// No trademark license is granted to use the trade names, trademarks, service
|
||||
// marks, or product names of Contributor, except as required to fulfill notice
|
||||
// requirements defined in MIT License.
|
||||
|
||||
import Foundation
|
||||
import InputMethodKit
|
||||
|
||||
// MARK: - IMKHelper by The vChewing Project (MIT License).
|
||||
|
||||
enum IMKHelper {
|
||||
/// 威注音有專門統計過,實際上會有差異的英數鍵盤佈局只有這幾種。
|
||||
/// 精簡成這種清單的話,不但節省 SwiftUI 的繪製壓力,也方便使用者做選擇。
|
||||
static let arrWhitelistedKeyLayoutsASCII: [String] = [
|
||||
"com.apple.keylayout.ABC",
|
||||
"com.apple.keylayout.ABC-AZERTY",
|
||||
"com.apple.keylayout.ABC-QWERTZ",
|
||||
"com.apple.keylayout.British",
|
||||
"com.apple.keylayout.Colemak",
|
||||
"com.apple.keylayout.Dvorak",
|
||||
"com.apple.keylayout.Dvorak-Left",
|
||||
"com.apple.keylayout.DVORAK-QWERTYCMD",
|
||||
"com.apple.keylayout.Dvorak-Right",
|
||||
]
|
||||
|
||||
static let arrDynamicBasicKeyLayouts: [String] = [
|
||||
"com.apple.keylayout.ZhuyinBopomofo",
|
||||
"com.apple.keylayout.ZhuyinEten",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingdachen",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingmitac",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingibm",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingseigyou",
|
||||
"org.atelierInmu.vChewing.keyLayouts.vchewingeten",
|
||||
"org.unknown.keylayout.vChewingDachen",
|
||||
"org.unknown.keylayout.vChewingFakeSeigyou",
|
||||
"org.unknown.keylayout.vChewingETen",
|
||||
"org.unknown.keylayout.vChewingIBM",
|
||||
"org.unknown.keylayout.vChewingMiTAC",
|
||||
]
|
||||
|
||||
static var allowedBasicLayoutsAsTISInputSources: [TISInputSource?] {
|
||||
// 為了保證清單順序,先弄兩個容器。
|
||||
var containerA: [TISInputSource?] = []
|
||||
var containerB: [TISInputSource?] = []
|
||||
var containerC: [TISInputSource?] = []
|
||||
|
||||
let rawDictionary = TISInputSource.rawTISInputSources(onlyASCII: false)
|
||||
|
||||
IMKHelper.arrWhitelistedKeyLayoutsASCII.forEach {
|
||||
if let neta = rawDictionary[$0], !arrDynamicBasicKeyLayouts.contains(neta.identifier) {
|
||||
containerC.append(neta)
|
||||
}
|
||||
}
|
||||
|
||||
IMKHelper.arrDynamicBasicKeyLayouts.forEach {
|
||||
if let neta = rawDictionary[$0] {
|
||||
if neta.identifier.contains("com.apple") {
|
||||
containerA.append(neta)
|
||||
} else {
|
||||
containerB.append(neta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 這裡的 nil 是用來讓選單插入分隔符用的。
|
||||
if !containerA.isEmpty { containerA.append(nil) }
|
||||
if !containerB.isEmpty { containerB.append(nil) }
|
||||
|
||||
return containerA + containerB + containerC
|
||||
}
|
||||
|
||||
struct CarbonKeyboardLayout {
|
||||
var strName: String = ""
|
||||
var strValue: String = ""
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 與輸入法的具體的安裝過程有關的命令
|
||||
|
||||
extension IMKHelper {
|
||||
@discardableResult static func registerInputMethod() -> Int32 {
|
||||
TISInputSource.registerInputMethod() ? 0 : -1
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TISInputSource Extension by The vChewing Project (MIT License).
|
||||
|
||||
extension TISInputSource {
|
||||
public static var allRegisteredInstancesOfThisInputMethod: [TISInputSource] {
|
||||
TISInputSource.modes.compactMap { TISInputSource.generate(from: $0) }
|
||||
}
|
||||
|
||||
public static var modes: [String] {
|
||||
guard let components = Bundle.main.infoDictionary?["ComponentInputModeDict"] as? [String: Any],
|
||||
let tsInputModeListKey = components["tsInputModeListKey"] as? [String: Any]
|
||||
else {
|
||||
return []
|
||||
}
|
||||
return tsInputModeListKey.keys.map { $0 }
|
||||
}
|
||||
|
||||
@discardableResult public static func registerInputMethod() -> Bool {
|
||||
let instances = TISInputSource.allRegisteredInstancesOfThisInputMethod
|
||||
if instances.isEmpty {
|
||||
// 有實例尚未登記。執行登記手續。
|
||||
NSLog("Registering input source.")
|
||||
if !TISInputSource.registerInputSource() {
|
||||
NSLog("Input source registration failed.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
var succeeded = true
|
||||
instances.forEach {
|
||||
NSLog("Enabling input source: \($0.identifier)")
|
||||
if !$0.activate() {
|
||||
NSLog("Failed from enabling input source: \($0.identifier)")
|
||||
succeeded = false
|
||||
}
|
||||
}
|
||||
return succeeded
|
||||
}
|
||||
|
||||
@discardableResult public static func registerInputSource() -> Bool {
|
||||
TISRegisterInputSource(Bundle.main.bundleURL as CFURL) == noErr
|
||||
}
|
||||
|
||||
@discardableResult public func activate() -> Bool {
|
||||
TISEnableInputSource(self) == noErr
|
||||
}
|
||||
|
||||
@discardableResult public func select() -> Bool {
|
||||
if !isSelectable {
|
||||
NSLog("Non-selectable: \(identifier)")
|
||||
return false
|
||||
}
|
||||
if TISSelectInputSource(self) != noErr {
|
||||
NSLog("Failed from switching to \(identifier)")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@discardableResult public func deactivate() -> Bool {
|
||||
TISDisableInputSource(self) == noErr
|
||||
}
|
||||
|
||||
public var isActivated: Bool {
|
||||
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsEnabled), to: CFBoolean.self)
|
||||
== kCFBooleanTrue
|
||||
}
|
||||
|
||||
public var isSelectable: Bool {
|
||||
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsSelectCapable), to: CFBoolean.self)
|
||||
== kCFBooleanTrue
|
||||
}
|
||||
|
||||
public static func generate(from identifier: String) -> TISInputSource? {
|
||||
TISInputSource.rawTISInputSources(onlyASCII: false)[identifier] ?? nil
|
||||
}
|
||||
|
||||
public var inputModeID: String {
|
||||
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputModeID), to: NSString.self) as String
|
||||
}
|
||||
|
||||
public var vChewingLocalizedName: String {
|
||||
switch identifier {
|
||||
case "com.apple.keylayout.ZhuyinBopomofo":
|
||||
return NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "")
|
||||
case "com.apple.keylayout.ZhuyinEten":
|
||||
return NSLocalizedString("Apple Zhuyin Eten (Traditional)", comment: "")
|
||||
default: return localizedName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TISInputSource Extension by Mizuno Hiroki (a.k.a. "Mzp") (MIT License)
|
||||
|
||||
// Ref: Original source codes are written in Swift 4 from Mzp's InputMethodKit textbook.
|
||||
// Note: Slightly modified by vChewing Project: Using Dictionaries when necessary.
|
||||
|
||||
extension TISInputSource {
|
||||
public var localizedName: String {
|
||||
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyLocalizedName), to: NSString.self) as String
|
||||
}
|
||||
|
||||
public var identifier: String {
|
||||
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceID), to: NSString.self) as String
|
||||
}
|
||||
|
||||
public var scriptCode: Int {
|
||||
let r = TISGetInputSourceProperty(self, "TSMInputSourcePropertyScriptCode" as CFString)
|
||||
return unsafeBitCast(r, to: NSString.self).integerValue
|
||||
}
|
||||
|
||||
public static func rawTISInputSources(onlyASCII: Bool = false) -> [String: TISInputSource] {
|
||||
// 為了指定檢索條件,先構築 CFDictionary 辭典。
|
||||
// 第二項代指辭典容量。
|
||||
let conditions = CFDictionaryCreateMutable(nil, 2, nil, nil)
|
||||
if onlyASCII {
|
||||
// 第一條件:僅接收靜態鍵盤佈局結果。
|
||||
CFDictionaryAddValue(
|
||||
conditions, unsafeBitCast(kTISPropertyInputSourceType, to: UnsafeRawPointer.self),
|
||||
unsafeBitCast(kTISTypeKeyboardLayout, to: UnsafeRawPointer.self)
|
||||
)
|
||||
// 第二條件:只能輸入 ASCII 內容。
|
||||
CFDictionaryAddValue(
|
||||
conditions, unsafeBitCast(kTISPropertyInputSourceIsASCIICapable, to: UnsafeRawPointer.self),
|
||||
unsafeBitCast(kCFBooleanTrue, to: UnsafeRawPointer.self)
|
||||
)
|
||||
}
|
||||
// 返回鍵盤配列清單。
|
||||
var result = TISCreateInputSourceList(conditions, true).takeRetainedValue() as? [TISInputSource] ?? .init()
|
||||
if onlyASCII {
|
||||
result = result.filter { $0.scriptCode == 0 }
|
||||
}
|
||||
var resultDictionary: [String: TISInputSource] = [:]
|
||||
result.forEach {
|
||||
resultDictionary[$0.inputModeID] = $0
|
||||
resultDictionary[$0.identifier] = $0
|
||||
}
|
||||
return resultDictionary
|
||||
}
|
||||
}
|
|
@ -93,12 +93,12 @@ struct suiPrefPaneKeyboard: View {
|
|||
mgrPrefs.mandarinParser = value
|
||||
switch value {
|
||||
case 0:
|
||||
if !AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) {
|
||||
if !IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout) {
|
||||
mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ZhuyinBopomofo"
|
||||
selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout
|
||||
}
|
||||
default:
|
||||
if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(mgrPrefs.basicKeyboardLayout) {
|
||||
if IMKHelper.arrDynamicBasicKeyLayouts.contains(mgrPrefs.basicKeyboardLayout) {
|
||||
mgrPrefs.basicKeyboardLayout = "com.apple.keylayout.ABC"
|
||||
selBasicKeyboardLayout = mgrPrefs.basicKeyboardLayout
|
||||
}
|
||||
|
@ -170,15 +170,19 @@ struct suiPrefPaneKeyboard: View {
|
|||
selection: $selBasicKeyboardLayout.onChange {
|
||||
let value = selBasicKeyboardLayout
|
||||
mgrPrefs.basicKeyboardLayout = value
|
||||
if AppleKeyboardConverter.arrDynamicBasicKeyLayout.contains(value) {
|
||||
if IMKHelper.arrDynamicBasicKeyLayouts.contains(value) {
|
||||
mgrPrefs.mandarinParser = 0
|
||||
selMandarinParser = mgrPrefs.mandarinParser
|
||||
}
|
||||
}
|
||||
) {
|
||||
ForEach(0...(IME.arrEnumerateSystemKeyboardLayouts.count - 1), id: \.self) { id in
|
||||
Text(IME.arrEnumerateSystemKeyboardLayouts[id].strName).tag(
|
||||
IME.arrEnumerateSystemKeyboardLayouts[id].strValue)
|
||||
ForEach(0...(IMKHelper.allowedBasicLayoutsAsTISInputSources.count - 1), id: \.self) { id in
|
||||
let theEntry = IMKHelper.allowedBasicLayoutsAsTISInputSources[id]
|
||||
if let theEntry = theEntry {
|
||||
Text(theEntry.vChewingLocalizedName).tag(theEntry.identifier)
|
||||
} else {
|
||||
Divider()
|
||||
}
|
||||
}.id(UUID())
|
||||
}
|
||||
.labelsHidden()
|
||||
|
|
|
@ -23,7 +23,7 @@ switch max(CommandLine.arguments.count - 1, 0) {
|
|||
switch CommandLine.arguments[1] {
|
||||
case "install":
|
||||
if CommandLine.arguments[1] == "install" {
|
||||
let exitCode = IME.registerInputMethod()
|
||||
let exitCode = IMKHelper.registerInputMethod()
|
||||
exit(exitCode)
|
||||
}
|
||||
case "uninstall":
|
||||
|
|
|
@ -93,88 +93,24 @@ class ctlPrefWindow: NSWindowController {
|
|||
currentLanguageSelectItem = chosenLanguageItem ?? autoMUISelectItem
|
||||
uiLanguageButton.select(currentLanguageSelectItem)
|
||||
|
||||
let list = TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource]
|
||||
var usKeyboardLayoutItem: NSMenuItem?
|
||||
var chosenBaseKeyboardLayoutItem: NSMenuItem?
|
||||
|
||||
basicKeyboardLayoutButton.menu?.removeAllItems()
|
||||
|
||||
let itmAppleZhuyinBopomofo = NSMenuItem()
|
||||
itmAppleZhuyinBopomofo.title = NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "")
|
||||
itmAppleZhuyinBopomofo.representedObject = String(
|
||||
"com.apple.keylayout.ZhuyinBopomofo")
|
||||
basicKeyboardLayoutButton.menu?.addItem(itmAppleZhuyinBopomofo)
|
||||
|
||||
let itmAppleZhuyinEten = NSMenuItem()
|
||||
itmAppleZhuyinEten.title = NSLocalizedString("Apple Zhuyin Eten (Traditional)", comment: "")
|
||||
itmAppleZhuyinEten.representedObject = String("com.apple.keylayout.ZhuyinEten")
|
||||
basicKeyboardLayoutButton.menu?.addItem(itmAppleZhuyinEten)
|
||||
|
||||
let basicKeyboardLayoutID = mgrPrefs.basicKeyboardLayout
|
||||
|
||||
for source in list {
|
||||
if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) {
|
||||
let category = Unmanaged<CFString>.fromOpaque(categoryPtr).takeUnretainedValue()
|
||||
if category != kTISCategoryKeyboardInputSource {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
for source in IMKHelper.allowedBasicLayoutsAsTISInputSources {
|
||||
guard let source = source else {
|
||||
basicKeyboardLayoutButton.menu?.addItem(NSMenuItem.separator())
|
||||
continue
|
||||
}
|
||||
|
||||
if let asciiCapablePtr = TISGetInputSourceProperty(
|
||||
source, kTISPropertyInputSourceIsASCIICapable
|
||||
) {
|
||||
let asciiCapable = Unmanaged<CFBoolean>.fromOpaque(asciiCapablePtr)
|
||||
.takeUnretainedValue()
|
||||
if asciiCapable != kCFBooleanTrue {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if let sourceTypePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) {
|
||||
let sourceType = Unmanaged<CFString>.fromOpaque(sourceTypePtr).takeUnretainedValue()
|
||||
if sourceType != kTISTypeKeyboardLayout {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
guard let sourceIDPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceID),
|
||||
let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName)
|
||||
else {
|
||||
continue
|
||||
}
|
||||
|
||||
let sourceID = String(Unmanaged<CFString>.fromOpaque(sourceIDPtr).takeUnretainedValue())
|
||||
let localizedName = String(
|
||||
Unmanaged<CFString>.fromOpaque(localizedNamePtr).takeUnretainedValue())
|
||||
|
||||
let menuItem = NSMenuItem()
|
||||
menuItem.title = localizedName
|
||||
menuItem.representedObject = sourceID
|
||||
|
||||
if sourceID == "com.apple.keylayout.US" {
|
||||
usKeyboardLayoutItem = menuItem
|
||||
}
|
||||
if basicKeyboardLayoutID == sourceID {
|
||||
chosenBaseKeyboardLayoutItem = menuItem
|
||||
}
|
||||
if IME.arrWhitelistedKeyLayoutsASCII.contains(sourceID) || sourceID.contains("vChewing") {
|
||||
basicKeyboardLayoutButton.menu?.addItem(menuItem)
|
||||
}
|
||||
}
|
||||
|
||||
switch basicKeyboardLayoutID {
|
||||
case "com.apple.keylayout.ZhuyinBopomofo":
|
||||
chosenBaseKeyboardLayoutItem = itmAppleZhuyinBopomofo
|
||||
case "com.apple.keylayout.ZhuyinEten":
|
||||
chosenBaseKeyboardLayoutItem = itmAppleZhuyinEten
|
||||
default:
|
||||
break // nothing to do
|
||||
menuItem.title = source.vChewingLocalizedName
|
||||
menuItem.representedObject = source.identifier
|
||||
if source.identifier == "com.apple.keylayout.US" { usKeyboardLayoutItem = menuItem }
|
||||
if basicKeyboardLayoutID == source.identifier { chosenBaseKeyboardLayoutItem = menuItem }
|
||||
basicKeyboardLayoutButton.menu?.addItem(menuItem)
|
||||
}
|
||||
|
||||
basicKeyboardLayoutButton.select(chosenBaseKeyboardLayoutItem ?? usKeyboardLayoutItem)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; };
|
||||
5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; };
|
||||
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; };
|
||||
5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; };
|
||||
5B20430728BEE30900BFC6FD /* BookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B20430628BEE30900BFC6FD /* BookmarkManager.swift */; };
|
||||
5B21176C287539BB000443A9 /* ctlInputMethod_HandleStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */; };
|
||||
5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176D28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift */; };
|
||||
|
@ -32,7 +33,6 @@
|
|||
5B5948CE289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5948CD289CC04500C85824 /* LMInstantiator_DateTimeExtension.swift */; };
|
||||
5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.swift */; };
|
||||
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; };
|
||||
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; };
|
||||
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33527AE795800A19448 /* mgrPrefs.swift */; };
|
||||
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
|
||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; };
|
||||
|
@ -116,6 +116,7 @@
|
|||
5BEDB724283B4C250078EB25 /* data-symbols.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71E283B4AEA0078EB25 /* data-symbols.plist */; };
|
||||
5BEDB725283B4C250078EB25 /* data-chs.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71C283B4AEA0078EB25 /* data-chs.plist */; };
|
||||
5BF0B84C28C070B000795FC6 /* NSEventExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF0B84B28C070B000795FC6 /* NSEventExtension.swift */; };
|
||||
5BF13B9428C627BB00E99EC1 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; };
|
||||
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF56F9728C39A2700DD6839 /* IMEState.swift */; };
|
||||
5BF56F9A28C39D1800DD6839 /* IMEStateData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF56F9928C39D1800DD6839 /* IMEStateData.swift */; };
|
||||
5BF9DA2728840E6200DBD48E /* template-usersymbolphrases.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2228840E6200DBD48E /* template-usersymbolphrases.txt */; };
|
||||
|
@ -213,6 +214,7 @@
|
|||
5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKHelper.swift; sourceTree = "<group>"; };
|
||||
5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; };
|
||||
5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
|
||||
5B18BA7127C7BD8B0056EB19 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
|
@ -524,7 +526,7 @@
|
|||
children = (
|
||||
5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */,
|
||||
5B5E535127EF261400C6AA1E /* IME.swift */,
|
||||
5B62A33127AE792F00A19448 /* InputSourceHelper.swift */,
|
||||
5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */,
|
||||
5B62A33527AE795800A19448 /* mgrPrefs.swift */,
|
||||
);
|
||||
path = IMEModules;
|
||||
|
@ -884,6 +886,7 @@
|
|||
5BBBB77827AEDB330023B93A /* Resources */,
|
||||
D4F0BBE0279AF8B30071253C /* AppDelegate.swift */,
|
||||
D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */,
|
||||
5B62A33127AE792F00A19448 /* InputSourceHelper.swift */,
|
||||
6ACA41F215FC1D9000935EF6 /* Installer-Info.plist */,
|
||||
5BC0AACA27F58472002D33E9 /* pkgPostInstall.sh */,
|
||||
5BC0AAC927F58472002D33E9 /* pkgPreInstall.sh */,
|
||||
|
@ -1250,13 +1253,13 @@
|
|||
5B2170E3289FACAD00BE7304 /* 3_KeyValuePaired.swift in Sources */,
|
||||
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
|
||||
5B2170E6289FACAD00BE7304 /* 4_Span.swift in Sources */,
|
||||
5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */,
|
||||
5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */,
|
||||
5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */,
|
||||
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */,
|
||||
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
|
||||
5B8457A12871ADBE00C93B01 /* HotenkaCCBridge.swift in Sources */,
|
||||
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */,
|
||||
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */,
|
||||
5B5E535227EF261400C6AA1E /* IME.swift in Sources */,
|
||||
5B2170E0289FACAD00BE7304 /* 7_LangModel.swift in Sources */,
|
||||
5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */,
|
||||
|
@ -1286,6 +1289,7 @@
|
|||
D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */,
|
||||
D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */,
|
||||
5B62A35327AE89C400A19448 /* InputSourceHelper.swift in Sources */,
|
||||
5BF13B9428C627BB00E99EC1 /* IMKHelper.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue