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

View File

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

View File

@@ -57,11 +57,12 @@ final class CBMenuBarItem: NSObject, NSWindowDelegate {
return event
}
// On click and drag, the button should panel should desappear and
// un-highlight.
// On click and drag, the button should panel should desappear and un-highlight.
localDragEventMonitor = LocalEventMonitor(mask: [.leftMouseDragged, .keyDown]) { [weak self] event in
let modifiers = event.modifierFlags.rawValue
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()
}
}

View File

@@ -5,8 +5,7 @@ class CmdFile {
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
}
@@ -41,13 +40,10 @@ 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)
@@ -106,31 +102,23 @@ 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)
@@ -138,21 +126,13 @@ 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
}

View File

@@ -132,11 +132,7 @@ class CmdViewController: NSViewController {
private var updateButton: NSButton = {
let button = NSButton()
button.image = NSImage(systemSymbolName: "arrow.down.circle.fill", accessibilityDescription: nil)?
.withSymbolConfiguration(
NSImage.SymbolConfiguration(textStyle: .title2, scale: .large)
.applying(.init(paletteColors: [.white, .systemGreen]))
)
button.image = NSImage(systemSymbolName: "arrow.down.circle.fill", accessibilityDescription: nil)?.withSymbolConfiguration(NSImage.SymbolConfiguration(textStyle: .title2, scale: .large) .applying(.init(paletteColors: [.white, .systemGreen])))
button.isBordered = false
button.action = #selector(updateApp)
button.sizeToFit()
@@ -327,17 +323,14 @@ 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"
}
private func startReloadTextTimer() {
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
DispatchQueue.main.async {
guard let controller = self, let file = controller.cmdFile else { return }
@@ -349,24 +342,16 @@ 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 key = event.keyCode
if modsContains(keys: OSShift, in: modifiers) {
self?.reloadButton.image =
systemImage("arrow.clockwise.circle.fill", .title2,
.large,.init(paletteColors: [.white,
.systemCyan]))
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"
}
@@ -383,10 +368,8 @@ class CmdViewController: NSViewController {
}
} 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.
// 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()
}

View File

@@ -17,7 +17,7 @@ func modsContainsNone(in modifiers: UInt) -> Bool {
enum ViewConstants {
static let spacing2: CGFloat = 2
static let spacing5: CGFloat = 2
static let spacing5: CGFloat = 5
static let spacing10: CGFloat = 10
static let spacing15: CGFloat = 15
static let spacing20: CGFloat = 20
@@ -27,25 +27,20 @@ enum ViewConstants {
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)
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 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"
}
#else
struct EndpointConstants {
static var versionURL = "http://cmdbar.app/version"
static var appURL = "http://cmdbar.app/download"
static var appURLZip = "http://cmdbar.app/static/app/CmdBar.zip"
static var changelog = "http://cmdbar.app/changelog"
static var versionURL = "http://cmdbar.app/version"
static var appURL = "http://cmdbar.app/download"
static var appURLZip = "http://cmdbar.app/static/app/CmdBar.zip"
static var changelog = "http://cmdbar.app/changelog"
static var validateURL = "https://cmdbar.app/validate"
}
#endif
@@ -181,15 +176,9 @@ 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)
.applying(configuration)
)
.withSymbolConfiguration(NSImage.SymbolConfiguration(textStyle: size, scale: scale).applying(configuration))
}
func openLink(_ url: String) {
@@ -197,6 +186,11 @@ func openLink(_ url: String) {
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) {
if state {
DistributedNotificationCenter.default().post(name: .beginMenuTracking, object: nil)

View File

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

View File

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

View File

@@ -6,8 +6,7 @@ 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
)
@@ -23,8 +22,7 @@ class PopoverPanel: NSPanel {
titlebarAppearsTransparent = true
animationBehavior = .none
collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary,
.transient]
collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary, .transient]
isReleasedWhenClosed = false
hidesOnDeactivate = false

View File

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

View File

@@ -17,8 +17,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
private var appIconImage: NSImageView = {
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
@@ -31,9 +30,7 @@ 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
}()
@@ -47,9 +44,7 @@ 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
}()
@@ -61,9 +56,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
(scrollableTextView.documentView as? NSTextView)?.isEditable = false
(scrollableTextView.documentView as? NSTextView)?.textColor = .gray
(scrollableTextView.documentView as? NSTextView)?.font =
NSFont.systemFont(ofSize: NSFontDescriptor
.preferredFontDescriptor(forTextStyle: .body).pointSize,
weight: .regular)
NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .regular)
scrollableTextView.translatesAutoresizingMaskIntoConstraints = false
return scrollableTextView
}()
@@ -132,46 +125,25 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
NSLayoutConstraint.activate([
appIconImage.widthAnchor.constraint(equalToConstant: 70),
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.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),
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.spacing10),
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),
])
}
@@ -185,6 +157,7 @@ class UpdateViewController: NSViewController, UpdateManagerDelegate {
override func viewDidAppear() {
super.viewDidAppear()
view.window?.center()
(changelogLabel.documentView as? NSTextView)?.string = ""
subStatusLabel.isHidden = true
changelogLabel.isHidden = true
@@ -205,44 +178,36 @@ 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)
}
}
@@ -262,12 +227,9 @@ 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)"
(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 += "\n"
}
}
}