AboutUI // Reimplement without XIB for old systems.

This commit is contained in:
ShikiSuen 2024-02-08 22:51:07 +08:00
parent 80fe5ecb74
commit a219b7881f
3 changed files with 266 additions and 12 deletions

View File

@ -9,21 +9,38 @@
import AppKit import AppKit
import SwiftUI import SwiftUI
@available(macOS 12, *)
public class CtlAboutUI: NSWindowController, NSWindowDelegate { public class CtlAboutUI: NSWindowController, NSWindowDelegate {
public static var shared: CtlAboutUI? public static var shared: CtlAboutUI?
private var viewController: NSViewController?
var useLegacyView: Bool = false
public init(forceLegacy: Bool = false) {
useLegacyView = forceLegacy
let newWindow = NSWindow(
contentRect: CGRect(x: 401, y: 295, width: 577, height: 568),
styleMask: [.titled, .closable, .miniaturizable],
backing: .buffered, defer: true
)
super.init(window: newWindow)
guard #available(macOS 12, *), !useLegacyView else {
viewController = VwrAboutCocoa()
viewController?.loadView()
return
}
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
public static func show() { public static func show() {
let forceLegacy = NSEvent.modifierFlags == .option
if shared == nil { if shared == nil {
let newWindow = NSWindow( let newInstance = CtlAboutUI(forceLegacy: forceLegacy)
contentRect: CGRect(x: 401, y: 295, width: 577, height: 568),
styleMask: [.titled, .closable, .miniaturizable],
backing: .buffered, defer: true
)
let newInstance = CtlAboutUI(window: newWindow)
shared = newInstance shared = newInstance
} }
guard let shared = shared, let sharedWindow = shared.window else { return } guard let shared = shared, let sharedWindow = shared.window else { return }
shared.useLegacyView = forceLegacy
sharedWindow.delegate = shared sharedWindow.delegate = shared
if !sharedWindow.isVisible { if !sharedWindow.isVisible {
shared.windowDidLoad() shared.windowDidLoad()
@ -37,6 +54,28 @@ public class CtlAboutUI: NSWindowController, NSWindowDelegate {
override public func windowDidLoad() { override public func windowDidLoad() {
super.windowDidLoad() super.windowDidLoad()
guard let window = window else { return }
if #available(macOS 12, *), !useLegacyView {
windowDidLoadSwiftUI()
return
}
let theViewController = viewController ?? VwrAboutCocoa()
viewController = theViewController
window.contentViewController = viewController
let size = theViewController.view.fittingSize
window.setPosition(vertical: .top, horizontal: .left, padding: 20)
window.setFrame(.init(origin: window.frame.origin, size: size), display: true)
window.standardWindowButton(.closeButton)?.isHidden = true
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
window.standardWindowButton(.zoomButton)?.isHidden = true
if #available(macOS 10.10, *) {
window.titlebarAppearsTransparent = true
}
window.title = "i18n:aboutWindow.ABOUT_APP_TITLE_FULL".localized + " (v\(IMEApp.appMainVersionLabel.joined(separator: " Build ")))"
}
@available(macOS 12, *)
private func windowDidLoadSwiftUI() {
window?.setPosition(vertical: .top, horizontal: .left, padding: 20) window?.setPosition(vertical: .top, horizontal: .left, padding: 20)
window?.standardWindowButton(.closeButton)?.isHidden = true window?.standardWindowButton(.closeButton)?.isHidden = true
window?.standardWindowButton(.miniaturizeButton)?.isHidden = true window?.standardWindowButton(.miniaturizeButton)?.isHidden = true

View File

@ -0,0 +1,211 @@
// (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 AppKit
import Foundation
import Shared
public extension VwrAboutCocoa {
static let copyrightLabel = Bundle.main.localizedInfoDictionary?["NSHumanReadableCopyright"] as? String ?? "BAD_COPYRIGHT_LABEL"
static let eulaContent = Bundle.main.localizedInfoDictionary?["CFEULAContent"] as? String ?? "BAD_EULA_CONTENT"
static let eulaContentUpstream = Bundle.main.infoDictionary?["CFUpstreamEULAContent"] as? String ?? "BAD_EULA_UPSTREAM"
}
public class VwrAboutCocoa: NSViewController {
let windowWidth: CGFloat = 533
let contentWidth: CGFloat = 510
let imgWidth: CGFloat = 63
override public func loadView() {
view = body ?? .init()
(view as? NSStackView)?.alignment = .centerX
view.makeSimpleConstraint(.width, relation: .equal, value: windowWidth)
}
var appNameAndVersionString: NSAttributedString {
let strResult = NSMutableAttributedString(string: "i18n:aboutWindow.APP_NAME".localized)
strResult.addAttribute(
.font,
value: {
if #available(macOS 10.11, *) {
return NSFont.systemFont(ofSize: 12, weight: .bold)
}
return NSFont.boldSystemFont(ofSize: 12)
}(),
range: .init(location: 0, length: strResult.length)
)
let strVersion = NSMutableAttributedString(string: " \(versionString)")
strVersion.addAttribute(
.font,
value: NSFont.systemFont(ofSize: 11),
range: .init(location: 0, length: strVersion.length)
)
strResult.append(strVersion)
strResult.addAttribute(
.kern,
value: 0,
range: .init(location: 0, length: strResult.length)
)
return strResult
}
var body: NSView? {
NSStackView.buildSection(width: contentWidth - 18) {
NSStackView.build(.horizontal) {
bannerImage
NSStackView.build(.vertical) {
appNameAndVersionString.makeNSLabel(fixWidth: contentWidth - imgWidth - 10)
makeFormattedLabel(
verbatim: "i18n:aboutWindow.APP_DERIVED_FROM".localized
+ "\n"
+ Self.copyrightLabel,
size: 11,
isBold: false, fixWidth: contentWidth - imgWidth - 10
)
makeFormattedLabel(
verbatim: "i18n:aboutWindow.DEV_CREW".localized,
size: 11,
isBold: false, fixWidth: contentWidth - imgWidth - 10
)
makeFormattedLabel(
verbatim: "i18n:aboutWindow.LICENSE_TITLE".localized,
size: 11,
isBold: false, fixWidth: contentWidth - imgWidth - 10
)
eulaBox
}
}
NSStackView.build(.horizontal) {
NSStackView.build(.vertical) {
"i18n:aboutWindow.DISCLAIMER_TEXT".makeNSLabel(
descriptive: true, fixWidth: contentWidth - 120
)
NSView()
}
NSStackView.build(.vertical, width: 114) {
addKeyEquivalent(
NSButton(
"i18n:aboutWindow.OK_BUTTON",
target: self, action: #selector(btnOKAction(_:))
)
)
NSButton(
"i18n:aboutWindow.WEBSITE_BUTTON",
target: self, action: #selector(btnWebSiteAction(_:))
)
NSButton(
"i18n:aboutWindow.BUGREPORT_BUTTON",
target: self, action: #selector(btnBugReportAction(_:))
)
}
}
}?.withInsets(
{
if #available(macOS 10.10, *) {
return .new(all: 20, top: 0, bottom: 24)
} else {
return .new(all: 20, top: 10, bottom: 24)
}
}()
)
}
var versionString: String {
"v\(IMEApp.appMainVersionLabel.joined(separator: " Build ")) - \(IMEApp.appSignedDateLabel)"
}
var bannerImage: NSImageView {
let maybeImg = NSImage(named: "AboutBanner")
let imgIsNull = maybeImg == nil
let img = maybeImg ?? .init(size: .init(width: 63, height: 310))
let result = NSImageView()
result.image = img
result.makeSimpleConstraint(.width, relation: .equal, value: 63)
result.makeSimpleConstraint(.height, relation: .equal, value: 310)
if imgIsNull {
result.wantsLayer = true
result.layer?.backgroundColor = NSColor.black.cgColor
}
return result
}
func makeFormattedLabel(
verbatim: String,
size: CGFloat = 12,
isBold: Bool = false,
fixWidth: CGFloat? = nil
) -> NSTextField {
let attrStr = NSMutableAttributedString(string: verbatim)
attrStr.addAttribute(
.kern,
value: 0,
range: .init(location: 0, length: attrStr.length)
)
attrStr.addAttribute(
.font,
value: {
guard isBold else { return NSFont.systemFont(ofSize: size) }
if #available(macOS 10.11, *) {
return NSFont.systemFont(ofSize: size, weight: .bold)
}
return NSFont.boldSystemFont(ofSize: size)
}(),
range: .init(location: 0, length: attrStr.length)
)
return attrStr.makeNSLabel(fixWidth: fixWidth)
}
var eulaBox: NSScrollView {
let textView = NSTextView()
let clipView = NSClipView()
let scrollView = NSScrollView()
textView.autoresizingMask = [.width, .height]
textView.isEditable = false
textView.isRichText = false
textView.isSelectable = true
textView.isVerticallyResizable = true
textView.smartInsertDeleteEnabled = true
textView.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
textView.string = Self.eulaContent + "\n" + Self.eulaContentUpstream
clipView.documentView = textView
clipView.autoresizingMask = [.width, .height]
clipView.drawsBackground = false
scrollView.contentView = clipView
scrollView.makeSimpleConstraint(.width, relation: .equal, value: 430)
scrollView.hasVerticalScroller = true
scrollView.hasHorizontalScroller = false
scrollView.scrollerStyle = .legacy
return scrollView
}
@discardableResult func addKeyEquivalent(_ button: NSButton) -> NSButton {
button.keyEquivalent = String(NSEvent.SpecialKey.carriageReturn.unicodeScalar)
return button
}
@objc func btnOKAction(_: NSControl) {
CtlAboutUI.shared?.window?.close()
}
@objc func btnWebSiteAction(_: NSControl) {
if let url = URL(string: "https://vchewing.github.io/") {
NSWorkspace.shared.open(url)
}
}
@objc func btnBugReportAction(_: NSControl) {
if let url = URL(string: "https://vchewing.github.io/BUGREPORT.html") {
NSWorkspace.shared.open(url)
}
}
}
@available(macOS 14.0, *)
#Preview(traits: .fixedLayout(width: 533, height: 550)) {
VwrAboutCocoa()
}

View File

@ -9,12 +9,15 @@
import AppKit import AppKit
import SwiftUI import SwiftUI
@available(macOS 12, *) public struct VwrAboutUI {
public struct VwrAboutUI: View { public static var copyrightLabel: String { VwrAboutCocoa.copyrightLabel }
public static let copyrightLabel = Bundle.main.localizedInfoDictionary?["NSHumanReadableCopyright"] as? String ?? "BAD_COPYRIGHT_LABEL" public static var eulaContent: String { VwrAboutCocoa.eulaContent }
public static let eulaContent = Bundle.main.localizedInfoDictionary?["CFEULAContent"] as? String ?? "BAD_EULA_CONTENT" public static var eulaContentUpstream: String { VwrAboutCocoa.eulaContentUpstream }
public static let eulaContentUpstream = Bundle.main.infoDictionary?["CFUpstreamEULAContent"] as? String ?? "BAD_EULA_UPSTREAM" let foobar = "FOO_BAR"
}
@available(macOS 12, *)
extension VwrAboutUI: View {
public var body: some View { public var body: some View {
GroupBox { GroupBox {
VStack(alignment: .leading, spacing: 6) { VStack(alignment: .leading, spacing: 6) {
@ -29,6 +32,7 @@ public struct VwrAboutUI: View {
Text("v\(IMEApp.appMainVersionLabel.joined(separator: " Build ")) - \(IMEApp.appSignedDateLabel)").lineLimit(1) Text("v\(IMEApp.appMainVersionLabel.joined(separator: " Build ")) - \(IMEApp.appSignedDateLabel)").lineLimit(1)
}.fixedSize() }.fixedSize()
Text("i18n:aboutWindow.APP_DERIVED_FROM").font(.custom("Tahoma", size: 11)) Text("i18n:aboutWindow.APP_DERIVED_FROM").font(.custom("Tahoma", size: 11))
Text(Self.copyrightLabel).font(.custom("Tahoma", size: 11))
Text("i18n:aboutWindow.DEV_CREW").font(.custom("Tahoma", size: 11)).padding([.vertical], 2) Text("i18n:aboutWindow.DEV_CREW").font(.custom("Tahoma", size: 11)).padding([.vertical], 2)
} }
} }