PrefUI // Massive renovation - phase 2.
- This is the last workable commit prior to boosting the SwiftUI system requirements from macOS 10.15 Catalina to macOS 13 Ventura.
This commit is contained in:
parent
37104cb8a9
commit
f81f2d3b43
|
@ -16,6 +16,21 @@ public enum IMEApp {
|
|||
// MARK: - 獲取輸入法的版本以及建置編號
|
||||
|
||||
public static let appVersionLabel: String = {
|
||||
[appMainVersionLabel.joined(separator: " Build "), appSignedDateLabel].joined(separator: " - ")
|
||||
}()
|
||||
|
||||
public static let appMainVersionLabel: [String] = {
|
||||
guard
|
||||
let intBuild = Bundle.main.infoDictionary?[kCFBundleVersionKey as String] as? String,
|
||||
let strVer = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
else {
|
||||
return ["1.14.514", "19190810"]
|
||||
}
|
||||
|
||||
return [strVer, intBuild]
|
||||
}()
|
||||
|
||||
public static let appSignedDateLabel: String = {
|
||||
let maybeDateModified: Date? = {
|
||||
guard let executableURL = Bundle.main.executableURL,
|
||||
let infoDate = (try? executableURL.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate
|
||||
|
@ -25,32 +40,13 @@ public enum IMEApp {
|
|||
return infoDate
|
||||
}()
|
||||
|
||||
func dateStringTag(date givenDate: Date) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyyMMdd.HHmm"
|
||||
dateFormatter.timeZone = .init(secondsFromGMT: +28800) ?? .current
|
||||
let strDate = dateFormatter.string(from: givenDate)
|
||||
return strDate
|
||||
}
|
||||
|
||||
guard
|
||||
let intBuild = Bundle.main.infoDictionary?[kCFBundleVersionKey as String] as? String,
|
||||
let strVer = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
else {
|
||||
return "1.14.514 - 19190810"
|
||||
}
|
||||
|
||||
var theResults = ["\(strVer) Build \(intBuild)"]
|
||||
|
||||
if let theDate = Bundle.main.getCodeSignedDate() {
|
||||
theResults.append(dateStringTag(date: theDate))
|
||||
return theDate.stringTag
|
||||
} else if let theDate = maybeDateModified {
|
||||
theResults.append("\(dateStringTag(date: theDate)) Unsigned")
|
||||
return "\(theDate.stringTag) Unsigned"
|
||||
} else {
|
||||
theResults.append("Unsigned")
|
||||
return "Unsigned"
|
||||
}
|
||||
|
||||
return theResults.joined(separator: " - ")
|
||||
}()
|
||||
|
||||
// MARK: - 輸入法的當前的簡繁體中文模式
|
||||
|
@ -75,3 +71,13 @@ public enum IMEApp {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Date {
|
||||
var stringTag: String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyyMMdd.HHmm"
|
||||
dateFormatter.timeZone = .init(secondsFromGMT: +28800) ?? .current
|
||||
let strDate = dateFormatter.string(from: self)
|
||||
return strDate
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,12 +146,12 @@ public extension SessionCtl {
|
|||
/// Instructions for Apple Developer relations to reveal this bug:
|
||||
///
|
||||
/// 0) Please go to Step 1. Reason: IMK Candidate Window support has been removed in this repo.
|
||||
/// 1) Make sure the usage of ".languageIdentifier" is disabled in the Dev Zone of the vChewing SSPreferences.
|
||||
/// 1) Make sure the usage of ".languageIdentifier" is disabled in the Dev Zone of the vChewing Preferences.
|
||||
/// 2) Run "make update" in the project folder to download the latest git-submodule of dictionary file.
|
||||
/// 3) Compile the target "vChewingInstaller", run it. It will install the input method into
|
||||
/// "~/Library/Input Methods/" folder. Remember to ENABLE BOTH "vChewing-CHS"
|
||||
/// and "vChewing-CHT" input sources in System Preferences / Settings.
|
||||
/// 4) Type Zhuyin "ej3" (ㄍㄨˇ) (or "gu3" in Pinyin if you enabled Pinyin typing in vChewing SSPreferences.)
|
||||
/// 4) Type Zhuyin "ej3" (ㄍㄨˇ) (or "gu3" in Pinyin if you enabled Pinyin typing in vChewing Preferences.)
|
||||
/// using both "vChewing-CHS" and "vChewing-CHT", and check the candidate window by pressing SPACE key.
|
||||
/// 5) Do NOT enable either KangXi conversion mode nor JIS conversion mode. They are disabled by default.
|
||||
/// 6) Expecting the glyph differences of the candidate "骨" between PingFang SC and PingFang TC when rendering
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import AppKit
|
||||
import MainAssembly
|
||||
import NotifierUI
|
||||
import SSPreferences
|
||||
|
||||
private extension Bool {
|
||||
var state: NSControl.StateValue {
|
||||
|
@ -220,13 +219,9 @@ extension SessionCtl {
|
|||
|
||||
public extension SessionCtl {
|
||||
@objc override func showPreferences(_: Any? = nil) {
|
||||
osCheck: if #available(macOS 10.15, *) {
|
||||
osCheck: if #available(macOS 13, *) {
|
||||
switch NSEvent.keyModifierFlags {
|
||||
case .option: break osCheck
|
||||
// case .shift:
|
||||
// CtlPrefUIShared.shared.controller.show(preferencePane: PrefUITabs.tabGeneral.ssPaneIdentifier)
|
||||
// CtlPrefUIShared.shared.controller.window?.level = .statusBar
|
||||
// CtlPrefUIShared.shared.controller.window?.setPosition(vertical: .top, horizontal: .right, padding: 20)
|
||||
default: CtlPrefUI.show()
|
||||
}
|
||||
NSApp.popup()
|
||||
|
|
|
@ -18,35 +18,6 @@ private let kWindowTitleHeight: Double = 78
|
|||
|
||||
@available(macOS 10.15, *)
|
||||
class CtlPrefUI: NSWindowController, NSWindowDelegate {
|
||||
static var vwrGeneral: NSView = generateView(tab: .tabGeneral)
|
||||
static var vwrCandidates: NSView = generateView(tab: .tabCandidates)
|
||||
static var vwrBehavior: NSView = generateView(tab: .tabBehavior)
|
||||
static var vwrOutput: NSView = generateView(tab: .tabOutput)
|
||||
static var vwrDictionary: NSView = generateView(tab: .tabDictionary)
|
||||
static var vwrPhrases: NSView = generateView(tab: .tabPhrases)
|
||||
static var vwrCassette: NSView = generateView(tab: .tabCassette)
|
||||
static var vwrKeyboard: NSView = generateView(tab: .tabKeyboard)
|
||||
static var vwrDevZone: NSView = generateView(tab: .tabDevZone)
|
||||
|
||||
static func generateView(tab: PrefUITabs) -> NSView {
|
||||
var body: some View {
|
||||
Group {
|
||||
switch tab {
|
||||
case .tabGeneral: VwrPrefPaneGeneral()
|
||||
case .tabCandidates: VwrPrefPaneCandidates()
|
||||
case .tabBehavior: VwrPrefPaneBehavior()
|
||||
case .tabOutput: VwrPrefPaneOutput()
|
||||
case .tabDictionary: VwrPrefPaneDictionary()
|
||||
case .tabPhrases: VwrPrefPanePhrases()
|
||||
case .tabCassette: VwrPrefPaneCassette()
|
||||
case .tabKeyboard: VwrPrefPaneKeyboard()
|
||||
case .tabDevZone: VwrPrefPaneDevZone()
|
||||
}
|
||||
}.fixedSize()
|
||||
}
|
||||
return NSHostingView(rootView: body.edgesIgnoringSafeArea(.top))
|
||||
}
|
||||
|
||||
public static var shared: CtlPrefUI?
|
||||
|
||||
static func show() {
|
||||
|
@ -76,48 +47,36 @@ class CtlPrefUI: NSWindowController, NSWindowDelegate {
|
|||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
window?.setPosition(vertical: .top, horizontal: .right, padding: 20)
|
||||
|
||||
var preferencesTitleName = NSLocalizedString("vChewing Preferences…", comment: "")
|
||||
preferencesTitleName.removeLast()
|
||||
|
||||
if #available(macOS 13, *) {
|
||||
window?.contentView = NSHostingView(
|
||||
rootView: VwrSettingsUI()
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
.ignoresSafeArea()
|
||||
)
|
||||
}
|
||||
let toolbar = NSToolbar(identifier: "preference toolbar")
|
||||
toolbar.allowsUserCustomization = false
|
||||
toolbar.autosavesConfiguration = false
|
||||
toolbar.sizeMode = .default
|
||||
toolbar.delegate = self
|
||||
toolbar.selectedItemIdentifier = PrefUITabs.tabGeneral.toolbarIdentifier
|
||||
toolbar.selectedItemIdentifier = nil
|
||||
toolbar.showsBaselineSeparator = true
|
||||
if #available(macOS 11.0, *) {
|
||||
window?.toolbarStyle = .preference
|
||||
if #available(macOS 11, *) {
|
||||
window?.toolbarStyle = .unifiedCompact
|
||||
}
|
||||
window?.toolbar = toolbar
|
||||
window?.title = "\(preferencesTitleName) (\(IMEApp.appVersionLabel))"
|
||||
window?.titlebarAppearsTransparent = false
|
||||
use(view: Self.vwrGeneral, animate: false)
|
||||
var preferencesTitleName = NSLocalizedString("vChewing Preferences…", comment: "")
|
||||
preferencesTitleName.removeLast()
|
||||
window?.title = preferencesTitleName
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSToolbarDelegate Methods
|
||||
// MARK: - NSToolbarDelegate.
|
||||
|
||||
@available(macOS 10.15, *)
|
||||
extension CtlPrefUI: NSToolbarDelegate {
|
||||
func use(view newView: NSView, animate: Bool = true) {
|
||||
// 強制重置語彙編輯器畫面。
|
||||
if window?.contentView == Self.vwrPhrases || newView == Self.vwrPhrases {
|
||||
Self.vwrPhrases = Self.generateView(tab: .tabPhrases)
|
||||
}
|
||||
guard let window = window, let existingContentView = window.contentView else { return }
|
||||
let temporaryViewOld = NSView(frame: existingContentView.frame)
|
||||
window.contentView = temporaryViewOld
|
||||
var newWindowRect = NSRect(origin: window.frame.origin, size: newView.fittingSize)
|
||||
newWindowRect.size.height += kWindowTitleHeight
|
||||
newWindowRect.origin.y = window.frame.maxY - newWindowRect.height
|
||||
window.setFrame(newWindowRect, display: true, animate: animate)
|
||||
window.contentView = newView
|
||||
}
|
||||
|
||||
var toolbarIdentifiers: [NSToolbarItem.Identifier] {
|
||||
PrefUITabs.allCases.map(\.toolbarIdentifier)
|
||||
[.init("Collapse or Expand Sidebar")]
|
||||
}
|
||||
|
||||
func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
|
@ -129,36 +88,21 @@ extension CtlPrefUI: NSToolbarDelegate {
|
|||
}
|
||||
|
||||
func toolbarSelectableItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
toolbarIdentifiers
|
||||
}
|
||||
|
||||
@objc func updateTab(_ target: NSToolbarItem) {
|
||||
guard let tab = PrefUITabs.fromInt(target.tag) else { return }
|
||||
switch tab {
|
||||
case .tabGeneral: use(view: Self.vwrGeneral)
|
||||
case .tabCandidates: use(view: Self.vwrCandidates)
|
||||
case .tabBehavior: use(view: Self.vwrBehavior)
|
||||
case .tabOutput: use(view: Self.vwrOutput)
|
||||
case .tabDictionary: use(view: Self.vwrDictionary)
|
||||
case .tabPhrases: use(view: Self.vwrPhrases)
|
||||
case .tabCassette: use(view: Self.vwrCassette)
|
||||
case .tabKeyboard: use(view: Self.vwrKeyboard)
|
||||
case .tabDevZone: use(view: Self.vwrDevZone)
|
||||
}
|
||||
window?.toolbar?.selectedItemIdentifier = tab.toolbarIdentifier
|
||||
[]
|
||||
}
|
||||
|
||||
func toolbar(
|
||||
_: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
|
||||
willBeInsertedIntoToolbar _: Bool
|
||||
) -> NSToolbarItem? {
|
||||
guard let tab = PrefUITabs(rawValue: itemIdentifier.rawValue) else { return nil }
|
||||
let item = NSToolbarItem(itemIdentifier: itemIdentifier)
|
||||
item.target = self
|
||||
item.image = tab.icon
|
||||
item.label = tab.i18nTitle
|
||||
item.tag = tab.cocoaTag
|
||||
item.action = #selector(updateTab(_:))
|
||||
if #available(macOS 11.0, *) {
|
||||
item.isNavigational = true
|
||||
}
|
||||
item.target = window?.firstResponder
|
||||
item.image = NSImage(named: "NSTouchBarSidebarTemplate") ?? .init()
|
||||
item.tag = 0
|
||||
item.action = #selector(NSSplitViewController.toggleSidebar(_:))
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,55 +7,31 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import MainAssembly
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
|
||||
@available(macOS 10.15, *)
|
||||
extension PrefUITabs {
|
||||
var ssPaneIdentifier: SSPreferences.Settings.PaneIdentifier { .init(rawValue: rawValue) }
|
||||
}
|
||||
|
||||
@available(macOS 10.15, *)
|
||||
struct VwrPrefPage: View {
|
||||
@State var tabType: PrefUITabs
|
||||
var body: some View {
|
||||
Group {
|
||||
switch tabType {
|
||||
case .tabGeneral: VwrPrefPaneGeneral()
|
||||
case .tabCandidates: VwrPrefPaneCandidates()
|
||||
case .tabBehavior: VwrPrefPaneBehavior()
|
||||
case .tabOutput: VwrPrefPaneOutput()
|
||||
case .tabDictionary: VwrPrefPaneDictionary()
|
||||
case .tabPhrases: VwrPrefPanePhrases()
|
||||
case .tabCassette: VwrPrefPaneCassette()
|
||||
case .tabKeyboard: VwrPrefPaneKeyboard()
|
||||
case .tabDevZone: VwrPrefPaneDevZone()
|
||||
}
|
||||
}.fixedSize()
|
||||
@ViewBuilder
|
||||
var suiView: some View {
|
||||
switch self {
|
||||
case .tabGeneral: VwrPrefPaneGeneral()
|
||||
case .tabCandidates: VwrPrefPaneCandidates()
|
||||
case .tabBehavior: VwrPrefPaneBehavior()
|
||||
case .tabOutput: VwrPrefPaneOutput()
|
||||
case .tabDictionary: VwrPrefPaneDictionary()
|
||||
case .tabPhrases: VwrPrefPanePhrases()
|
||||
case .tabCassette: VwrPrefPaneCassette()
|
||||
case .tabKeyboard: VwrPrefPaneKeyboard()
|
||||
case .tabDevZone: VwrPrefPaneDevZone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.15, *)
|
||||
class CtlPrefUIShared {
|
||||
var controller = PreferencesWindowController(
|
||||
panes: {
|
||||
var result = [PreferencePaneConvertible]()
|
||||
PrefUITabs.allCases.forEach { neta in
|
||||
let item: PreferencePaneConvertible = SSPreferences.Settings.Pane(
|
||||
identifier: SSPreferences.Settings.PaneIdentifier(rawValue: neta.rawValue),
|
||||
title: neta.i18nTitle, toolbarIcon: neta.icon,
|
||||
contentView: { VwrPrefPage(tabType: neta) }
|
||||
)
|
||||
result.append(item)
|
||||
}
|
||||
return result
|
||||
}(),
|
||||
style: .toolbarItems
|
||||
)
|
||||
|
||||
static var sharedWindow: NSWindow? {
|
||||
CtlPrefUI.shared?.window ?? CtlPrefUIShared.shared.controller.window
|
||||
CtlPrefUI.shared?.window
|
||||
}
|
||||
|
||||
static let shared = CtlPrefUIShared()
|
||||
|
@ -73,18 +49,6 @@ class CtlPrefUIShared {
|
|||
}()
|
||||
|
||||
static let contentMaxHeight: Double = 490
|
||||
static let contentWidth: Double = {
|
||||
switch PrefMgr.shared.appleLanguages[0] {
|
||||
case "ja":
|
||||
return 520
|
||||
default:
|
||||
if PrefMgr.shared.appleLanguages[0].contains("zh-Han") {
|
||||
return 500
|
||||
} else {
|
||||
return 580
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
static let formWidth: Double = {
|
||||
switch PrefMgr.shared.appleLanguages[0] {
|
||||
|
@ -102,9 +66,6 @@ class CtlPrefUIShared {
|
|||
static var isCJKInterface: Bool {
|
||||
PrefMgr.shared.appleLanguages[0].contains("zh-Han") || PrefMgr.shared.appleLanguages[0] == "ja"
|
||||
}
|
||||
|
||||
static var containerWidth: Double { contentWidth + 60 }
|
||||
static var maxDescriptionWidth: Double { contentWidth * 0.8 }
|
||||
}
|
||||
|
||||
@available(macOS 10.15, *)
|
||||
|
@ -121,6 +82,6 @@ public extension View {
|
|||
public extension View {
|
||||
func formStyled() -> some View {
|
||||
if #available(macOS 13, *) { return self.formStyle(.grouped) }
|
||||
return self.padding()
|
||||
return padding()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import MainAssembly
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -252,7 +251,7 @@ struct VwrPrefPaneBehavior: View {
|
|||
.settingsDescription()
|
||||
}
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import MainAssembly
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -178,7 +177,7 @@ struct VwrPrefPaneCandidates: View {
|
|||
// MARK: (header: Text("Experimental:"))
|
||||
|
||||
let imkEOSNoticeButton = Button("Where's IMK Candidate Window?") {
|
||||
if let window = CtlPrefUIShared.sharedWindow {
|
||||
if let window = CtlPrefUI.shared?.window {
|
||||
let title = "The End of Support for IMK Candidate Window"
|
||||
let explanation = "1) Only macOS has IMKCandidates. Since it relies on a dedicated ObjC Bridging Header to expose necessary internal APIs to work, it hinders vChewing from completely modularized for multi-platform support.\n\n2) IMKCandidates is buggy. It is not likely to be completely fixed by Apple, and its devs are not allowed to talk about it to non-Apple individuals. That's why we have had enough with IMKCandidates. It is likely the reason why Apple had never used IMKCandidates in their official InputMethodKit sample projects (as of August 2023)."
|
||||
window.callAlert(title: title.localized, text: explanation.localized)
|
||||
|
@ -191,7 +190,7 @@ struct VwrPrefPaneCandidates: View {
|
|||
isOn: $enableMouseScrollingForTDKCandidatesCocoa
|
||||
)
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
@ -226,7 +225,7 @@ private struct VwrPrefPaneCandidates_SelectionKeys: View {
|
|||
let keys = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased().deduplicated
|
||||
// Start Error Handling.
|
||||
if let errorResult = CandidateKey.validate(keys: keys) {
|
||||
if let window = CtlPrefUIShared.sharedWindow, !keys.isEmpty {
|
||||
if let window = CtlPrefUI.shared?.window, !keys.isEmpty {
|
||||
IMEApp.buzz()
|
||||
let alert = NSAlert(error: NSLocalizedString("Invalid Selection Keys.", comment: ""))
|
||||
alert.informativeText = errorResult
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import BookmarkManager
|
||||
import MainAssembly
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -86,7 +85,7 @@ struct VwrPrefPaneCassette: View {
|
|||
let bolPreviousPathValidity = LMMgr.checkCassettePathValidity(
|
||||
cassettePath.expandingTildeInPath)
|
||||
|
||||
if let window = CtlPrefUIShared.sharedWindow {
|
||||
if let window = CtlPrefUI.shared?.window {
|
||||
Self.dlgOpenFile.beginSheetModal(for: window) { result in
|
||||
if result == NSApplication.ModalResponse.OK {
|
||||
guard let url = Self.dlgOpenFile.url else { return }
|
||||
|
@ -129,7 +128,7 @@ struct VwrPrefPaneCassette: View {
|
|||
LocalizedStringKey("Enable cassette mode, suppressing phonabet input"),
|
||||
isOn: $cassetteEnabled.onChange {
|
||||
if cassetteEnabled, !LMMgr.checkCassettePathValidity(cassettePath) {
|
||||
if let window = CtlPrefUIShared.sharedWindow {
|
||||
if let window = CtlPrefUI.shared?.window {
|
||||
IMEApp.buzz()
|
||||
let alert = NSAlert(error: NSLocalizedString("Path invalid or file access error.", comment: ""))
|
||||
alert.informativeText = NSLocalizedString(
|
||||
|
@ -186,7 +185,7 @@ struct VwrPrefPaneCassette: View {
|
|||
.settingsDescription()
|
||||
}
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -57,7 +56,7 @@ struct VwrPrefPaneDevZone: View {
|
|||
.settingsDescription()
|
||||
}
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import BookmarkManager
|
|||
import CocoaExtension
|
||||
import MainAssembly
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -107,7 +106,7 @@ struct VwrPrefPaneDictionary: View {
|
|||
let bolPreviousFolderValidity = LMMgr.checkIfSpecifiedUserDataFolderValid(
|
||||
userDataFolderSpecified.expandingTildeInPath)
|
||||
|
||||
if let window = CtlPrefUIShared.sharedWindow {
|
||||
if let window = CtlPrefUI.shared?.window {
|
||||
Self.dlgOpenPath.beginSheetModal(for: window) { result in
|
||||
if result == NSApplication.ModalResponse.OK {
|
||||
guard let url = Self.dlgOpenPath.url else { return }
|
||||
|
@ -233,7 +232,7 @@ struct VwrPrefPaneDictionary: View {
|
|||
.settingsDescription()
|
||||
}
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import MainAssembly
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -144,7 +143,7 @@ struct VwrPrefPaneGeneral: View {
|
|||
}
|
||||
}
|
||||
alert.addButton(withTitle: NSLocalizedString("Leave it checked", comment: ""))
|
||||
if let window = CtlPrefUIShared.sharedWindow, !shouldNotFartInLieuOfBeep {
|
||||
if let window = CtlPrefUI.shared?.window, !shouldNotFartInLieuOfBeep {
|
||||
shouldNotFartInLieuOfBeep = true
|
||||
alert.beginSheetModal(for: window) { result in
|
||||
switch result {
|
||||
|
@ -176,7 +175,7 @@ struct VwrPrefPaneGeneral: View {
|
|||
isOn: $isDebugModeEnabled
|
||||
)
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import IMKUtils
|
||||
import MainAssembly
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -126,7 +125,7 @@ struct VwrPrefPaneKeyboard: View {
|
|||
Section(header: Text("Keyboard Shortcuts:")) {
|
||||
VwrPrefPaneKeyboard_KeyboardShortcuts()
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
// requirements defined in MIT License.
|
||||
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
import SwiftUIBackports
|
||||
|
@ -74,7 +73,7 @@ struct VwrPrefPaneOutput: View {
|
|||
.settingsDescription()
|
||||
}
|
||||
}
|
||||
}.formStyled().frame(width: CtlPrefUIShared.formWidth)
|
||||
}.formStyled().frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import MainAssembly
|
||||
import PhraseEditorUI
|
||||
import Shared
|
||||
import SSPreferences
|
||||
import SwiftExtension
|
||||
import SwiftUI
|
||||
|
||||
|
@ -19,7 +18,7 @@ struct VwrPrefPanePhrases: View {
|
|||
ScrollView {
|
||||
VStack {
|
||||
GroupBox {
|
||||
VwrPhraseEditorUI(delegate: LMMgr.shared, window: CtlPrefUIShared.sharedWindow)
|
||||
VwrPhraseEditorUI(delegate: LMMgr.shared, window: CtlPrefUI.shared?.window)
|
||||
.padding(4)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 440)
|
||||
|
@ -27,7 +26,7 @@ struct VwrPrefPanePhrases: View {
|
|||
}
|
||||
.padding(4)
|
||||
.padding()
|
||||
.frame(width: CtlPrefUIShared.formWidth)
|
||||
.frame(minWidth: CtlPrefUIShared.formWidth, maxWidth: ceil(CtlPrefUIShared.formWidth * 1.2))
|
||||
}
|
||||
.frame(maxHeight: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// (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 MainAssembly
|
||||
import SwiftUI
|
||||
|
||||
@available(macOS 13, *)
|
||||
public struct VwrSettingsUI: View {
|
||||
@State private var selectedTabID: PrefUITabs?
|
||||
public init() {}
|
||||
|
||||
/// 雖說從 macOS 13 開始的正確做法是使用 NavigationSplitView,
|
||||
/// 但由於這個畫面是藉由 NSHostingView 叫出來的、所以無法正確處理大型標題列。
|
||||
/// 目前還是暫時繼續用 NavigationView。
|
||||
public var body: some View {
|
||||
NavigationView {
|
||||
VStack {
|
||||
List(PrefUITabs.allCases, selection: $selectedTabID) { neta in
|
||||
if neta == PrefUITabs.tabGeneral {
|
||||
if let appIcon = NSImage(named: "IconSansMargin") {
|
||||
Image(nsImage: appIcon).resizable()
|
||||
.frame(width: 86, height: 86)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding([.top, .bottom], NSFont.systemFontSize / 2)
|
||||
}
|
||||
}
|
||||
NavigationLink(destination: neta.suiView) {
|
||||
Label {
|
||||
Text(verbatim: neta.i18nTitle)
|
||||
} icon: {
|
||||
Image(nsImage: neta.icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
VStack(alignment: .leading) {
|
||||
Text("v" + IMEApp.appMainVersionLabel.joined(separator: " Build "))
|
||||
Text(IMEApp.appSignedDateLabel)
|
||||
}.settingsDescription().padding()
|
||||
}
|
||||
.navigationTitle(PrefUITabs.tabGeneral.i18nTitle)
|
||||
.frame(minWidth: 128, idealWidth: 128, maxWidth: 128)
|
||||
PrefUITabs.tabGeneral.suiView
|
||||
}
|
||||
.frame(width: CtlPrefUIShared.formWidth + 140, height: CtlPrefUIShared.contentMaxHeight)
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@ import AppKit
|
|||
import MainAssembly
|
||||
import Shared
|
||||
|
||||
enum PrefUITabs: String, CaseIterable {
|
||||
enum PrefUITabs: String, CaseIterable, Identifiable, Hashable {
|
||||
var id: ObjectIdentifier { .init(rawValue as NSString) }
|
||||
case tabGeneral = "General"
|
||||
case tabCandidates = "Candidates"
|
||||
case tabBehavior = "Behavior"
|
||||
|
|
|
@ -124,7 +124,7 @@ class CtlPrefWindow: NSWindowController, NSWindowDelegate {
|
|||
toolbar.delegate = self
|
||||
toolbar.selectedItemIdentifier = PrefUITabs.tabGeneral.toolbarIdentifier
|
||||
toolbar.showsBaselineSeparator = true
|
||||
if #available(macOS 11.0, *) {
|
||||
if #available(macOS 13, *) {
|
||||
window?.toolbarStyle = .preference
|
||||
}
|
||||
window?.toolbar = toolbar
|
||||
|
|
21
Source/Resources/Images/Images.xcassets/IconSansMargin.imageset/Contents.json
vendored
Normal file
21
Source/Resources/Images/Images.xcassets/IconSansMargin.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "IconSansMargin.heic",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Source/Resources/Images/Images.xcassets/IconSansMargin.imageset/IconSansMargin.heic
vendored
Normal file
BIN
Source/Resources/Images/Images.xcassets/IconSansMargin.imageset/IconSansMargin.heic
vendored
Normal file
Binary file not shown.
Binary file not shown.
|
@ -24,6 +24,7 @@
|
|||
5B30BF282944867800BD87A9 /* CtlRevLookupWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B30BF272944867800BD87A9 /* CtlRevLookupWindow.swift */; };
|
||||
5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; };
|
||||
5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; };
|
||||
5B4C59AE2ABCB18700098626 /* VwrSettingsUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4C59AD2ABCB18700098626 /* VwrSettingsUI.swift */; };
|
||||
5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */ = {isa = PBXBuildFile; productRef = 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */; };
|
||||
5B62A33D27AE7CC100A19448 /* CtlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */; };
|
||||
5B65FB342A9518DA007EEFB0 /* MainAssembly in Frameworks */ = {isa = PBXBuildFile; productRef = 5B65FB332A9518DA007EEFB0 /* MainAssembly */; };
|
||||
|
@ -187,6 +188,7 @@
|
|||
5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = "<group>"; };
|
||||
5B312684287800DE001AA720 /* FAQ.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = FAQ.md; sourceTree = "<group>"; };
|
||||
5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; };
|
||||
5B4C59AD2ABCB18700098626 /* VwrSettingsUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VwrSettingsUI.swift; sourceTree = "<group>"; };
|
||||
5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = ShapsBenkau_SwiftUIBackports; path = Packages/ShapsBenkau_SwiftUIBackports; sourceTree = "<group>"; };
|
||||
5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = CtlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
|
@ -475,6 +477,7 @@
|
|||
5B2E009328FD1E8100E78D6E /* VwrPrefPaneCassette.swift */,
|
||||
5BA9FD0B27FEDB6B002DE248 /* VwrPrefPaneKeyboard.swift */,
|
||||
5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */,
|
||||
5B4C59AD2ABCB18700098626 /* VwrSettingsUI.swift */,
|
||||
);
|
||||
path = PrefUI;
|
||||
sourceTree = "<group>";
|
||||
|
@ -984,6 +987,7 @@
|
|||
5BA9FD1127FEDB6B002DE248 /* CtlPrefUIShared.swift in Sources */,
|
||||
5BA9FD1327FEDB6B002DE248 /* VwrPrefPaneDictionary.swift in Sources */,
|
||||
5BF7548929B2F04F00FA50DA /* CtlPrefUI.swift in Sources */,
|
||||
5B4C59AE2ABCB18700098626 /* VwrSettingsUI.swift in Sources */,
|
||||
5BF018F9299923BD00248CDD /* VwrPrefPaneBehavior.swift in Sources */,
|
||||
5BB802DA27FABA8300CF1C19 /* SessionCtl_Menu.swift in Sources */,
|
||||
5B2E009428FD1E8100E78D6E /* VwrPrefPaneCassette.swift in Sources */,
|
||||
|
|
Loading…
Reference in New Issue