AboutUI // Reimplement without XIB for old systems.
This commit is contained in:
parent
80fe5ecb74
commit
a219b7881f
|
@ -9,21 +9,38 @@
|
|||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
@available(macOS 12, *)
|
||||
public class CtlAboutUI: NSWindowController, NSWindowDelegate {
|
||||
public static var shared: CtlAboutUI?
|
||||
private var viewController: NSViewController?
|
||||
var useLegacyView: Bool = false
|
||||
|
||||
public static func show() {
|
||||
if shared == nil {
|
||||
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
|
||||
)
|
||||
let newInstance = CtlAboutUI(window: newWindow)
|
||||
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() {
|
||||
let forceLegacy = NSEvent.modifierFlags == .option
|
||||
if shared == nil {
|
||||
let newInstance = CtlAboutUI(forceLegacy: forceLegacy)
|
||||
shared = newInstance
|
||||
}
|
||||
guard let shared = shared, let sharedWindow = shared.window else { return }
|
||||
shared.useLegacyView = forceLegacy
|
||||
sharedWindow.delegate = shared
|
||||
if !sharedWindow.isVisible {
|
||||
shared.windowDidLoad()
|
||||
|
@ -37,6 +54,28 @@ public class CtlAboutUI: NSWindowController, NSWindowDelegate {
|
|||
|
||||
override public func 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?.standardWindowButton(.closeButton)?.isHidden = true
|
||||
window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -9,12 +9,15 @@
|
|||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
@available(macOS 12, *)
|
||||
public struct VwrAboutUI: View {
|
||||
public static let copyrightLabel = Bundle.main.localizedInfoDictionary?["NSHumanReadableCopyright"] as? String ?? "BAD_COPYRIGHT_LABEL"
|
||||
public static let eulaContent = Bundle.main.localizedInfoDictionary?["CFEULAContent"] as? String ?? "BAD_EULA_CONTENT"
|
||||
public static let eulaContentUpstream = Bundle.main.infoDictionary?["CFUpstreamEULAContent"] as? String ?? "BAD_EULA_UPSTREAM"
|
||||
public struct VwrAboutUI {
|
||||
public static var copyrightLabel: String { VwrAboutCocoa.copyrightLabel }
|
||||
public static var eulaContent: String { VwrAboutCocoa.eulaContent }
|
||||
public static var eulaContentUpstream: String { VwrAboutCocoa.eulaContentUpstream }
|
||||
let foobar = "FOO_BAR"
|
||||
}
|
||||
|
||||
@available(macOS 12, *)
|
||||
extension VwrAboutUI: View {
|
||||
public var body: some View {
|
||||
GroupBox {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
|
@ -29,6 +32,7 @@ public struct VwrAboutUI: View {
|
|||
Text("v\(IMEApp.appMainVersionLabel.joined(separator: " Build ")) - \(IMEApp.appSignedDateLabel)").lineLimit(1)
|
||||
}.fixedSize()
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue