This commit is contained in:
2025-03-07 16:54:08 -08:00
parent d31763e66f
commit c846fc5a15
11 changed files with 136 additions and 287 deletions

View File

@@ -1,10 +1,3 @@
//
// AboutLicenseView.swift
// CmdBar
//
// Created by Igor Kolokolnikov on 8/16/23.
//
import AppKit import AppKit
// MARK: - Constants // MARK: - Constants
@@ -29,10 +22,8 @@ enum Strings {
class AboutViewController: NSViewController, NSTextFieldDelegate { class AboutViewController: NSViewController, NSTextFieldDelegate {
// MARK: - Views // MARK: - Views
private var appIconImage: NSImageView = { private var appIconImage: NSImageView = {
//let image = NSImageView(image: NSApp.applicationIconImage)
let image = NSImageView() let image = NSImageView()
image.image = NSWorkspace.shared image.image = NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath)
.icon(forFile: Bundle.main.bundlePath)
image.imageScaling = .scaleAxesIndependently image.imageScaling = .scaleAxesIndependently
image.translatesAutoresizingMaskIntoConstraints = false image.translatesAutoresizingMaskIntoConstraints = false
return image return image
@@ -40,16 +31,12 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
private var appNameLabel: NSTextField = { private var appNameLabel: NSTextField = {
let textField = NSTextField() let textField = NSTextField()
textField.stringValue = textField.stringValue = (Bundle.main.infoDictionary?["CFBundleName"] as? String) ?? "NOT FOUND"
(Bundle.main.infoDictionary?["CFBundleName"] as? String) ??
"NOT FOUND"
textField.isEditable = false textField.isEditable = false
textField.isBezeled = false textField.isBezeled = false
textField.drawsBackground = false textField.drawsBackground = false
textField.alignment = .center textField.alignment = .center
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .title1).pointSize, weight: .bold)
.preferredFontDescriptor(forTextStyle: .title1).pointSize,
weight: .bold)
textField.translatesAutoresizingMaskIntoConstraints = false textField.translatesAutoresizingMaskIntoConstraints = false
return textField return textField
}() }()
@@ -62,9 +49,7 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
textField.drawsBackground = false textField.drawsBackground = false
textField.alignment = .center textField.alignment = .center
textField.textColor = NSColor.systemGray textField.textColor = NSColor.systemGray
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .subheadline).pointSize, weight: .regular)
.preferredFontDescriptor(forTextStyle: .subheadline).pointSize,
weight: .regular)
textField.translatesAutoresizingMaskIntoConstraints = false textField.translatesAutoresizingMaskIntoConstraints = false
return textField return textField
}() }()
@@ -79,9 +64,7 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
textField.drawsBackground = false textField.drawsBackground = false
textField.alignment = .center textField.alignment = .center
textField.textColor = NSColor.systemGray textField.textColor = NSColor.systemGray
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .subheadline).pointSize, weight: .regular)
.preferredFontDescriptor(forTextStyle: .subheadline).pointSize,
weight: .regular)
textField.translatesAutoresizingMaskIntoConstraints = false textField.translatesAutoresizingMaskIntoConstraints = false
return textField return textField
}() }()
@@ -160,68 +143,39 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
// App image. // App image.
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
appIconImage.widthAnchor.constraint(equalToConstant: 100), appIconImage.widthAnchor.constraint(equalToConstant: 100),
appIconImage.heightAnchor appIconImage.heightAnchor.constraint(equalTo: appIconImage.widthAnchor, multiplier: 1),
.constraint(equalTo: appIconImage.widthAnchor, appIconImage.topAnchor.constraint(equalTo: view.topAnchor, constant: ViewConstants.spacing20),
multiplier: 1), appIconImage.centerXAnchor.constraint(equalTo: view.centerXAnchor),
appIconImage.topAnchor
.constraint(equalTo: view.topAnchor,
constant: ViewConstants.spacing20),
appIconImage.centerXAnchor
.constraint(equalTo: view.centerXAnchor),
]) ])
// Title // Title
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
appNameLabel.topAnchor appNameLabel.topAnchor.constraint(equalTo: appIconImage.bottomAnchor, constant: ViewConstants.spacing20),
.constraint(equalTo: appIconImage.bottomAnchor, appNameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
constant: ViewConstants.spacing20),
appNameLabel.centerXAnchor
.constraint(equalTo: view.centerXAnchor),
versionLabel.topAnchor versionLabel.topAnchor.constraint(equalTo: appNameLabel.bottomAnchor, constant: ViewConstants.spacing2),
.constraint(equalTo: appNameLabel.bottomAnchor, versionLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
constant: ViewConstants.spacing2),
versionLabel.centerXAnchor
.constraint(equalTo: view.centerXAnchor),
copyrightLabel.topAnchor copyrightLabel.topAnchor.constraint(equalTo: versionLabel.bottomAnchor, constant: ViewConstants.spacing10),
.constraint(equalTo: versionLabel.bottomAnchor, copyrightLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
constant: ViewConstants.spacing10),
copyrightLabel.centerXAnchor
.constraint(equalTo: view.centerXAnchor),
]) ])
// Buttons // Buttons
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
buttonsContainer.topAnchor buttonsContainer.topAnchor.constraint(equalTo: copyrightLabel.bottomAnchor, constant: ViewConstants.spacing20),
.constraint(equalTo: copyrightLabel.bottomAnchor, buttonsContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -ViewConstants.spacing20),
constant: ViewConstants.spacing20), buttonsContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor),
buttonsContainer.bottomAnchor
.constraint(equalTo: view.bottomAnchor,
constant: -ViewConstants.spacing20),
buttonsContainer.centerXAnchor
.constraint(equalTo: view.centerXAnchor),
privacyButton.topAnchor privacyButton.topAnchor.constraint(equalTo: buttonsContainer.topAnchor),
.constraint(equalTo: buttonsContainer.topAnchor), privacyButton.bottomAnchor.constraint(equalTo: buttonsContainer.bottomAnchor),
privacyButton.bottomAnchor privacyButton.leadingAnchor.constraint(equalTo: buttonsContainer.leadingAnchor),
.constraint(equalTo: buttonsContainer.bottomAnchor),
privacyButton.leadingAnchor
.constraint(equalTo: buttonsContainer.leadingAnchor),
documentationButton.firstBaselineAnchor documentationButton.firstBaselineAnchor.constraint(equalTo: privacyButton.firstBaselineAnchor),
.constraint(equalTo: privacyButton.firstBaselineAnchor), documentationButton.leadingAnchor.constraint(equalTo: privacyButton.trailingAnchor, constant: ViewConstants.spacing10),
documentationButton.leadingAnchor
.constraint(equalTo: privacyButton.trailingAnchor,
constant: ViewConstants.spacing10),
websiteButton.firstBaselineAnchor websiteButton.firstBaselineAnchor.constraint(equalTo: privacyButton.firstBaselineAnchor),
.constraint(equalTo: privacyButton.firstBaselineAnchor), websiteButton.leadingAnchor.constraint(equalTo: documentationButton.trailingAnchor, constant: ViewConstants.spacing10),
websiteButton.leadingAnchor websiteButton.trailingAnchor.constraint(equalTo: buttonsContainer.trailingAnchor),
.constraint(equalTo: documentationButton.trailingAnchor,
constant: ViewConstants.spacing10),
websiteButton.trailingAnchor
.constraint(equalTo: buttonsContainer.trailingAnchor),
]) ])
} }

View File

@@ -22,11 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// MARK: - Notifications // MARK: - Notifications
private func setupNotifications() { private func setupNotifications() {
NSWorkspace.shared.notificationCenter.addObserver( NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(wakeUp), name: NSWorkspace.didWakeNotification, object: nil)
self,
selector: #selector(wakeUp),
name: NSWorkspace.didWakeNotification,
object: nil)
} }
@objc private func wakeUp() { @objc private func wakeUp() {
@@ -47,7 +43,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@objc func checkForUpdates() { @objc func checkForUpdates() {
NSApplication.shared.activate(ignoringOtherApps: true) NSApplication.shared.activate(ignoringOtherApps: true)
updateWindow.makeKeyAndOrderFront(nil) updateWindow.makeKeyAndOrderFront(nil)
updateWindow.center()
} }
// MARK: - Setup About Window // MARK: - Setup About Window

View File

@@ -57,11 +57,12 @@ final class CBMenuBarItem: NSObject, NSWindowDelegate {
return event return event
} }
// On click and drag, the button should panel should desappear and // On click and drag, the button should panel should desappear and un-highlight.
// un-highlight.
localDragEventMonitor = LocalEventMonitor(mask: [.leftMouseDragged, .keyDown]) { [weak self] event in localDragEventMonitor = LocalEventMonitor(mask: [.leftMouseDragged, .keyDown]) { [weak self] event in
let modifiers = event.modifierFlags.rawValue
if let panel = self?.panel, panel.isKeyWindow { if let panel = self?.panel, panel.isKeyWindow {
if event.modifierFlags.contains(.command) && event.type == .leftMouseDragged { if modsContains(keys: OSCmd, in: modifiers) && event.type == .leftMouseDragged {
self?.panel.resignKey() self?.panel.resignKey()
} }
} }

View File

@@ -5,8 +5,7 @@ class CmdFile {
private(set) var lastExec = Int(Date().timeIntervalSince1970) private(set) var lastExec = Int(Date().timeIntervalSince1970)
var untilNextExec: Int { var untilNextExec: Int {
get { get {
let res = reloadTime - let res = reloadTime - (Int(Date().timeIntervalSince1970) - lastExec)
(Int(Date().timeIntervalSince1970) - lastExec)
if res <= 0 { return reloadTime } if res <= 0 { return reloadTime }
return res return res
} }
@@ -41,13 +40,10 @@ class CmdFile {
let execResult = execute(atPath: url) let execResult = execute(atPath: url)
DispatchQueue.main.async { DispatchQueue.main.async {
if execResult.title.isEmpty { if execResult.title.isEmpty {
self?.statusItem.setImage( self?.statusItem.setImage(title: "exclamationmark.triangle.fill", description: "Warning")
title: "exclamationmark.triangle.fill",
description: "Warning")
self?.statusItem.setTitle("") self?.statusItem.setTitle("")
} else { } else {
self?.statusItem.setImage(title: nil, self?.statusItem.setImage(title: nil, description: "")
description: "")
self?.statusItem.setTitle(execResult.0) self?.statusItem.setTitle(execResult.0)
} }
self?.statusItem.setContents(to: execResult.body) self?.statusItem.setContents(to: execResult.body)
@@ -106,31 +102,23 @@ class CmdManager {
private func getPaths() { private func getPaths() {
let manager = FileManager.default let manager = FileManager.default
let path = manager.homeDirectoryForCurrentUser let path = manager.homeDirectoryForCurrentUser.appending(path: ".cmdbar")
.appending(path: ".cmdbar") if let contents = try? manager.contentsOfDirectory(atPath: path.path()) {
if let contents = try? manager
.contentsOfDirectory(atPath: path.path()) {
for content in contents { for content in contents {
let filePath = path.appending(path: content) let filePath = path.appending(path: content)
let domains = filePath.path().components(separatedBy: ".") let domains = filePath.path().components(separatedBy: ".")
if (isValidTimeFormat(domains[domains.count - 1])) { if (isValidTimeFormat(domains[domains.count - 1])) {
paths.append(CmdFile(url: filePath, paths.append(CmdFile(url: filePath, reloadTime: intervalToSeconds(from: domains[domains.count - 1])))
reloadTime: intervalToSeconds( } else if domains.last == "sh" && isValidTimeFormat(domains[domains.count - 2]) {
from: domains[domains.count - 1]))) paths.append(CmdFile(url: filePath, reloadTime: intervalToSeconds(from: domains[domains.count - 2])))
} 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() { private func setupMenu() {
defaultStatusItem = NSStatusBar.system defaultStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
.statusItem(withLength: NSStatusItem.variableLength)
if let btn = defaultStatusItem?.button { if let btn = defaultStatusItem?.button {
let image = NSApp.applicationIconImage let image = NSApp.applicationIconImage
image!.size = NSSize(width: 22, height: 22) image!.size = NSSize(width: 22, height: 22)
@@ -138,21 +126,13 @@ class CmdManager {
} }
guard defaultStatusItem != nil else { return } guard defaultStatusItem != nil else { return }
let menu = NSMenu() let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Reload", menu.addItem(NSMenuItem(title: "Reload", action: #selector(reloadWidgets), keyEquivalent: ""))
action: #selector(reloadWidgets),
keyEquivalent: ""))
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "About", menu.addItem(NSMenuItem(title: "About", action: #selector(showAbout), keyEquivalent: ""))
action: #selector(showAbout),
keyEquivalent: ""))
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Check for Updates...", menu.addItem(NSMenuItem(title: "Check for Updates...", action: #selector(checkForUpdates), keyEquivalent: ""))
action: #selector(checkForUpdates),
keyEquivalent: ""))
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
action: #selector(NSApplication.terminate(_:)),
keyEquivalent: "q"))
defaultStatusItem!.menu = menu defaultStatusItem!.menu = menu
} }

View File

@@ -132,11 +132,7 @@ class CmdViewController: NSViewController {
private var updateButton: NSButton = { private var updateButton: NSButton = {
let button = NSButton() let button = NSButton()
button.image = NSImage(systemSymbolName: "arrow.down.circle.fill", accessibilityDescription: nil)? button.image = NSImage(systemSymbolName: "arrow.down.circle.fill", accessibilityDescription: nil)?.withSymbolConfiguration(NSImage.SymbolConfiguration(textStyle: .title2, scale: .large) .applying(.init(paletteColors: [.white, .systemGreen])))
.withSymbolConfiguration(
NSImage.SymbolConfiguration(textStyle: .title2, scale: .large)
.applying(.init(paletteColors: [.white, .systemGreen]))
)
button.isBordered = false button.isBordered = false
button.action = #selector(updateApp) button.action = #selector(updateApp)
button.sizeToFit() button.sizeToFit()
@@ -327,17 +323,14 @@ class CmdViewController: NSViewController {
} }
private func resetReloadButton() { private func resetReloadButton() {
reloadButton.image = systemImage("arrow.clockwise.circle.fill", reloadButton.image = systemImage("arrow.clockwise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemBlue]))
.title2, .large,
.init(paletteColors: [.white,
.systemBlue]))
reloadButton.action = #selector(reloadWidget) reloadButton.action = #selector(reloadWidget)
reloadButton.toolTip = "Reload Current" reloadButton.toolTip = "Reload Current"
} }
private func startReloadTextTimer() { private func startReloadTextTimer() {
timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global()) timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
timer?.schedule(deadline: .now(), repeating: .seconds(1)) timer?.schedule(deadline: .now(), repeating: .milliseconds(500))
timer?.setEventHandler { [weak self] in timer?.setEventHandler { [weak self] in
DispatchQueue.main.async { DispatchQueue.main.async {
guard let controller = self, let file = controller.cmdFile else { return } guard let controller = self, let file = controller.cmdFile else { return }
@@ -349,24 +342,16 @@ class CmdViewController: NSViewController {
} }
private func setupKeyEvents() { private func setupKeyEvents() {
keyboardEvents = LocalEventMonitor(mask: [.flagsChanged, .keyDown], keyboardEvents = LocalEventMonitor(mask: [.flagsChanged, .keyDown], handler: { [weak self] event in
handler:
{ [weak self] event in
let modifiers = event.modifierFlags.rawValue let modifiers = event.modifierFlags.rawValue
let key = event.keyCode let key = event.keyCode
if modsContains(keys: OSShift, in: modifiers) { if modsContains(keys: OSShift, in: modifiers) {
self?.reloadButton.image = self?.reloadButton.image = systemImage("arrow.clockwise.circle.fill", .title2, .large,.init(paletteColors: [.white, .systemCyan]))
systemImage("arrow.clockwise.circle.fill", .title2,
.large,.init(paletteColors: [.white,
.systemCyan]))
self?.reloadButton.action = #selector(self?.reloadWidgets) self?.reloadButton.action = #selector(self?.reloadWidgets)
self?.reloadButton.toolTip = "Reload All" self?.reloadButton.toolTip = "Reload All"
} else { } else {
self?.reloadButton.image = self?.reloadButton.image = systemImage("arrow.clockwise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemBlue]))
systemImage("arrow.clockwise.circle.fill", .title2,
.large, .init(paletteColors: [.white,
.systemBlue]))
self?.reloadButton.action = #selector(self?.reloadWidget) self?.reloadButton.action = #selector(self?.reloadWidget)
self?.reloadButton.toolTip = "Reload Current" self?.reloadButton.toolTip = "Reload Current"
} }
@@ -383,10 +368,8 @@ class CmdViewController: NSViewController {
} }
} else if modsContains(keys: OSCmd | OSShift, in: modifiers) { } else if modsContains(keys: OSCmd | OSShift, in: modifiers) {
if key == kVK_ANSI_R { if key == kVK_ANSI_R {
// NOTE: We need to resign the window in order to // NOTE: We need to resign the window in order to prevent the program from intercepting the events, which result in sounds
// prevent the program from intercepting the // beign made for missing performKeyEquivalent.
// events, which result in sounds beign made for
// missing performKeyEquivalent.
self?.view.window?.resignKey() self?.view.window?.resignKey()
self?.reloadWidgets() self?.reloadWidgets()
} }

View File

@@ -17,7 +17,7 @@ func modsContainsNone(in modifiers: UInt) -> Bool {
enum ViewConstants { enum ViewConstants {
static let spacing2: CGFloat = 2 static let spacing2: CGFloat = 2
static let spacing5: CGFloat = 2 static let spacing5: CGFloat = 5
static let spacing10: CGFloat = 10 static let spacing10: CGFloat = 10
static let spacing15: CGFloat = 15 static let spacing15: CGFloat = 15
static let spacing20: CGFloat = 20 static let spacing20: CGFloat = 20
@@ -27,11 +27,6 @@ enum ViewConstants {
static let spacing40: CGFloat = 40 static let spacing40: CGFloat = 40
} }
fileprivate extension Notification.Name {
static let beginMenuTracking = Notification.Name("com.apple.HIToolbox.beginMenuTrackingNotification")
static let endMenuTracking = Notification.Name("com.apple.HIToolbox.endMenuTrackingNotification")
}
#if(DEBUG) #if(DEBUG)
struct EndpointConstants { struct EndpointConstants {
static var versionURL = "http://localhost:8081/version" static var versionURL = "http://localhost:8081/version"
@@ -181,15 +176,9 @@ func getColors(_ light: NSColor, _ dark: NSColor, for appearance: NSAppearance)
} }
// ex: systemImage("sunrise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemOrange])) // ex: systemImage("sunrise.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemOrange]))
func systemImage(_ name: String, _ size: NSFont.TextStyle, func systemImage(_ name: String, _ size: NSFont.TextStyle, _ scale: NSImage.SymbolScale, _ configuration: NSImage.SymbolConfiguration) -> NSImage? {
_ scale: NSImage.SymbolScale,
_ configuration: NSImage.SymbolConfiguration) -> NSImage?
{
return NSImage(systemSymbolName: name, accessibilityDescription: nil)? return NSImage(systemSymbolName: name, accessibilityDescription: nil)?
.withSymbolConfiguration( .withSymbolConfiguration(NSImage.SymbolConfiguration(textStyle: size, scale: scale).applying(configuration))
NSImage.SymbolConfiguration(textStyle: size, scale: scale)
.applying(configuration)
)
} }
func openLink(_ url: String) { func openLink(_ url: String) {
@@ -197,6 +186,11 @@ func openLink(_ url: String) {
NSWorkspace.shared.open(url) NSWorkspace.shared.open(url)
} }
fileprivate extension Notification.Name {
static let beginMenuTracking = Notification.Name("com.apple.HIToolbox.beginMenuTrackingNotification")
static let endMenuTracking = Notification.Name("com.apple.HIToolbox.endMenuTrackingNotification")
}
func persistMenuBar(_ state: Bool) { func persistMenuBar(_ state: Bool) {
if state { if state {
DistributedNotificationCenter.default().post(name: .beginMenuTracking, object: nil) DistributedNotificationCenter.default().post(name: .beginMenuTracking, object: nil)

View File

@@ -7,12 +7,9 @@ XCODE_PATH = $(shell xcode-select --print-path)
EXEC = CmdBar EXEC = CmdBar
SRCMODULES = UpdateManager.swift AboutViewController.swift \ SRCMODULES = UpdateManager.swift AboutViewController.swift AppDelegate.swift CBMenuBarItem.swift CircularProgressView.swift \
AppDelegate.swift CBMenuBarItem.swift CircularProgressView.swift \ CmdManager.swift CmdViewController.swift CopyableTextView.swift EditableNSTextField.swift EventMonitor.swift Helpers.swift \
CmdManager.swift CmdViewController.swift CopyableTextView.swift \ MenulessWindow.swift PopoverPanel.swift QuietView.swift UpdateViewController.swift main.swift
EditableNSTextField.swift EventMonitor.swift Helpers.swift \
MenulessWindow.swift PopoverPanel.swift QuietView.swift \
UpdateViewController.swift main.swift
ARMOBJMODULES = $(addprefix ./arm64/,$(SRCMODULES:.swift=.o)) ARMOBJMODULES = $(addprefix ./arm64/,$(SRCMODULES:.swift=.o))
X86OBJMODULES = $(addprefix ./x86_64/,$(SRCMODULES:.swift=.o)) X86OBJMODULES = $(addprefix ./x86_64/,$(SRCMODULES:.swift=.o))
@@ -22,51 +19,40 @@ FRAMEWORKS = -framework AppKit -framework ServiceManagement
LIBS = -lzip LIBS = -lzip
zip: zip:
@$(MAKE) -C libs/Zip/Zip FLAGS=$(FLAGS) CFLAGS=$(CFLAGS) \ @$(MAKE) -C libs/Zip/Zip FLAGS=$(FLAGS) CFLAGS=$(CFLAGS) MACOS_VERSION=$(MACOS_VERSION) all
MACOS_VERSION=$(MACOS_VERSION) all
cmdbar_updater: cmdbar_updater:
@$(MAKE) -C updater FLAGS=$(FLAGS) all @$(MAKE) -C updater FLAGS=$(FLAGS) all
@cp updater/$@ . @cp updater/$@ .
./arm64/%.o: %.swift ./arm64/%.o: %.swift
swift -frontend -c $(if $(DEBUG), -D DEBUG,) \ swift -frontend -c $(if $(DEBUG), -D DEBUG,) -target arm64-apple-macos$(MACOS_VERSION) $(FLAGS) \
-target arm64-apple-macos$(MACOS_VERSION) $(FLAGS) \
-I./libs/Zip/Zip -I./libs/Zip/Zip/arm64 -L./libs/Zip/Zip/arm64 \ -I./libs/Zip/Zip -I./libs/Zip/Zip/arm64 -L./libs/Zip/Zip/arm64 \
-primary-file $< $(filter-out $<, $(SRCMODULES)) $(LIBS) \ -primary-file $< $(filter-out $<, $(SRCMODULES)) $(LIBS) -sdk $(SDK) -module-name $(EXEC) \
-sdk $(SDK) -module-name $(EXEC) -o $@ -emit-module && touch $@ -o $@ -emit-module && touch $@
ifdef UNIVERSAL ifdef UNIVERSAL
./x86_64/%.o: %.swift ./x86_64/%.o: %.swift
@swift -frontend -c $(if $(DEBUG), -D DEBUG,) \ @swift -frontend -c $(if $(DEBUG), -D DEBUG,) -target x86_64-apple-macos$(MACOS_VERSION) $(FLAGS) \
-target x86_64-apple-macos$(MACOS_VERSION) $(FLAGS) \
-I./libs/Zip/Zip -I./libs/Zip/Zip/x86_64 -L./libs/Zip/Zip/x86_64 \ -I./libs/Zip/Zip -I./libs/Zip/Zip/x86_64 -L./libs/Zip/Zip/x86_64 \
-primary-file $< $(filter-out $<, $(SRCMODULES)) $(LIBS) \ -primary-file $< $(filter-out $<, $(SRCMODULES)) $(LIBS) -sdk $(SDK) -module-name $(EXEC) \
-sdk $(SDK) -module-name $(EXEC) -o $@ -emit-module && touch $@ -o $@ -emit-module && touch $@
endif endif
./arm64/$(EXEC): $(ARMOBJMODULES) ./arm64/$(EXEC): $(ARMOBJMODULES)
@ld -syslibroot $(SDK) -lSystem -arch arm64 -macos_version_min \ @ld -syslibroot $(SDK) -lSystem -arch arm64 -macos_version_min $(MACOS_VERSION).0 \
$(MACOS_VERSION).0 \
/Library/Developer/CommandLineTools/usr/lib/swift/macosx/libswiftCompatibilityPacks.a \ /Library/Developer/CommandLineTools/usr/lib/swift/macosx/libswiftCompatibilityPacks.a \
-sectcreate __TEXT __info_plist Info.plist \ -sectcreate __TEXT __info_plist Info.plist -L /Library/Developer/CommandLineTools/usr/lib/swift/macosx -L \
-L /Library/Developer/CommandLineTools/usr/lib/swift/macosx -L \ /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/swift -no_objc_category_merging -L $(XCODE_PATH) -rpath \
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/swift \ Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx ./arm64/main.o $(filter-out ./arm64/main.o, $(ARMOBJMODULES)) \
-no_objc_category_merging -L $(XCODE_PATH) -rpath \
Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
./arm64/main.o $(filter-out ./arm64/main.o, $(ARMOBJMODULES)) \
./libs/Zip/Zip/arm64/libzip.a -o $@ ./libs/Zip/Zip/arm64/libzip.a -o $@
ifdef UNIVERSAL ifdef UNIVERSAL
./x86_64/$(EXEC): $(X86OBJMODULES) ./x86_64/$(EXEC): $(X86OBJMODULES)
@ld -syslibroot $(SDK) -lSystem -arch x86_64 -macos_version_min \ @ld -syslibroot $(SDK) -lSystem -arch x86_64 -macos_version_min $(MACOS_VERSION).0 \
$(MACOS_VERSION).0 \
/Library/Developer/CommandLineTools/usr/lib/swift/macosx/libswiftCompatibilityPacks.a \ /Library/Developer/CommandLineTools/usr/lib/swift/macosx/libswiftCompatibilityPacks.a \
-sectcreate __TEXT __info_plist Info.plist \ -sectcreate __TEXT __info_plist Info.plist -L /Library/Developer/CommandLineTools/usr/lib/swift/macosx -L \
-L /Library/Developer/CommandLineTools/usr/lib/swift/macosx -L \ /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/swift -no_objc_category_merging -L $(XCODE_PATH) -rpath \
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/swift \ Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx ./x86_64/main.o $(filter-out ./x86_64/main.o, $(X86OBJMODULES)) \
-no_objc_category_merging -L $(XCODE_PATH) -rpath \
Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
./x86_64/main.o $(filter-out ./x86_64/main.o, $(X86OBJMODULES)) \
./libs/Zip/Zip/x86_64/libzip.a -o $@ ./libs/Zip/Zip/x86_64/libzip.a -o $@
endif endif
@@ -86,16 +72,19 @@ $(EXEC).app: $(EXEC)
cp resources/AppIcon.icns $@/Contents/Resources/ && \ cp resources/AppIcon.icns $@/Contents/Resources/ && \
cp $(EXEC) $@/Contents/MacOS/ && \ cp $(EXEC) $@/Contents/MacOS/ && \
cp cmdbar_updater $@/Contents/MacOS/ && \ cp cmdbar_updater $@/Contents/MacOS/ && \
$(if $(DEBUG), \ $(if $(DEBUG), codesign --entitlements Grapp.entitlements -s ${APPLE_DEVELOPMENT} -f --timestamp -o runtime $(EXEC).app, \
codesign --entitlements Grapp.entitlements \ codesign -s ${APPLE_DEVELOPER_ID_APPLICATION} -f --timestamp -o runtime $(EXEC).app)
-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 all: zip cmdbar_updater $(EXEC).app
run: all clear:
@open $(EXEC).app clear
kill:
-pkill $(EXEC)
run: clear kill all
./$(EXEC)
clean: clean:
rm -rf $(EXEC) $(EXEC).app cmdbar_updater arm64 x86_64 rm -rf $(EXEC) $(EXEC).app cmdbar_updater arm64 x86_64

View File

@@ -23,10 +23,9 @@ class MenulessWindow: NSWindow {
let key = event.keyCode let key = event.keyCode
if event.type == NSEvent.EventType.keyDown { if event.type == NSEvent.EventType.keyDown {
if modsContains(keys: OSCmd, in: modifiers) && if modsContains(keys: OSCmd, in: modifiers) && key == kVK_ANSI_W {
key == kVK_ANSI_W
{
performClose(nil) performClose(nil)
return true
} }
} }

View File

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

View File

@@ -1,10 +1,3 @@
//
// UpdateManager.swift
// CmdBar
//
// Created by Igor Kolokolnikov on 5/23/24.
//
import Foundation import Foundation
import Zip import Zip
@@ -27,9 +20,10 @@ final class UpdateManager {
func currentVersion() -> Double? { func currentVersion() -> Double? {
guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return nil } guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return nil }
if let version = Double(version) { if let version = Double(version)
return version { return version }
} else { return nil } else
{ return nil }
} }
func latestVersion() async -> VersionModel? { func latestVersion() async -> VersionModel? {

View File

@@ -17,8 +17,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
private var appIconImage: NSImageView = { private var appIconImage: NSImageView = {
let image = NSImageView() let image = NSImageView()
image.image = NSWorkspace.shared image.image = NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath)
.icon(forFile: Bundle.main.bundlePath)
image.imageScaling = .scaleAxesIndependently image.imageScaling = .scaleAxesIndependently
image.translatesAutoresizingMaskIntoConstraints = false image.translatesAutoresizingMaskIntoConstraints = false
return image return image
@@ -31,9 +30,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
textField.drawsBackground = false textField.drawsBackground = false
textField.alignment = .left textField.alignment = .left
textField.textColor = NSColor(name: nil) { getColors(.black, .white, for: $0) } textField.textColor = NSColor(name: nil) { getColors(.black, .white, for: $0) }
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .bold)
.preferredFontDescriptor(forTextStyle: .body).pointSize,
weight: .bold)
textField.translatesAutoresizingMaskIntoConstraints = false textField.translatesAutoresizingMaskIntoConstraints = false
return textField return textField
}() }()
@@ -47,9 +44,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
textField.drawsBackground = false textField.drawsBackground = false
textField.alignment = .left textField.alignment = .left
textField.textColor = .gray textField.textColor = .gray
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .bold)
.preferredFontDescriptor(forTextStyle: .body).pointSize,
weight: .bold)
textField.translatesAutoresizingMaskIntoConstraints = false textField.translatesAutoresizingMaskIntoConstraints = false
return textField return textField
}() }()
@@ -61,9 +56,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
(scrollableTextView.documentView as? NSTextView)?.isEditable = false (scrollableTextView.documentView as? NSTextView)?.isEditable = false
(scrollableTextView.documentView as? NSTextView)?.textColor = .gray (scrollableTextView.documentView as? NSTextView)?.textColor = .gray
(scrollableTextView.documentView as? NSTextView)?.font = (scrollableTextView.documentView as? NSTextView)?.font =
NSFont.systemFont(ofSize: NSFontDescriptor NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .regular)
.preferredFontDescriptor(forTextStyle: .body).pointSize,
weight: .regular)
scrollableTextView.translatesAutoresizingMaskIntoConstraints = false scrollableTextView.translatesAutoresizingMaskIntoConstraints = false
return scrollableTextView return scrollableTextView
}() }()
@@ -132,46 +125,25 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
appIconImage.widthAnchor.constraint(equalToConstant: 70), appIconImage.widthAnchor.constraint(equalToConstant: 70),
appIconImage.heightAnchor appIconImage.heightAnchor.constraint(equalTo: appIconImage.widthAnchor,multiplier: 1),
.constraint(equalTo: appIconImage.widthAnchor, appIconImage.topAnchor.constraint(equalTo: view.topAnchor, constant: ViewConstants.spacing10),
multiplier: 1), 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 statusLabel.topAnchor.constraint(equalTo: appIconImage.topAnchor, constant: ViewConstants.spacing10),
.constraint(equalTo: appIconImage.topAnchor, statusLabel.leadingAnchor.constraint(equalTo: appIconImage.trailingAnchor, constant: ViewConstants.spacing10),
constant: ViewConstants.spacing10),
statusLabel.leadingAnchor
.constraint(equalTo: appIconImage.trailingAnchor,
constant: ViewConstants.spacing10),
subStatusLabel.topAnchor subStatusLabel.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: ViewConstants.spacing10),
.constraint(equalTo: statusLabel.bottomAnchor, subStatusLabel.leadingAnchor.constraint(equalTo: statusLabel.leadingAnchor),
constant: ViewConstants.spacing10),
subStatusLabel.leadingAnchor
.constraint(equalTo: statusLabel.leadingAnchor),
changelogLabel.widthAnchor changelogLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor),
.constraint(equalTo: stackView.widthAnchor),
changelogLabel.heightAnchor.constraint(equalToConstant: 150), changelogLabel.heightAnchor.constraint(equalToConstant: 150),
stackView.topAnchor stackView.topAnchor.constraint(equalTo: subStatusLabel.bottomAnchor, constant: ViewConstants.spacing2),
.constraint(equalTo: subStatusLabel.bottomAnchor, stackView.leadingAnchor.constraint(equalTo: statusLabel.leadingAnchor),
constant: ViewConstants.spacing2), view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: ViewConstants.spacing20),
stackView.leadingAnchor view.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: ViewConstants.spacing20),
.constraint(equalTo: statusLabel.leadingAnchor),
view.trailingAnchor
.constraint(equalTo: stackView.trailingAnchor,
constant: ViewConstants.spacing20),
view.bottomAnchor
.constraint(equalTo: stackView.bottomAnchor,
constant: ViewConstants.spacing20),
buttonsStackView.heightAnchor buttonsStackView.heightAnchor.constraint(equalTo: updateButton.heightAnchor),
.constraint(equalTo: updateButton.heightAnchor),
]) ])
} }
@@ -185,6 +157,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
override func viewDidAppear() { override func viewDidAppear() {
super.viewDidAppear() super.viewDidAppear()
view.window?.center()
(changelogLabel.documentView as? NSTextView)?.string = "" (changelogLabel.documentView as? NSTextView)?.string = ""
subStatusLabel.isHidden = true subStatusLabel.isHidden = true
changelogLabel.isHidden = true changelogLabel.isHidden = true
@@ -205,44 +178,36 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
case .check: case .check:
break break
case .latest: case .latest:
self?.statusLabel.stringValue = UpdateStatus.latest self?.statusLabel.stringValue = UpdateStatus.latest.rawValue
.rawValue
break break
case .checking: case .checking:
self?.statusLabel.stringValue = UpdateStatus.checking self?.statusLabel.stringValue = UpdateStatus.checking.rawValue
.rawValue
self?.updateButton.keyEquivalent = "" self?.updateButton.keyEquivalent = ""
self?.updateButton.isEnabled = false self?.updateButton.isEnabled = false
case .available: case .available:
self?.updateButton.title = "Download" self?.updateButton.title = "Download"
self?.updateButton.keyEquivalent = "" self?.updateButton.keyEquivalent = ""
self?.statusLabel.stringValue = UpdateStatus.available self?.statusLabel.stringValue = UpdateStatus.available.rawValue
.rawValue
self?.updateButton.action = #selector(self!.downloadUpdate) self?.updateButton.action = #selector(self!.downloadUpdate)
case .downloading: case .downloading:
self?.updateButton.isEnabled = false self?.updateButton.isEnabled = false
self?.updateButton.title = "Install" self?.updateButton.title = "Install"
self?.progressIndicator.isHidden = false self?.progressIndicator.isHidden = false
self?.statusLabel.stringValue = UpdateStatus.downloading self?.statusLabel.stringValue = UpdateStatus.downloading.rawValue
.rawValue
case .extracting: case .extracting:
self?.updateButton.isEnabled = false self?.updateButton.isEnabled = false
self?.updateButton.title = "Install" self?.updateButton.title = "Install"
self?.progressIndicator.isHidden = false self?.progressIndicator.isHidden = false
self?.statusLabel.stringValue = UpdateStatus.extracting self?.statusLabel.stringValue = UpdateStatus.extracting.rawValue
.rawValue
case .install: case .install:
self?.updateButton.title = "Install" self?.updateButton.title = "Install"
self?.progressIndicator.isHidden = false self?.progressIndicator.isHidden = false
self?.statusLabel.stringValue = UpdateStatus.install self?.statusLabel.stringValue = UpdateStatus.install.rawValue
.rawValue
self?.updateButton.action = #selector(self!.installUpdate) self?.updateButton.action = #selector(self!.installUpdate)
case .checkFailed: case .checkFailed:
self?.statusLabel.stringValue = UpdateStatus.checkFailed self?.statusLabel.stringValue = UpdateStatus.checkFailed.rawValue
.rawValue
case .installFailed: case .installFailed:
self?.statusLabel.stringValue = UpdateStatus.installFailed self?.statusLabel.stringValue = UpdateStatus.installFailed.rawValue
.rawValue
self?.updateButton.action = #selector(self!.checkForUpdate) self?.updateButton.action = #selector(self!.checkForUpdate)
} }
} }
@@ -262,12 +227,9 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
self?.subStatusLabel.isHidden = false self?.subStatusLabel.isHidden = false
self?.changelogLabel.isHidden = false self?.changelogLabel.isHidden = false
for (i, change) in model.changes.enumerated() { for (i, change) in model.changes.enumerated() {
(self?.changelogLabel.documentView as? (self?.changelogLabel.documentView as? NSTextView)?.string += "\u{2022} \(change)"
NSTextView)?.string +=
"\u{2022} \(change)"
if i < model.changes.count-1 { if i < model.changes.count-1 {
(self?.changelogLabel.documentView as? (self?.changelogLabel.documentView as? NSTextView)?.string += "\n"
NSTextView)?.string += "\n"
} }
} }
} }