No more keyboard shortcuts sounds in the popover.

This commit is contained in:
2025-01-15 15:10:11 -08:00
parent 94a2c217ef
commit e1146840f6
16 changed files with 257 additions and 173 deletions

4
.gitignore vendored
View File

@@ -2,3 +2,7 @@
arm64
x86_64
build
CmdBar
CmdBar.app
cmdbar_updater
src/updater

View File

@@ -5,7 +5,7 @@
// Created by Igor Kolokolnikov on 8/16/23.
//
import Cocoa
import AppKit
// MARK: - Constants
fileprivate enum AboutLinks {
@@ -24,12 +24,6 @@ enum Strings {
static let activating = "Activating..."
}
fileprivate enum ViewConstants {
static let spacing2: CGFloat = 2
static let spacing10: CGFloat = 10
static let spacing20: CGFloat = 20
}
// MARK: - Controller
class AboutViewController: NSViewController, NSTextFieldDelegate {
// MARK: - Views

View File

@@ -1,4 +1,4 @@
import Cocoa
import AppKit
import ServiceManagement
class AppDelegate: NSObject, NSApplicationDelegate {

View File

@@ -28,7 +28,7 @@
// from the coverage of the license being applied to the rest of the code.
//
import Cocoa
import AppKit
import os
final class CBMenuBarItem: NSObject, NSWindowDelegate {

View File

@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>

View File

@@ -1,14 +1,12 @@
import Foundation
import Cocoa
import AppKit
// MARK: - CmdFile
class CmdFile {
// MARK: - State
private var timer: DispatchSourceTimer?
private(set) var lastExec = Int(Date().timeIntervalSince1970)
var untilNextExec: Int {
get {
let res = reloadTime - (Int(Date().timeIntervalSince1970) - lastExec)
let res = reloadTime -
(Int(Date().timeIntervalSince1970) - lastExec)
if res <= 0 { return reloadTime }
return res
}
@@ -16,11 +14,10 @@ class CmdFile {
private(set) var url: URL
private(set) var reloadTime: Int
private var statusItem: CBMenuBarItem = CBMenuBarItem()
private(set) var statusItem: CBMenuBarItem = CBMenuBarItem()
private var shouldWait = false
// MARK: - Initializers
init(url: URL, reloadTime: Int) {
self.url = url
self.reloadTime = reloadTime
@@ -37,7 +34,6 @@ class CmdFile {
cancelTimer()
}
// MARK: - Reload Widget
private func reloadWidget() {
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
if let url = self?.url {
@@ -45,10 +41,13 @@ class CmdFile {
let execResult = execute(atPath: url)
DispatchQueue.main.async {
if execResult.title.isEmpty {
self?.statusItem.setImage(title: "exclamationmark.triangle.fill", description: "Warning")
self?.statusItem.setImage(
title: "exclamationmark.triangle.fill",
description: "Warning")
self?.statusItem.setTitle("")
} else {
self?.statusItem.setImage(title: nil, description: "")
self?.statusItem.setImage(title: nil,
description: "")
self?.statusItem.setTitle(execResult.0)
}
self?.statusItem.setContents(to: execResult.body)
@@ -58,7 +57,6 @@ class CmdFile {
}
}
// MARK: - Timer
private func setupTimer() {
timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
timer?.schedule(deadline: .now(), repeating: .seconds(reloadTime))
@@ -91,15 +89,12 @@ class CmdFile {
}
}
// MARK: - CmdManager
class CmdManager {
//MARK: - State
static let standard = CmdManager()
var paths: [CmdFile] = []
private var defaultStatusItem: NSStatusItem?
// MARK: - Configure
func configure() {
getPaths()
if paths.isEmpty {
@@ -111,23 +106,31 @@ class CmdManager {
private func getPaths() {
let manager = FileManager.default
let path = manager.homeDirectoryForCurrentUser.appending(path: ".cmdbar")
if let contents = try? manager.contentsOfDirectory(atPath: path.path()) {
let path = manager.homeDirectoryForCurrentUser
.appending(path: ".cmdbar")
if let contents = try? manager
.contentsOfDirectory(atPath: path.path()) {
for content in contents {
let filePath = path.appending(path: content)
let domains = filePath.path().components(separatedBy: ".")
if (isValidTimeFormat(domains[domains.count - 1])) {
paths.append(CmdFile(url: filePath, reloadTime: intervalToSeconds(from: domains[domains.count - 1])))
} else if (domains.last == "sh" && isValidTimeFormat(domains[domains.count - 2])) {
paths.append(CmdFile(url: filePath, reloadTime: intervalToSeconds(from: domains[domains.count - 2])))
paths.append(CmdFile(url: filePath,
reloadTime: intervalToSeconds(
from: domains[domains.count - 1])))
} else if domains.last == "sh" &&
isValidTimeFormat(domains[domains.count - 2]) {
paths.append(CmdFile(url: filePath,
reloadTime: intervalToSeconds(
from: domains[domains.count - 2])))
}
}
}
}
private func setupMenu() {
defaultStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
defaultStatusItem = NSStatusBar.system
.statusItem(withLength: NSStatusItem.variableLength)
if let btn = defaultStatusItem?.button {
let image = NSApp.applicationIconImage
image!.size = NSSize(width: 22, height: 22)
@@ -135,13 +138,21 @@ class CmdManager {
}
guard defaultStatusItem != nil else { return }
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Reload", action: #selector(reloadWidgets), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Reload",
action: #selector(reloadWidgets),
keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "About", action: #selector(showAbout), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "About",
action: #selector(showAbout),
keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Check for Updates...", action: #selector(checkForUpdates), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Check for Updates...",
action: #selector(checkForUpdates),
keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
menu.addItem(NSMenuItem(title: "Quit",
action: #selector(NSApplication.terminate(_:)),
keyEquivalent: "q"))
defaultStatusItem!.menu = menu
}
@@ -164,7 +175,6 @@ class CmdManager {
reloadItems()
}
// MARK: - NSMenu Functions
@objc private func showAbout() {
delegate.showAbout()
}

View File

@@ -1,22 +1,17 @@
import Cocoa
import AppKit
import Carbon
import ServiceManagement
import OSLog
fileprivate enum Metrics {
static let contentMinWidth = 400.0
static let contentMaxWidth = /*600.0*/ NSScreen.main!.visibleFrame.size.width * 0.7 // REVIEW: Under what circumstances can NSScreen return nil?
static let contentMinWidth = 400.0
static let contentMaxWidth = /*600.0*/ NSScreen.main!.visibleFrame.size.width * 0.7 // WARNING: Under what circumstances can NSScreen return nil?
static let contentMinHeight = 160.0
static let contentMaxHeight = /*800.0*/ NSScreen.main!.visibleFrame.size.height * 0.8 // REVIEW: Under what circumstances can NSScreen return nil?
static let padding = 10.0
static let buttonSpacing = 2.0
static let contentMaxHeight = /*800.0*/ NSScreen.main!.visibleFrame.size.height * 0.8 // WARNING: Under what circumstances can NSScreen return nil?
static let padding = 10.0
static let buttonSpacing = 2.0
}
class CmdViewController: NSViewController {
fileprivate static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: CmdViewController.self)
)
private weak var cmdFile: CmdFile?
private var timer: DispatchSourceTimer?
private var keyboardEvents: EventMonitor?
@@ -332,7 +327,10 @@ class CmdViewController: NSViewController {
}
private func resetReloadButton() {
reloadButton.image = systemImage("arrow.clockwise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemBlue]))
reloadButton.image = systemImage("arrow.clockwise.circle.fill",
.title2, .large,
.init(paletteColors: [.white,
.systemBlue]))
reloadButton.action = #selector(reloadWidget)
reloadButton.toolTip = "Reload Current"
}
@@ -351,51 +349,51 @@ class CmdViewController: NSViewController {
}
private func setupKeyEvents() {
keyboardEvents = LocalEventMonitor(mask: [.flagsChanged, .keyDown], handler: { [weak self] event in
keyboardEvents = LocalEventMonitor(mask: [.flagsChanged, .keyDown],
handler:
{ [weak self] event in
let modifiers = event.modifierFlags.rawValue
let command = NSEvent.ModifierFlags.command.rawValue
let shift = NSEvent.ModifierFlags.shift.rawValue
let control = NSEvent.ModifierFlags.control.rawValue
let option = NSEvent.ModifierFlags.option.rawValue
let key = event.keyCode
if event.modifierFlags.contains(.shift) {
self?.reloadButton.image = systemImage("arrow.clockwise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemCyan]))
if modsContains(keys: OSShift, in: modifiers) {
self?.reloadButton.image =
systemImage("arrow.clockwise.circle.fill", .title2,
.large,.init(paletteColors: [.white,
.systemCyan]))
self?.reloadButton.action = #selector(self?.reloadWidgets)
self?.reloadButton.toolTip = "Reload All"
} else {
self?.reloadButton.image = systemImage("arrow.clockwise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemBlue]))
self?.reloadButton.image =
systemImage("arrow.clockwise.circle.fill", .title2,
.large, .init(paletteColors: [.white,
.systemBlue]))
self?.reloadButton.action = #selector(self?.reloadWidget)
self?.reloadButton.toolTip = "Reload Current"
}
// NOTE: Standalone keys should go last!
if (modifiers & command) == command,
(modifiers & (control | shift | option)) == 0,
event.keyCode == 12 // Q
{
self?.terminateApp()
} else if (modifiers & command) == command,
(modifiers & (control | shift | option)) == 0,
event.keyCode == 13 // W
{
self?.view.superview?.window?.resignKey()
} else if (modifiers & command) == command,
(modifiers & (control | shift | option)) == 0,
event.keyCode == 15 // R
{
self?.reloadWidget()
} else if (modifiers & (command & shift)) == command & shift,
(modifiers & (control | option)) == 0,
event.keyCode == 15 // R
{
self?.reloadWidgets()
} else if (modifiers & command) == command,
(modifiers & (control | shift | option)) == 0,
event.keyCode == 8 // C
{
self?.textLabel.currentEditor()?.copy(nil)
} else if event.keyCode == 12 || event.keyCode == 53 { // Q, ESC
self?.view.superview?.window?.resignKey()
if modsContains(keys: OSCmd, in: modifiers) {
if key == kVK_ANSI_Q {
self?.terminateApp()
} else if key == kVK_ANSI_W {
self?.view.superview?.window?.resignKey()
} else if key == kVK_ANSI_R {
self?.reloadWidget()
} else if key == kVK_ANSI_C {
self?.textLabel.currentEditor()?.copy(nil)
}
} else if modsContains(keys: OSCmd | OSShift, in: modifiers) {
if key == kVK_ANSI_R {
// NOTE: We need to resign the window in order to
// prevent the program from intercepting the
// events, which result in sounds beign made for
// missing performKeyEquivalent.
self?.view.window?.resignKey()
self?.reloadWidgets()
}
} else if modsContainsNone(in: modifiers) {
if key == kVK_ANSI_Q || key == kVK_Escape {
self?.view.superview?.window?.resignKey()
}
}
return event

View File

@@ -1,32 +1,44 @@
import Cocoa
import AppKit
import Carbon
final class EditableNSTextField: NSTextField {
private let commandKey = NSEvent.ModifierFlags.command.rawValue
private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
override func performKeyEquivalent(with event: NSEvent) -> Bool {
let modifiers = event.modifierFlags.rawValue
let key = event.keyCode
if event.type == NSEvent.EventType.keyDown {
if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
switch event.charactersIgnoringModifiers! {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true }
case "z":
if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true }
case "a":
if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true }
default:
break
if modsContains(keys: OSCmd, in: modifiers) {
if key == kVK_ANSI_X {
if NSApp.sendAction(#selector(NSText.cut(_:)),
to: nil, from: self)
{ return true }
} else if key == kVK_ANSI_C {
if NSApp.sendAction(#selector(NSText.copy(_:)),
to: nil, from: self)
{ return true }
} else if key == kVK_ANSI_V {
if NSApp.sendAction(#selector(NSText.paste(_:)),
to: nil, from: self)
{ return true }
} else if key == kVK_ANSI_Z {
if NSApp.sendAction(Selector(("undo:")),
to: nil, from: self)
{ return true }
} else if key == kVK_ANSI_A {
if NSApp.sendAction(
#selector(NSResponder.selectAll(_:)),
to: nil, from: self)
{ return true }
}
} else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true }
} else if modsContains(keys: OSCmd | OSShift, in: modifiers) {
if key == kVK_ANSI_Z {
if NSApp.sendAction(Selector(("redo:")),
to: nil, from: self)
{ return true }
}
}
}
return super.performKeyEquivalent(with: event)
}
}

View File

@@ -1,4 +1,4 @@
import Cocoa
import AppKit
class EventMonitor {
fileprivate let mask: NSEvent.EventTypeMask

View File

@@ -1,6 +1,32 @@
import AppKit
import Carbon
let OSCtrl = NSEvent.ModifierFlags.control.rawValue
let OSCmd = NSEvent.ModifierFlags.command.rawValue
let OSOpt = NSEvent.ModifierFlags.option.rawValue
let OSShift = NSEvent.ModifierFlags.shift.rawValue
let OSMods = UInt(OSCtrl | OSCmd | OSOpt | OSShift)
func modsContains(keys: UInt, in modifiers: UInt) -> Bool {
return (modifiers & keys) == keys && ((modifiers ^ keys) & OSMods) == 0
}
func modsContainsNone(in modifiers: UInt) -> Bool {
return (modifiers & OSMods) == 0
}
enum ViewConstants {
static let spacing2: CGFloat = 2
static let spacing5: CGFloat = 2
static let spacing10: CGFloat = 10
static let spacing15: CGFloat = 15
static let spacing20: CGFloat = 20
static let spacing25: CGFloat = 25
static let spacing30: CGFloat = 30
static let spacing35: CGFloat = 35
static let spacing40: CGFloat = 40
}
// MARK: - Tracking Notifications
fileprivate extension Notification.Name {
static let beginMenuTracking = Notification.Name("com.apple.HIToolbox.beginMenuTrackingNotification")
static let endMenuTracking = Notification.Name("com.apple.HIToolbox.endMenuTrackingNotification")
@@ -9,10 +35,10 @@ fileprivate extension Notification.Name {
#if(DEBUG)
struct EndpointConstants {
static var versionURL = "http://localhost:8081/version"
static var appURL = "http://localhost:8081/download"
static var appURLZip = "http://localhost:8081/static/app/CmdBar.zip"
static var changelog = "http://localhost:8081/changelog"
static var validateURL = "http://localhost:8081/validate"
static var appURL = "http://localhost:8081/download"
static var appURLZip = "http://localhost:8081/static/app/CmdBar.zip"
static var changelog = "http://localhost:8081/changelog"
static var validateURL = "http://localhost:8081/validate"
}
#else
struct EndpointConstants {
@@ -24,7 +50,6 @@ struct EndpointConstants {
}
#endif
//
enum Resulting<Success, Failure> {
case success
case failure
@@ -156,7 +181,10 @@ func getColors(_ light: NSColor, _ dark: NSColor, for appearance: NSAppearance)
}
// ex: systemImage("sunrise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemOrange]))
func systemImage(_ name: String, _ size: NSFont.TextStyle, _ scale: NSImage.SymbolScale, _ configuration: NSImage.SymbolConfiguration) -> NSImage? {
func systemImage(_ name: String, _ size: NSFont.TextStyle,
_ scale: NSImage.SymbolScale,
_ configuration: NSImage.SymbolConfiguration) -> NSImage?
{
return NSImage(systemSymbolName: name, accessibilityDescription: nil)?
.withSymbolConfiguration(
NSImage.SymbolConfiguration(textStyle: size, scale: scale)

View File

@@ -5,7 +5,7 @@
// Created by Igor Kolokolnikov on 8/26/23.
//
import Cocoa
import AppKit
import CryptoKit
fileprivate struct LicenseModel: Codable {

View File

@@ -86,7 +86,11 @@ $(EXEC).app: $(EXEC)
cp resources/AppIcon.icns $@/Contents/Resources/ && \
cp $(EXEC) $@/Contents/MacOS/ && \
cp cmdbar_updater $@/Contents/MacOS/ && \
codesign -s ${DEVELOPER_ID} -f --timestamp -o runtime $(EXEC).app
$(if $(DEBUG), \
codesign --entitlements Grapp.entitlements \
-s ${APPLE_DEVELOPMENT} -f --timestamp -o runtime $(EXEC).app, \
codesign -s ${APPLE_DEVELOPER_ID_APPLICATION} -f --timestamp \
-o runtime $(EXEC).app)
all: zip cmdbar_updater $(EXEC).app

View File

@@ -1,4 +1,4 @@
import Cocoa
import AppKit
class MenulessWindow: NSWindow {
init(viewController: NSViewController) {

View File

@@ -1,4 +1,4 @@
import Cocoa
import AppKit
class PopoverPanel: NSPanel {
override var canBecomeKey: Bool { true }
@@ -6,7 +6,8 @@ class PopoverPanel: NSPanel {
init(viewController: NSViewController) {
super.init(
contentRect: CGRect(x: 0, y: 0, width: 100, height: 100),
styleMask: [.titled, .nonactivatingPanel, .utilityWindow, .fullSizeContentView],
styleMask: [.titled, .nonactivatingPanel, .utilityWindow,
.fullSizeContentView],
backing: .buffered,
defer: false
)
@@ -22,7 +23,8 @@ class PopoverPanel: NSPanel {
titlebarAppearsTransparent = true
animationBehavior = .none
collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary, .transient]
collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary,
.transient]
isReleasedWhenClosed = false
hidesOnDeactivate = false

View File

@@ -1,28 +1,14 @@
//
// UpdateViewController.swift
// CmdBar
//
// Created by Igor Kolokolnikov on 5/23/24.
//
import AppKit
fileprivate enum ViewConstants {
static let spacing2: CGFloat = 2
static let spacing10: CGFloat = 10
static let spacing20: CGFloat = 20
static let spacing40: CGFloat = 40
}
fileprivate enum UpdateStatus: String {
case check = "Check for updates..."
case latest = "You're up-to-date!"
case checking = "Checking for udpates..."
case available = "A new version is available!"
case downloading = "Downloading update..."
case extracting = "Extracting update..."
case install = "Ready to Install"
case checkFailed = "Failed to Check for Updates"
case check = "Check for updates..."
case latest = "You're up-to-date!"
case checking = "Checking for udpates..."
case available = "A new version is available!"
case downloading = "Downloading update..."
case extracting = "Extracting update..."
case install = "Ready to Install"
case checkFailed = "Failed to Check for Updates"
case installFailed = "Failed to Install Update"
}
@@ -30,9 +16,9 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
private weak var updaterDelegate: UpdateManagerDelegate?
private var appIconImage: NSImageView = {
//let image = NSImageView(image: NSApp.applicationIconImage)
let image = NSImageView()
image.image = NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath)
image.image = NSWorkspace.shared
.icon(forFile: Bundle.main.bundlePath)
image.imageScaling = .scaleAxesIndependently
image.translatesAutoresizingMaskIntoConstraints = false
return image
@@ -45,7 +31,9 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
textField.drawsBackground = false
textField.alignment = .left
textField.textColor = NSColor(name: nil) { getColors(.black, .white, for: $0) }
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .bold)
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor
.preferredFontDescriptor(forTextStyle: .body).pointSize,
weight: .bold)
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
@@ -59,18 +47,23 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
textField.drawsBackground = false
textField.alignment = .left
textField.textColor = .gray
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .bold)
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor
.preferredFontDescriptor(forTextStyle: .body).pointSize,
weight: .bold)
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
private var changelogLabel: NSScrollView = { // TODO: Improve some more. Maybe make it scrollable.
// TODO: Improve some more. Maybe make it scrollable.
private var changelogLabel: NSScrollView = {
let scrollableTextView = NSTextView.scrollableTextView()
scrollableTextView.isHidden = true
(scrollableTextView.documentView as? NSTextView)?.isEditable = false
// (scrollableTextView.documentView as? NSTextView)?.drawsBackground = false
(scrollableTextView.documentView as? NSTextView)?.textColor = .gray
(scrollableTextView.documentView as? NSTextView)?.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .regular)
(scrollableTextView.documentView as? NSTextView)?.font =
NSFont.systemFont(ofSize: NSFontDescriptor
.preferredFontDescriptor(forTextStyle: .body).pointSize,
weight: .regular)
scrollableTextView.translatesAutoresizingMaskIntoConstraints = false
return scrollableTextView
}()
@@ -139,26 +132,46 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
NSLayoutConstraint.activate([
appIconImage.widthAnchor.constraint(equalToConstant: 70),
appIconImage.heightAnchor.constraint(equalTo: appIconImage.widthAnchor, multiplier: 1),
appIconImage.heightAnchor
.constraint(equalTo: appIconImage.widthAnchor,
multiplier: 1),
appIconImage.topAnchor.constraint(equalTo: view.topAnchor,
constant: ViewConstants.spacing10),
appIconImage.leadingAnchor
.constraint(equalTo: view.leadingAnchor,
constant: ViewConstants.spacing10),
appIconImage.topAnchor.constraint(equalTo: view.topAnchor, constant: ViewConstants.spacing10),
appIconImage.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: ViewConstants.spacing10),
statusLabel.topAnchor
.constraint(equalTo: appIconImage.topAnchor,
constant: ViewConstants.spacing10),
statusLabel.leadingAnchor
.constraint(equalTo: appIconImage.trailingAnchor,
constant: ViewConstants.spacing10),
statusLabel.topAnchor.constraint(equalTo: appIconImage.topAnchor, constant: ViewConstants.spacing2),
statusLabel.leadingAnchor.constraint(equalTo: appIconImage.trailingAnchor, constant: ViewConstants.spacing10),
subStatusLabel.topAnchor
.constraint(equalTo: statusLabel.bottomAnchor,
constant: ViewConstants.spacing10),
subStatusLabel.leadingAnchor
.constraint(equalTo: statusLabel.leadingAnchor),
subStatusLabel.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: ViewConstants.spacing10),
subStatusLabel.leadingAnchor.constraint(equalTo: statusLabel.leadingAnchor),
changelogLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor),
changelogLabel.widthAnchor
.constraint(equalTo: stackView.widthAnchor),
changelogLabel.heightAnchor.constraint(equalToConstant: 150),
stackView.topAnchor.constraint(equalTo: subStatusLabel.bottomAnchor, constant: ViewConstants.spacing2),
stackView.leadingAnchor.constraint(equalTo: statusLabel.leadingAnchor),
view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: ViewConstants.spacing20),
view.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: ViewConstants.spacing20),
stackView.topAnchor
.constraint(equalTo: subStatusLabel.bottomAnchor,
constant: ViewConstants.spacing2),
stackView.leadingAnchor
.constraint(equalTo: statusLabel.leadingAnchor),
view.trailingAnchor
.constraint(equalTo: stackView.trailingAnchor,
constant: ViewConstants.spacing20),
view.bottomAnchor
.constraint(equalTo: stackView.bottomAnchor,
constant: ViewConstants.spacing20),
buttonsStackView.heightAnchor.constraint(equalTo: updateButton.heightAnchor),
buttonsStackView.heightAnchor
.constraint(equalTo: updateButton.heightAnchor),
])
}
@@ -192,36 +205,44 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
case .check:
break
case .latest:
self?.statusLabel.stringValue = UpdateStatus.latest.rawValue
self?.statusLabel.stringValue = UpdateStatus.latest
.rawValue
break
case .checking:
self?.statusLabel.stringValue = UpdateStatus.checking.rawValue
self?.statusLabel.stringValue = UpdateStatus.checking
.rawValue
self?.updateButton.keyEquivalent = ""
self?.updateButton.isEnabled = false
case .available:
self?.updateButton.title = "Download"
self?.updateButton.keyEquivalent = ""
self?.statusLabel.stringValue = UpdateStatus.available.rawValue
self?.statusLabel.stringValue = UpdateStatus.available
.rawValue
self?.updateButton.action = #selector(self!.downloadUpdate)
case .downloading:
self?.updateButton.isEnabled = false
self?.updateButton.title = "Install"
self?.progressIndicator.isHidden = false
self?.statusLabel.stringValue = UpdateStatus.downloading.rawValue
self?.statusLabel.stringValue = UpdateStatus.downloading
.rawValue
case .extracting:
self?.updateButton.isEnabled = false
self?.updateButton.title = "Install"
self?.progressIndicator.isHidden = false
self?.statusLabel.stringValue = UpdateStatus.extracting.rawValue
self?.statusLabel.stringValue = UpdateStatus.extracting
.rawValue
case .install:
self?.updateButton.title = "Install"
self?.progressIndicator.isHidden = false
self?.statusLabel.stringValue = UpdateStatus.install.rawValue
self?.statusLabel.stringValue = UpdateStatus.install
.rawValue
self?.updateButton.action = #selector(self!.installUpdate)
case .checkFailed:
self?.statusLabel.stringValue = UpdateStatus.checkFailed.rawValue
self?.statusLabel.stringValue = UpdateStatus.checkFailed
.rawValue
case .installFailed:
self?.statusLabel.stringValue = UpdateStatus.installFailed.rawValue
self?.statusLabel.stringValue = UpdateStatus.installFailed
.rawValue
self?.updateButton.action = #selector(self!.checkForUpdate)
}
}
@@ -241,8 +262,13 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
self?.subStatusLabel.isHidden = false
self?.changelogLabel.isHidden = false
for (i, change) in model.changes.enumerated() {
(self?.changelogLabel.documentView as? NSTextView)?.string += "\u{2022} \(change)"
if i < model.changes.count-1 { (self?.changelogLabel.documentView as? NSTextView)?.string += "\n" }
(self?.changelogLabel.documentView as?
NSTextView)?.string +=
"\u{2022} \(change)"
if i < model.changes.count-1 {
(self?.changelogLabel.documentView as?
NSTextView)?.string += "\n"
}
}
}
}

View File

@@ -20,7 +20,8 @@ FRAMEWORKS = -framework AppKit
$(EXEC): ./arm64/$(EXEC) ./x86_64/$(EXEC)
lipo -create -output $(EXEC) $^ && \
codesign -s ${DEVELOPER_ID} -f --timestamp -o runtime $(EXEC)
codesign -s ${APPLE_DEVELOPER_ID_APPLICATION} -f --timestamp \
-o runtime $(EXEC)
all: $(EXEC)