Filesystem monitor and refactor.

This commit is contained in:
2025-02-06 17:03:16 -08:00
parent 6557993214
commit 5ed67e0cef
15 changed files with 777 additions and 481 deletions

View File

@@ -2,10 +2,9 @@ import AppKit
import Carbon
import ServiceManagement
class SettingsViewController: NSViewController, NSTextFieldDelegate,
KeyDetectorButtonDelegate,
NSTableViewDataSource, NSTableViewDelegate,
PathsTableCellViewDelegate
class SettingsViewController: NSViewController,
NSTextFieldDelegate, KeyDetectorButtonDelegate, NSTableViewDataSource,
NSTableViewDelegate, PathsTableCellViewDelegate
{
private var recording = false
@@ -14,6 +13,8 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
private var keyCode = Int(kVK_Space)
private var modifiers = Int(optionKey)
private var paths: [String] = []
// NOTE: PERF: This is very slow to initialize because it creates a
// a new process. This also cannot be done on a separate
// thread. This sucks because the program now takes
@@ -29,13 +30,22 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
private var shortcutsLabel: NSTextField = {
let textField = NSTextField(labelWithString: "Shortcut")
textField.font = NSFont.systemFont(
ofSize: NSFontDescriptor.preferredFontDescriptor(
forTextStyle: .title2).pointSize, weight: .bold)
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .title2).pointSize, weight: .bold)
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
private var aboutButton: NSButton = {
let button = NSButton()
button.image = systemImage("info.circle.fill", .title2, .large, .init(paletteColors: [.white, .systemGray]))
button.isBordered = false
button.action = #selector(showAbout)
button.sizeToFit()
button.toolTip = "About"
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private var ctrlButton: NSButton = {
let button = NSButton()
button.title = ""
@@ -87,9 +97,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
textField.isBezeled = false
textField.drawsBackground = false
textField.alignment = .center
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
}()
@@ -104,11 +112,8 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
}()
private var pathsLabel: NSTextField = {
let textField =
NSTextField(labelWithString: "Application Directories")
textField.font = NSFont.systemFont(
ofSize: NSFontDescriptor.preferredFontDescriptor(
forTextStyle: .title2).pointSize, weight: .bold)
let textField = NSTextField(labelWithString: "Application Directories")
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .title2).pointSize, weight: .bold)
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
@@ -132,8 +137,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
table.allowsColumnReordering = false
table.allowsColumnResizing = false
table.allowsColumnSelection = false
table.addTableColumn(NSTableColumn(
identifier: NSUserInterfaceItemIdentifier("Paths")))
table.addTableColumn(NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Paths")))
//rowHeight cgfloat must see doc
@@ -146,12 +150,8 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
control.segmentCount = 2
control.segmentStyle = .roundRect
control.setImage(
NSImage(systemSymbolName: "plus",
accessibilityDescription: nil), forSegment: 0)
control.setImage(
NSImage(systemSymbolName: "minus",
accessibilityDescription: nil), forSegment: 1)
control.setImage(NSImage(systemSymbolName: "plus", accessibilityDescription: nil), forSegment: 0)
control.setImage(NSImage(systemSymbolName: "minus", accessibilityDescription: nil), forSegment: 1)
control.setToolTip("Add Path", forSegment: 0)
control.setToolTip("Remove Path", forSegment: 1)
@@ -187,6 +187,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
private func addSubviews() {
view.addSubview(shortcutsLabel)
view.addSubview(aboutButton)
view.addSubview(ctrlButton)
view.addSubview(cmdButton)
view.addSubview(optButton)
@@ -204,87 +205,49 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
private func setConstraints() {
NSLayoutConstraint.activate([
shortcutsLabel.topAnchor.constraint(
equalTo: view.topAnchor,
constant: ViewConstants.spacing10),
shortcutsLabel.leadingAnchor.constraint(
equalTo: view.leadingAnchor,
constant: ViewConstants.spacing10),
shortcutsLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: ViewConstants.spacing10),
shortcutsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: ViewConstants.spacing10),
ctrlButton.topAnchor.constraint(
equalTo: shortcutsLabel.bottomAnchor,
constant: ViewConstants.spacing10),
ctrlButton.leadingAnchor.constraint(
equalTo: shortcutsLabel.leadingAnchor),
aboutButton.firstBaselineAnchor.constraint(equalTo: shortcutsLabel.firstBaselineAnchor),
aboutButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -ViewConstants.spacing10),
cmdButton.centerYAnchor.constraint(
equalTo: ctrlButton.centerYAnchor),
cmdButton.leadingAnchor.constraint(
equalTo: ctrlButton.trailingAnchor,
constant: ViewConstants.spacing5),
ctrlButton.topAnchor.constraint(equalTo: shortcutsLabel.bottomAnchor, constant: ViewConstants.spacing10),
ctrlButton.leadingAnchor.constraint(equalTo: shortcutsLabel.leadingAnchor),
optButton.centerYAnchor.constraint(
equalTo: ctrlButton.centerYAnchor),
optButton.leadingAnchor.constraint(
equalTo: cmdButton.trailingAnchor,
constant: ViewConstants.spacing5),
cmdButton.centerYAnchor.constraint(equalTo: ctrlButton.centerYAnchor),
cmdButton.leadingAnchor.constraint(equalTo: ctrlButton.trailingAnchor, constant: ViewConstants.spacing5),
shiftButton.centerYAnchor.constraint(
equalTo: ctrlButton.centerYAnchor),
shiftButton.leadingAnchor.constraint(
equalTo: optButton.trailingAnchor,
constant: ViewConstants.spacing5),
optButton.centerYAnchor.constraint(equalTo: ctrlButton.centerYAnchor),
optButton.leadingAnchor.constraint(equalTo: cmdButton.trailingAnchor, constant: ViewConstants.spacing5),
plusLabel.centerYAnchor.constraint(
equalTo: ctrlButton.centerYAnchor),
plusLabel.leadingAnchor.constraint(
equalTo: shiftButton.trailingAnchor,
constant: ViewConstants.spacing5),
shiftButton.centerYAnchor.constraint(equalTo: ctrlButton.centerYAnchor),
shiftButton.leadingAnchor.constraint(equalTo: optButton.trailingAnchor, constant: ViewConstants.spacing5),
plusLabel.centerYAnchor.constraint(equalTo: ctrlButton.centerYAnchor),
plusLabel.leadingAnchor.constraint(equalTo: shiftButton.trailingAnchor, constant: ViewConstants.spacing5),
recordButton.widthAnchor.constraint(equalToConstant: 40),
recordButton.centerYAnchor.constraint(
equalTo: ctrlButton.centerYAnchor),
recordButton.leadingAnchor.constraint(
equalTo: plusLabel.trailingAnchor,
constant: ViewConstants.spacing5),
recordButton.centerYAnchor.constraint(equalTo: ctrlButton.centerYAnchor),
recordButton.leadingAnchor.constraint(equalTo: plusLabel.trailingAnchor, constant: ViewConstants.spacing5),
pathsLabel.topAnchor.constraint(
equalTo: ctrlButton.bottomAnchor,
constant: ViewConstants.spacing20),
pathsLabel.leadingAnchor.constraint(
equalTo: shortcutsLabel.leadingAnchor),
pathsLabel.topAnchor.constraint(equalTo: ctrlButton.bottomAnchor, constant: ViewConstants.spacing20),
pathsLabel.leadingAnchor.constraint(equalTo: shortcutsLabel.leadingAnchor),
tableScrollView.widthAnchor.constraint(equalToConstant: 350),
tableScrollView.heightAnchor.constraint(equalToConstant: 150),
tableScrollView.topAnchor.constraint(
equalTo: pathsLabel.bottomAnchor),
tableScrollView.leadingAnchor.constraint(
equalTo: view.leadingAnchor),
tableScrollView.trailingAnchor.constraint(
equalTo: view.trailingAnchor),
tableScrollView.topAnchor.constraint(equalTo: pathsLabel.bottomAnchor),
tableScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
pathsControl.topAnchor.constraint(
equalTo: tableScrollView.bottomAnchor,
constant: ViewConstants.spacing10),
pathsControl.leadingAnchor.constraint(
equalTo: view.leadingAnchor,
constant: ViewConstants.spacing10),
pathsControl.topAnchor.constraint(equalTo: tableScrollView.bottomAnchor, constant: ViewConstants.spacing10),
pathsControl.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: ViewConstants.spacing10),
launchAtLoginButton.topAnchor.constraint(
equalTo: pathsControl.bottomAnchor,
constant: ViewConstants.spacing10),
launchAtLoginButton.trailingAnchor.constraint(
equalTo: resetAllButton.leadingAnchor,
constant: -ViewConstants.spacing10),
launchAtLoginButton.topAnchor.constraint(equalTo: pathsControl.bottomAnchor, constant: ViewConstants.spacing10),
launchAtLoginButton.trailingAnchor.constraint(equalTo: resetAllButton.leadingAnchor, constant: -ViewConstants.spacing10),
resetAllButton.centerYAnchor.constraint(
equalTo: launchAtLoginButton.centerYAnchor),
resetAllButton.trailingAnchor.constraint(
equalTo: view.trailingAnchor,
constant: -ViewConstants.spacing10),
resetAllButton.bottomAnchor.constraint(
equalTo: view.bottomAnchor,
constant: -ViewConstants.spacing10),
resetAllButton.centerYAnchor.constraint(equalTo: launchAtLoginButton.centerYAnchor),
resetAllButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -ViewConstants.spacing10),
resetAllButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -ViewConstants.spacing10),
])
}
@@ -320,26 +283,20 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
// PERF: Maybe we shouldn't fetch it on every appearance?
// Only do it in AppDelegate?
if let code =
UserDefaults.standard.object(forKey: "keyCode") as? Int
{
if let code = UserDefaults.standard.object(forKey: "keyCode") as? Int {
keyCode = code
}
if let mods =
UserDefaults.standard.object(forKey: "keyModifiers") as? Int
{
if let mods = UserDefaults.standard.object(forKey: "keyModifiers") as? Int {
modifiers = mods
}
pathsTableView.reloadData()
loadPaths()
syncModifierButtons()
launchAtLoginStatus()
}
override func viewDidAppear() {
super.viewDidAppear()
self.view.window?.center()
}
override func viewWillDisappear() {
@@ -351,19 +308,34 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
UserDefaults.standard.set(keyCode, forKey: "keyCode")
UserDefaults.standard.set(modifiers, forKey: "keyModifiers")
PathManager.shared.removeEmpty()
// Merge PathManagers paths and user paths.
// WARNING: This seems a bit error prone.
for path in paths {
if !PathManager.shared.contains(path) {
PathManager.shared.addPath(path)
}
}
for path in PathManager.shared.paths {
if !paths.contains(path.key) {
PathManager.shared.removePath(path.key)
}
}
PathManager.shared.updateIndex()
PathManager.shared.savePaths()
PathManager.shared.rebuildIndex()
}
override func loadView() {
self.view = NSView()
}
@objc
private func showAbout() {
delegate.showAboutWindow()
}
@objc
private func handleModifiers() {
// NOTE: Revert to default modifier if none of the modifier
// buttons are on.
// Revert to default modifier if none of the modifier buttons are on.
if cmdButton.state != .on, optButton.state != .on,
ctrlButton.state != .on, shiftButton.state != .on
{
@@ -391,14 +363,23 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
@objc
private func reset() {
keyCode = Int(kVK_Space)
keyCode = Int(kVK_Space)
modifiers = Int(optionKey)
HotKeyManager.shared.registerHotKey(key: keyCode, modifiers: modifiers)
UserDefaults.standard.set(keyCode, forKey: "keyCode")
UserDefaults.standard.set(modifiers, forKey: "keyModifiers")
syncModifierButtons()
PathManager.shared.reset()
PathManager.shared.resetPaths()
loadPaths()
}
private func loadPaths() {
paths = []
for path in PathManager.shared.paths {
paths.append(path.key)
}
paths.sort()
pathsTableView.reloadData()
}
@@ -454,25 +435,17 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
let selectedSegment = sender.selectedSegment
switch selectedSegment {
case 0:
// NOTE: Seems a bit error prone.
var row = PathManager.shared.userPaths.count-1
if !PathManager.shared.userPaths[row].isEmpty {
row += 1
PathManager.shared.addPath("")
pathsTableView.insertRows(at: IndexSet(integer: row),
withAnimation: [])
}
let row = paths.count
paths.append("")
pathsTableView.insertRows(at: IndexSet(integer: row), withAnimation: [])
pathsTableView.scrollRowToVisible(row)
pathsTableView.selectRowIndexes(IndexSet(integer: row),
byExtendingSelection: false)
(pathsTableView.view(atColumn: 0, row: row,
makeIfNecessary: false) as? PathsTableCellView)?
.startEditing()
pathsTableView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
(pathsTableView.view(atColumn: 0, row: row, makeIfNecessary: false) as? PathsTableCellView)?.startEditing()
break
case 1:
if pathsTableView.selectedRow > -1 {
PathManager.shared.userPaths
.remove(at: pathsTableView.selectedRow)
paths.remove(at: pathsTableView.selectedRow)
pathsTableView.reloadData()
}
break
@@ -484,23 +457,18 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
@objc
private func editItem(_ sender: NSTableView) {
pathsTableView.deselectAll(nil)
pathsTableView.selectRowIndexes(
IndexSet(integer: pathsTableView.clickedRow),
byExtendingSelection: false)
pathsTableView.selectRowIndexes(IndexSet(integer: pathsTableView.clickedRow), byExtendingSelection: false)
if let cell = pathsTableView.view(atColumn: 0,
row: pathsTableView.clickedRow,
makeIfNecessary: false) as? PathsTableCellView
{
if let cell = pathsTableView.view(atColumn: 0, row: pathsTableView.clickedRow, makeIfNecessary: false) as? PathsTableCellView {
cell.startEditing()
}
}
func titleFieldFinishedEditing(tag: Int, text: String) {
if text.isEmpty {
PathManager.shared.userPaths.remove(at: tag)
paths.remove(at: tag)
} else {
PathManager.shared.userPaths[tag] = text
paths[tag] = text
}
pathsTableView.reloadData()
}
@@ -515,34 +483,32 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
func selectionButtonClicked(tag: Int) {
NSRunningApplication.current.activate(options: .activateAllWindows)
delegate.window.level = .normal
delegate.aboutWindow.performClose(nil)
if dirPicker.runModal() == .OK {
if let url = dirPicker.url {
PathManager.shared.userPaths[tag] = url.path
paths[tag] = url.path
pathsTableView.reloadData()
}
}
delegate.window.level = .statusBar
delegate.window.makeKeyAndOrderFront(nil)
if let controller =
delegate.window.contentViewController as? SearchViewController
{
if let controller = delegate.window.contentViewController as? SearchViewController {
controller.openSettings()
}
}
func numberOfRows(in tableView: NSTableView) -> Int {
return PathManager.shared.userPaths.count
return paths.count
}
func tableView(_ tableView: NSTableView,
viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
{
let rect = NSRect(x: 0, y: 0,
width: tableColumn!.width, height: 20)
let rect = NSRect(x: 0, y: 0, width: tableColumn!.width, height: 20)
let cell = PathsTableCellView(frame: rect)
cell.titleField.stringValue = PathManager.shared.userPaths[row]
cell.titleField.stringValue = paths[row]
cell.delegate = self
cell.id = row
return cell