This commit is contained in:
2025-03-07 16:54:27 -08:00
parent bba267f37d
commit 565b9f8254
3 changed files with 66 additions and 27 deletions

View File

@@ -82,7 +82,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
public func fsEventTriggered(_ path: String, _ flags: Int) { public func fsEventTriggered(_ path: String, _ flags: Int) {
if containsFlags(key: kFSEventStreamEventFlagItemCreated, in: flags) || if containsFlags(key: kFSEventStreamEventFlagItemCreated, in: flags) ||
containsFlags(key: kFSEventStreamEventFlagItemRemoved, in: flags) || containsFlags(key: kFSEventStreamEventFlagItemRemoved, in: flags) ||
containsFlags(key: kFSEventStreamEventFlagItemCloned, in: flags) || containsFlags(key: kFSEventStreamEventFlagItemCloned, in: flags) ||
containsFlags(key: kFSEventStreamEventFlagItemRenamed, in: flags) containsFlags(key: kFSEventStreamEventFlagItemRenamed, in: flags)
{ {
for dir in PathManager.shared.paths { for dir in PathManager.shared.paths {

View File

@@ -19,6 +19,21 @@ class ProgramsTableViewCell: NSTableCellView {
private(set) var isEditing = false private(set) var isEditing = false
public var indexLabel: NSTextField = {
let field = NSTextField(labelWithString: "-")
field.alignment = .center
// field.drawsBackground = true
// field.backgroundColor = NSColor.green.withAlphaComponent(0.2)
field.textColor = NSColor.secondaryLabelColor
field.cell?.lineBreakMode = .byTruncatingTail
field.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .caption1).pointSize, weight: .bold)
field.translatesAutoresizingMaskIntoConstraints = false
return field
}()
public var appIconImage: NSImageView = { public var appIconImage: NSImageView = {
let image = NSImageView() let image = NSImageView()
image.image = NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath) image.image = NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath)
@@ -28,38 +43,51 @@ class ProgramsTableViewCell: NSTableCellView {
}() }()
public var titleField: NSTextField = { public var titleField: NSTextField = {
let field = NSTextField(labelWithString: "") let field = NSTextField()
field.textColor = NSColor.secondaryLabelColor field.isBordered = false
field.drawsBackground = false
field.lineBreakMode = .byTruncatingTail field.lineBreakMode = .byTruncatingTail
field.textColor = NSColor.secondaryLabelColor
field.translatesAutoresizingMaskIntoConstraints = false field.translatesAutoresizingMaskIntoConstraints = false
return field return field
}() }()
public var progPathLabel: NSTextField = { public var progPathLabel: NSTextField = {
let textField = NSTextField(labelWithString: "") let field = NSTextField()
textField.cell?.lineBreakMode = .byTruncatingTail field.isBordered = false
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .caption1).pointSize, weight: .medium) field.drawsBackground = false
textField.translatesAutoresizingMaskIntoConstraints = false field.lineBreakMode = .byTruncatingTail
return textField field.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .caption1).pointSize, weight: .medium)
field.translatesAutoresizingMaskIntoConstraints = false
return field
}() }()
override init(frame frameRect: NSRect) { override init(frame frameRect: NSRect) {
super.init(frame: frameRect) super.init(frame: frameRect)
// wantsLayer = true
// layer?.backgroundColor = NSColor.yellow.withAlphaComponent(0.2).cgColor
addSubview(indexLabel)
addSubview(appIconImage) addSubview(appIconImage)
addSubview(titleField) addSubview(titleField)
addSubview(progPathLabel) addSubview(progPathLabel)
// indexLabel.setContentHuggingPriority(.required, for: .horizontal)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
indexLabel.widthAnchor.constraint(equalToConstant: 25),
indexLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
indexLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: ViewConstants.spacing5),
appIconImage.widthAnchor.constraint(equalToConstant: 40), appIconImage.widthAnchor.constraint(equalToConstant: 40),
appIconImage.heightAnchor.constraint(equalToConstant: 40), appIconImage.heightAnchor.constraint(equalToConstant: 40),
appIconImage.topAnchor.constraint(equalTo: topAnchor), appIconImage.topAnchor.constraint(equalTo: topAnchor),
appIconImage.bottomAnchor.constraint(equalTo: bottomAnchor), appIconImage.bottomAnchor.constraint(equalTo: bottomAnchor),
appIconImage.leadingAnchor.constraint(equalTo: leadingAnchor, constant: ViewConstants.spacing5), appIconImage.leadingAnchor.constraint(equalTo: indexLabel.trailingAnchor),
titleField.topAnchor.constraint(equalTo: appIconImage.topAnchor, constant: ViewConstants.spacing2), titleField.topAnchor.constraint(equalTo: appIconImage.topAnchor, constant: ViewConstants.spacing2),
titleField.leadingAnchor.constraint(equalTo: appIconImage.trailingAnchor, constant: ViewConstants.spacing5), titleField.leadingAnchor.constraint(equalTo: appIconImage.trailingAnchor, constant: ViewConstants.spacing5),
titleField.trailingAnchor.constraint(equalTo: trailingAnchor), titleField.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -ViewConstants.spacing5),
progPathLabel.topAnchor.constraint(equalTo: titleField.bottomAnchor), progPathLabel.topAnchor.constraint(equalTo: titleField.bottomAnchor),
progPathLabel.leadingAnchor.constraint(equalTo: titleField.leadingAnchor), progPathLabel.leadingAnchor.constraint(equalTo: titleField.leadingAnchor),

View File

@@ -1,23 +1,17 @@
import AppKit import AppKit
import Carbon import Carbon
// NOTE: This is the corner radius of the backgrounView view that acts as // NOTE: This is the corner radius of the backgrounView view that acts as a window frame and an NSViewController's view that clips all
// a window frame and an NSViewController's view that clips all
// elements inside of it. // elements inside of it.
fileprivate let windowCornerRadius = 15.0 fileprivate let windowCornerRadius = 15.0
struct ProgramWeighted { fileprivate let maxItems = 20
let program: Program
let weight: Int
}
fileprivate let maxItems = 10
class SearchViewController: NSViewController, NSTextFieldDelegate, NSPopoverDelegate, NSTableViewDataSource, NSTableViewDelegate { class SearchViewController: NSViewController, NSTextFieldDelegate, NSPopoverDelegate, NSTableViewDataSource, NSTableViewDelegate {
private var keyboardEvents: EventMonitor? private var keyboardEvents: EventMonitor?
private var listIndex = 0 private var listIndex = 0
private var programsList: [Program] = Array(repeating: Program(), count: maxItems) private var programsList: [Program] = []
private var programsListCells: [ProgramsTableViewCell] = [] private var programsListCells: [ProgramsTableViewCell] = []
private var programsTableViewSelection = 0 private var programsTableViewSelection = 0
@@ -99,7 +93,8 @@ class SearchViewController: NSViewController, NSTextFieldDelegate, NSPopoverDele
private var programsTableView: ProgramsTableView = { private var programsTableView: ProgramsTableView = {
let table = ProgramsTableView() let table = ProgramsTableView()
table.style = NSTableView.Style.plain table.style = .plain
table.intercellSpacing = NSSize.zero
table.backgroundColor = .clear table.backgroundColor = .clear
table.usesAutomaticRowHeights = true table.usesAutomaticRowHeights = true
@@ -166,15 +161,20 @@ class SearchViewController: NSViewController, NSTextFieldDelegate, NSPopoverDele
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// NOTE: This needs removeObserver on deinit? // NOTE: This needs removeObserver on deinit? Well, technically we don't care because this view controller will exist throughout
// the whole life of the program. When the program gets killed, the OS will clear this.
DistributedNotificationCenter.default.addObserver(self, selector: #selector(osThemeChanged(sender:)), name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil) DistributedNotificationCenter.default.addObserver(self, selector: #selector(osThemeChanged(sender:)), name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil)
// Initialize an array of reusable cells. // Initialize an array of programs and reusable cells.
for _ in 0..<maxItems { for i in 0..<maxItems {
programsListCells.append(ProgramsTableViewCell()) programsList.append(Program())
let cell = ProgramsTableViewCell()
cell.indexLabel.stringValue = "\(i+1)"
programsListCells.append(cell)
} }
updateViewsBasedOnTheme() updateViewsBasedOnOSTheme()
view.wantsLayer = true view.wantsLayer = true
view.layer?.backgroundColor = NSColor.clear.cgColor view.layer?.backgroundColor = NSColor.clear.cgColor
@@ -192,6 +192,14 @@ class SearchViewController: NSViewController, NSTextFieldDelegate, NSPopoverDele
modsContainsNone(in: modifiers) && key == kVK_DownArrow modsContainsNone(in: modifiers) && key == kVK_DownArrow
{ {
controller.programsTableViewSelection += 1 controller.programsTableViewSelection += 1
} else if modsContains(keys: OSCtrl | OSCmd, in: modifiers) && key == kVK_ANSI_P ||
modsContains(keys: OSCmd, in: modifiers) && key == kVK_UpArrow
{
controller.programsTableViewSelection = 0
} else if modsContains(keys: OSCtrl | OSCmd, in: modifiers) && key == kVK_ANSI_N ||
modsContains(keys: OSCmd, in: modifiers) && key == kVK_DownArrow
{
controller.programsTableViewSelection = controller.listIndex-1
} else if modsContains(keys: OSCmd, in: modifiers) && isNumericalCode(key) { } else if modsContains(keys: OSCmd, in: modifiers) && isNumericalCode(key) {
if key == kVK_ANSI_1 { controller.programsTableViewSelection = 0 } if key == kVK_ANSI_1 { controller.programsTableViewSelection = 0 }
if key == kVK_ANSI_2 { controller.programsTableViewSelection = 1 } if key == kVK_ANSI_2 { controller.programsTableViewSelection = 1 }
@@ -333,6 +341,9 @@ class SearchViewController: NSViewController, NSTextFieldDelegate, NSPopoverDele
} else if commandSelector == #selector(NSResponder.moveUp(_:)) || commandSelector == #selector(NSResponder.moveDown(_:)) { } else if commandSelector == #selector(NSResponder.moveUp(_:)) || commandSelector == #selector(NSResponder.moveDown(_:)) {
// Ignore arrows up and down because we use those to navigate the programs list. // Ignore arrows up and down because we use those to navigate the programs list.
return true return true
} else if commandSelector == #selector(NSResponder.moveToBeginningOfDocument(_:)) {
// Ignore command plus up and down arrows because we use those to move to the beginning and end of the list.
return true
} }
return false return false
@@ -378,10 +389,10 @@ class SearchViewController: NSViewController, NSTextFieldDelegate, NSPopoverDele
} }
@objc func osThemeChanged(sender: NSNotification) { @objc func osThemeChanged(sender: NSNotification) {
updateViewsBasedOnTheme() updateViewsBasedOnOSTheme()
} }
@objc func updateViewsBasedOnTheme() { @objc func updateViewsBasedOnOSTheme() {
if NSApp.windows.first?.effectiveAppearance.bestMatch(from: [.darkAqua, .vibrantDark]) == .darkAqua { // dark if NSApp.windows.first?.effectiveAppearance.bestMatch(from: [.darkAqua, .vibrantDark]) == .darkAqua { // dark
backgroundView.layer?.borderColor = NSColor.white.withAlphaComponent(0.2).cgColor backgroundView.layer?.borderColor = NSColor.white.withAlphaComponent(0.2).cgColor
} else { // light } else { // light