Cleaned up paths table.

This commit is contained in:
2025-01-09 18:15:20 -08:00
parent 75bbcdb049
commit df619ccc2c
10 changed files with 76 additions and 103 deletions

View File

@@ -1,21 +1,13 @@
import Cocoa import Cocoa
import Carbon import Carbon
import ServiceManagement import ServiceManagement
import OSLog
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
fileprivate static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: AppDelegate.self)
)
let fileManager = FileManager.default let fileManager = FileManager.default
let window = PopoverPanel(viewController: SearchViewController()) let window = PopoverPanel(viewController: SearchViewController())
func applicationDidFinishLaunching(_ notification: Notification) { func applicationDidFinishLaunching(_ notification: Notification) {
Self.logger.debug("applicationDidFinishLaunching")
PathManager.shared.rebuildIndex() PathManager.shared.rebuildIndex()
window.delegate = self window.delegate = self
@@ -24,7 +16,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
HotKeyManager.shared.handler = HotKeyManager.shared.handler =
{ (inHandlerCallRef, inEvent, inUserData) -> OSStatus in { (inHandlerCallRef, inEvent, inUserData) -> OSStatus in
AppDelegate.logger.debug("Shortcut handler fired off.")
if let delegate = if let delegate =
NSApplication.shared.delegate as? AppDelegate NSApplication.shared.delegate as? AppDelegate
{ {
@@ -59,12 +50,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
//} //}
func windowDidBecomeKey(_ notification: Notification) { func windowDidBecomeKey(_ notification: Notification) {
Self.logger.debug("Popover became key.")
} }
func windowDidResignKey(_ notification: Notification) { func windowDidResignKey(_ notification: Notification) {
Self.logger.debug("Popover resigned key.")
if window.isVisible { if window.isVisible {
window.orderOut(nil) window.orderOut(nil)
} }
@@ -73,8 +61,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
func applicationShouldHandleReopen(_ sender: NSApplication, func applicationShouldHandleReopen(_ sender: NSApplication,
hasVisibleWindows: Bool) -> Bool hasVisibleWindows: Bool) -> Bool
{ {
Self.logger.debug("Application reopened.")
if !window.isKeyWindow { if !window.isKeyWindow {
window.makeKeyAndOrderFront(nil) window.makeKeyAndOrderFront(nil)
} }

View File

@@ -1,10 +1,16 @@
import Cocoa import Cocoa
protocol EditableNSTextFieldDelegate: AnyObject {
func lostFocus()
}
final class EditableNSTextField: NSTextField { final class EditableNSTextField: NSTextField {
private let commandKey = NSEvent.ModifierFlags.command.rawValue private let commandKey = NSEvent.ModifierFlags.command.rawValue
private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue |
NSEvent.ModifierFlags.shift.rawValue NSEvent.ModifierFlags.shift.rawValue
weak var auxiliaryDelegate: EditableNSTextFieldDelegate?
override func performKeyEquivalent(with event: NSEvent) -> Bool { override func performKeyEquivalent(with event: NSEvent) -> Bool {
if event.type == NSEvent.EventType.keyDown { if event.type == NSEvent.EventType.keyDown {
if (event.modifierFlags.rawValue & if (event.modifierFlags.rawValue &
@@ -50,4 +56,8 @@ final class EditableNSTextField: NSTextField {
} }
return super.performKeyEquivalent(with: event) return super.performKeyEquivalent(with: event)
} }
override func textDidEndEditing(_ notification: Notification) {
auxiliaryDelegate?.lostFocus()
}
} }

View File

@@ -1,11 +1,5 @@
import AppKit import AppKit
import Carbon import Carbon
import OSLog
fileprivate let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: String("Helpers")
)
let OSCtrl = NSEvent.ModifierFlags.control.rawValue let OSCtrl = NSEvent.ModifierFlags.control.rawValue
let OSCmd = NSEvent.ModifierFlags.command.rawValue let OSCmd = NSEvent.ModifierFlags.command.rawValue
@@ -29,13 +23,6 @@ enum ViewConstants {
static let spacing40: CGFloat = 40 static let spacing40: CGFloat = 40
} }
struct Program {
let path: String
let name: String
let ext: String
var img: NSImage?
}
func keyName(virtualKeyCode: UInt16) -> String? { func keyName(virtualKeyCode: UInt16) -> String? {
let maxNameLength = 4 let maxNameLength = 4
var nameBuffer = [UniChar](repeating: 0, count : maxNameLength) var nameBuffer = [UniChar](repeating: 0, count : maxNameLength)
@@ -52,7 +39,7 @@ func keyName(virtualKeyCode: UInt16) -> String? {
guard let ptr = TISGetInputSourceProperty(source, guard let ptr = TISGetInputSourceProperty(source,
kTISPropertyUnicodeKeyLayoutData) kTISPropertyUnicodeKeyLayoutData)
else { else {
logger.log("Could not get keyboard layout data") print("Could not get keyboard layout data")
return nil return nil
} }
let layoutData = Unmanaged<CFData>.fromOpaque(ptr) let layoutData = Unmanaged<CFData>.fromOpaque(ptr)
@@ -65,7 +52,7 @@ func keyName(virtualKeyCode: UInt16) -> String? {
&deadKeys, maxNameLength, &nameLength, &nameBuffer) &deadKeys, maxNameLength, &nameLength, &nameBuffer)
} }
guard osStatus == noErr else { guard osStatus == noErr else {
logger.debug("Code: \(virtualKeyCode) Status: \(osStatus)") print("Code: \(virtualKeyCode) Status: \(osStatus)")
return nil return nil
} }

View File

@@ -10,8 +10,9 @@ EXEC = Grapp
SRCMODULES = Helpers.swift EditableNSTextField.swift EventMonitor.swift \ SRCMODULES = Helpers.swift EditableNSTextField.swift EventMonitor.swift \
GlobalEventTap.swift PopoverPanel.swift SearchViewController.swift \ GlobalEventTap.swift PopoverPanel.swift SearchViewController.swift \
SettingsViewController.swift HotKeyManager.swift \ SettingsViewController.swift HotKeyManager.swift \
KeyDetectorButton.swift PathManager.swift MyTableCellView.swift \ KeyDetectorButton.swift PathManager.swift PathsTableCellView.swift \
ProgramTableViewCell.swift AppDelegate.swift main.swift ProgramTableViewCell.swift ProgramsTableView.swift AppDelegate.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))

View File

@@ -1,12 +1,13 @@
import AppKit import AppKit
import OSLog
struct Program {
let path: String
let name: String
let ext: String
var img: NSImage?
}
final class PathManager { final class PathManager {
fileprivate static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: PathManager.self)
)
static let shared = PathManager() static let shared = PathManager()
// TODO: Filesystem events to watch changes on these directories and // TODO: Filesystem events to watch changes on these directories and
@@ -80,7 +81,7 @@ final class PathManager {
} }
} }
} catch { } catch {
Self.logger.error("Error reading directory: \(error.localizedDescription, privacy: .public)") print("Error reading directory: \(error.localizedDescription)")
} }
} }
} }

View File

@@ -1,19 +1,21 @@
import AppKit import AppKit
protocol MyTableCellViewDelegate: AnyObject { protocol PathsTableCellViewDelegate: AnyObject {
func selectionButtonClicked(tag: Int) func selectionButtonClicked(tag: Int)
func titleFieldTextChanged(tag: Int, text: String) func titleFieldTextChanged(tag: Int, text: String)
func titleFieldFinishedEditing(tag: Int, text: String) func titleFieldFinishedEditing(tag: Int, text: String)
} }
class MyTableCellView: NSTableCellView, NSTextFieldDelegate { class PathsTableCellView: NSTableCellView, NSTextFieldDelegate,
EditableNSTextFieldDelegate
{
var id: Int = -1 var id: Int = -1
weak var delegate: MyTableCellViewDelegate? weak var delegate: PathsTableCellViewDelegate?
private(set) var isEditing = false private(set) var isEditing = false
public var titleField: NSTextField = { public var titleField: EditableNSTextField = {
let field = NSTextField() let field = EditableNSTextField()
field.isEditable = false field.isEditable = false
field.maximumNumberOfLines = 1 field.maximumNumberOfLines = 1
field.lineBreakMode = .byTruncatingTail field.lineBreakMode = .byTruncatingTail
@@ -38,6 +40,7 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
super.init(frame: frameRect) super.init(frame: frameRect)
titleField.delegate = self titleField.delegate = self
titleField.auxiliaryDelegate = self
selectionButton.target = self selectionButton.target = self
selectionButton.action = #selector(makeSelection) selectionButton.action = #selector(makeSelection)
@@ -74,15 +77,19 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
} }
public func startEditing() { public func startEditing() {
guard !isEditing else { return }
isEditing = true isEditing = true
titleField.isEditable = true titleField.isEditable = true
window?.makeFirstResponder(titleField) window?.makeFirstResponder(titleField)
} }
public func stopEditing() { public func stopEditing() {
guard isEditing else { return }
isEditing = false isEditing = false
titleField.isEditable = false titleField.isEditable = false
window?.makeFirstResponder(nil) window?.makeFirstResponder(nil)
delegate?.titleFieldFinishedEditing(tag: id,
text: titleField.stringValue)
} }
func controlTextDidChange(_ obj: Notification) { func controlTextDidChange(_ obj: Notification) {
@@ -95,8 +102,6 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
{ {
if commandSelector == #selector(NSResponder.insertNewline(_:)) { if commandSelector == #selector(NSResponder.insertNewline(_:)) {
stopEditing() stopEditing()
delegate?.titleFieldFinishedEditing(tag: id,
text: titleField.stringValue)
return true return true
} else if commandSelector == #selector(NSResponder.insertTab(_:)) { } else if commandSelector == #selector(NSResponder.insertTab(_:)) {
return true return true
@@ -104,4 +109,8 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
return false return false
} }
func lostFocus() {
stopEditing()
}
} }

View File

@@ -1,13 +1,7 @@
import Cocoa import Cocoa
import Carbon import Carbon
import OSLog
class PopoverPanel: NSPanel { class PopoverPanel: NSPanel {
fileprivate static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: PopoverPanel.self)
)
override var canBecomeKey: Bool { true } override var canBecomeKey: Bool { true }
init(viewController: NSViewController) { init(viewController: NSViewController) {

View File

@@ -0,0 +1,5 @@
import AppKit
final class ProgramsTableView: NSTableView {
override var acceptsFirstResponder: Bool { false }
}

View File

@@ -1,15 +1,9 @@
import AppKit import AppKit
import Carbon import Carbon
import OSLog
class SearchViewController: NSViewController, NSTextFieldDelegate, class SearchViewController: NSViewController, NSTextFieldDelegate,
NSPopoverDelegate, NSTableViewDataSource, NSTableViewDelegate NSPopoverDelegate, NSTableViewDataSource, NSTableViewDelegate
{ {
fileprivate static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: SearchViewController.self)
)
private var keyboardEvents: EventMonitor? private var keyboardEvents: EventMonitor?
private var foundProgram: Program? = nil private var foundProgram: Program? = nil
@@ -65,8 +59,8 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
return scroll return scroll
}() }()
private var programsTableView: MyNSTableView = { private var programsTableView: ProgramsTableView = {
let table = MyNSTableView() let table = ProgramsTableView()
table.style = NSTableView.Style.plain table.style = NSTableView.Style.plain
table.backgroundColor = .clear table.backgroundColor = .clear
@@ -262,9 +256,9 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
configuration: config) configuration: config)
{ [weak self] application, error in { [weak self] application, error in
if let error = error { if let error = error {
Self.logger.debug("\(error.localizedDescription)") print("\(error.localizedDescription)")
} else { } else {
Self.logger.debug("Program opened successfully") print("Program opened successfully")
// NOTE: This needs a window! Do not just copy-paste // NOTE: This needs a window! Do not just copy-paste
// this block elsewhere. // this block elsewhere.
DispatchQueue.main.async { DispatchQueue.main.async {
@@ -384,7 +378,3 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
return cell return cell
} }
} }
final class MyNSTableView: NSTableView {
override var acceptsFirstResponder: Bool { false }
}

View File

@@ -1,17 +1,11 @@
import AppKit import AppKit
import Carbon import Carbon
import ServiceManagement import ServiceManagement
import OSLog
class SettingsViewController: NSViewController, NSTextFieldDelegate, class SettingsViewController: NSViewController, NSTextFieldDelegate,
KeyDetectorButtonDelegate, NSTableViewDataSource, NSTableViewDelegate, KeyDetectorButtonDelegate, NSTableViewDataSource, NSTableViewDelegate,
MyTableCellViewDelegate PathsTableCellViewDelegate
{ {
fileprivate static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: SettingsViewController.self)
)
private var recording = false private var recording = false
// NOTE: This is the default shortcut. If you were to change it, don't // NOTE: This is the default shortcut. If you were to change it, don't
@@ -133,7 +127,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
table.doubleAction = #selector(editItem) table.doubleAction = #selector(editItem)
table.headerView = nil table.headerView = nil
table.allowsMultipleSelection = true table.allowsMultipleSelection = false
table.allowsColumnReordering = false table.allowsColumnReordering = false
table.allowsColumnResizing = false table.allowsColumnResizing = false
table.allowsColumnSelection = false table.allowsColumnSelection = false
@@ -260,7 +254,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
equalTo: shortcutsLabel.leadingAnchor), equalTo: shortcutsLabel.leadingAnchor),
tableScrollView.widthAnchor.constraint(equalToConstant: 350), tableScrollView.widthAnchor.constraint(equalToConstant: 350),
tableScrollView.heightAnchor.constraint(equalToConstant: 100), tableScrollView.heightAnchor.constraint(equalToConstant: 150),
tableScrollView.topAnchor.constraint( tableScrollView.topAnchor.constraint(
equalTo: pathsLabel.bottomAnchor), equalTo: pathsLabel.bottomAnchor),
tableScrollView.leadingAnchor.constraint( tableScrollView.leadingAnchor.constraint(
@@ -336,6 +330,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
modifiers = mods modifiers = mods
} }
pathsTableView.reloadData()
syncModifierButtons() syncModifierButtons()
launchAtLoginStatus() launchAtLoginStatus()
} }
@@ -355,6 +350,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
UserDefaults.standard.set(keyCode, forKey: "keyCode") UserDefaults.standard.set(keyCode, forKey: "keyCode")
UserDefaults.standard.set(modifiers, forKey: "keyModifiers") UserDefaults.standard.set(modifiers, forKey: "keyModifiers")
PathManager.shared.removeEmpty()
PathManager.shared.savePaths() PathManager.shared.savePaths()
PathManager.shared.rebuildIndex() PathManager.shared.rebuildIndex()
} }
@@ -454,29 +450,30 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
@objc @objc
private func affectPaths(_ sender: NSSegmentedControl) { private func affectPaths(_ sender: NSSegmentedControl) {
// PERF: All of this could be written better.
let selectedSegment = sender.selectedSegment let selectedSegment = sender.selectedSegment
switch selectedSegment { switch selectedSegment {
case 0: 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("") PathManager.shared.addPath("")
pathsTableView.reloadData() pathsTableView.insertRows(at: IndexSet(integer: row),
withAnimation: [])
let row = PathManager.shared.userPaths.count-1 }
pathsTableView.scrollRowToVisible(row)
pathsTableView.selectRowIndexes(IndexSet(integer: row), pathsTableView.selectRowIndexes(IndexSet(integer: row),
byExtendingSelection: false) byExtendingSelection: false)
pathsTableView.scrollRowToVisible(row)
(pathsTableView.view(atColumn: 0, row: row, (pathsTableView.view(atColumn: 0, row: row,
makeIfNecessary: false) as? MyTableCellView)? makeIfNecessary: false) as? PathsTableCellView)?
.startEditing() .startEditing()
break break
case 1: case 1:
var toRemove: [String] = [] if pathsTableView.selectedRow > -1 {
for row in pathsTableView.selectedRowIndexes { PathManager.shared.userPaths
toRemove.append(PathManager.shared.userPaths[row]) .remove(at: pathsTableView.selectedRow)
}
PathManager.shared.userPaths.removeAll(
where: { toRemove.contains($0) })
pathsTableView.reloadData() pathsTableView.reloadData()
}
break break
default: default:
break break
@@ -492,19 +489,19 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
if let cell = pathsTableView.view(atColumn: 0, if let cell = pathsTableView.view(atColumn: 0,
row: pathsTableView.clickedRow, row: pathsTableView.clickedRow,
makeIfNecessary: false) as? MyTableCellView makeIfNecessary: false) as? PathsTableCellView
{ {
cell.startEditing() cell.startEditing()
} }
} }
func titleFieldFinishedEditing(tag: Int, text: String) { func titleFieldFinishedEditing(tag: Int, text: String) {
PathManager.shared.userPaths[tag] = text if text.isEmpty {
if PathManager.shared.userPaths[tag].isEmpty {
PathManager.shared.userPaths.remove(at: tag) PathManager.shared.userPaths.remove(at: tag)
pathsTableView.reloadData() } else {
PathManager.shared.userPaths[tag] = text
} }
pathsTableView.deselectAll(nil) pathsTableView.reloadData()
} }
func titleFieldTextChanged(tag: Int, text: String) { func titleFieldTextChanged(tag: Int, text: String) {
@@ -543,20 +540,13 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
{ {
let rect = NSRect(x: 0, y: 0, let rect = NSRect(x: 0, y: 0,
width: tableColumn!.width, height: 20) width: tableColumn!.width, height: 20)
let cell = MyTableCellView(frame: rect) let cell = PathsTableCellView(frame: rect)
cell.titleField.stringValue = PathManager.shared.userPaths[row] cell.titleField.stringValue = PathManager.shared.userPaths[row]
cell.delegate = self cell.delegate = self
cell.id = row cell.id = row
return cell return cell
} }
func tableViewSelectionDidChange(_ notification: Notification) { func tableViewSelectionDidChange(_ notification: Notification) {
/*
let selectedRow = tableView.selectedRow
if selectedRow >= 0 {
print("Selected: \(items[selectedRow])")
}
*/
} }
} }