Cleaned up paths table.
This commit is contained in:
@@ -1,21 +1,13 @@
|
||||
import Cocoa
|
||||
import Carbon
|
||||
import ServiceManagement
|
||||
import OSLog
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
||||
fileprivate static let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String(describing: AppDelegate.self)
|
||||
)
|
||||
|
||||
let fileManager = FileManager.default
|
||||
|
||||
let window = PopoverPanel(viewController: SearchViewController())
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
Self.logger.debug("applicationDidFinishLaunching")
|
||||
|
||||
PathManager.shared.rebuildIndex()
|
||||
|
||||
window.delegate = self
|
||||
@@ -24,7 +16,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
||||
|
||||
HotKeyManager.shared.handler =
|
||||
{ (inHandlerCallRef, inEvent, inUserData) -> OSStatus in
|
||||
AppDelegate.logger.debug("Shortcut handler fired off.")
|
||||
if let delegate =
|
||||
NSApplication.shared.delegate as? AppDelegate
|
||||
{
|
||||
@@ -59,12 +50,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
||||
//}
|
||||
|
||||
func windowDidBecomeKey(_ notification: Notification) {
|
||||
Self.logger.debug("Popover became key.")
|
||||
}
|
||||
|
||||
func windowDidResignKey(_ notification: Notification) {
|
||||
Self.logger.debug("Popover resigned key.")
|
||||
|
||||
if window.isVisible {
|
||||
window.orderOut(nil)
|
||||
}
|
||||
@@ -73,8 +61,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
||||
func applicationShouldHandleReopen(_ sender: NSApplication,
|
||||
hasVisibleWindows: Bool) -> Bool
|
||||
{
|
||||
Self.logger.debug("Application reopened.")
|
||||
|
||||
if !window.isKeyWindow {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import Cocoa
|
||||
|
||||
protocol EditableNSTextFieldDelegate: AnyObject {
|
||||
func lostFocus()
|
||||
}
|
||||
|
||||
final class EditableNSTextField: NSTextField {
|
||||
private let commandKey = NSEvent.ModifierFlags.command.rawValue
|
||||
private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue |
|
||||
NSEvent.ModifierFlags.shift.rawValue
|
||||
|
||||
weak var auxiliaryDelegate: EditableNSTextFieldDelegate?
|
||||
|
||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||
if event.type == NSEvent.EventType.keyDown {
|
||||
if (event.modifierFlags.rawValue &
|
||||
@@ -50,4 +56,8 @@ final class EditableNSTextField: NSTextField {
|
||||
}
|
||||
return super.performKeyEquivalent(with: event)
|
||||
}
|
||||
|
||||
override func textDidEndEditing(_ notification: Notification) {
|
||||
auxiliaryDelegate?.lostFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import AppKit
|
||||
import Carbon
|
||||
import OSLog
|
||||
|
||||
fileprivate let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String("Helpers")
|
||||
)
|
||||
|
||||
let OSCtrl = NSEvent.ModifierFlags.control.rawValue
|
||||
let OSCmd = NSEvent.ModifierFlags.command.rawValue
|
||||
@@ -29,13 +23,6 @@ enum ViewConstants {
|
||||
static let spacing40: CGFloat = 40
|
||||
}
|
||||
|
||||
struct Program {
|
||||
let path: String
|
||||
let name: String
|
||||
let ext: String
|
||||
var img: NSImage?
|
||||
}
|
||||
|
||||
func keyName(virtualKeyCode: UInt16) -> String? {
|
||||
let maxNameLength = 4
|
||||
var nameBuffer = [UniChar](repeating: 0, count : maxNameLength)
|
||||
@@ -52,7 +39,7 @@ func keyName(virtualKeyCode: UInt16) -> String? {
|
||||
guard let ptr = TISGetInputSourceProperty(source,
|
||||
kTISPropertyUnicodeKeyLayoutData)
|
||||
else {
|
||||
logger.log("Could not get keyboard layout data")
|
||||
print("Could not get keyboard layout data")
|
||||
return nil
|
||||
}
|
||||
let layoutData = Unmanaged<CFData>.fromOpaque(ptr)
|
||||
@@ -65,7 +52,7 @@ func keyName(virtualKeyCode: UInt16) -> String? {
|
||||
&deadKeys, maxNameLength, &nameLength, &nameBuffer)
|
||||
}
|
||||
guard osStatus == noErr else {
|
||||
logger.debug("Code: \(virtualKeyCode) Status: \(osStatus)")
|
||||
print("Code: \(virtualKeyCode) Status: \(osStatus)")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ EXEC = Grapp
|
||||
SRCMODULES = Helpers.swift EditableNSTextField.swift EventMonitor.swift \
|
||||
GlobalEventTap.swift PopoverPanel.swift SearchViewController.swift \
|
||||
SettingsViewController.swift HotKeyManager.swift \
|
||||
KeyDetectorButton.swift PathManager.swift MyTableCellView.swift \
|
||||
ProgramTableViewCell.swift AppDelegate.swift main.swift
|
||||
KeyDetectorButton.swift PathManager.swift PathsTableCellView.swift \
|
||||
ProgramTableViewCell.swift ProgramsTableView.swift AppDelegate.swift \
|
||||
main.swift
|
||||
ARMOBJMODULES = $(addprefix ./arm64/,$(SRCMODULES:.swift=.o))
|
||||
X86OBJMODULES = $(addprefix ./x86_64/,$(SRCMODULES:.swift=.o))
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import AppKit
|
||||
import OSLog
|
||||
|
||||
struct Program {
|
||||
let path: String
|
||||
let name: String
|
||||
let ext: String
|
||||
var img: NSImage?
|
||||
}
|
||||
|
||||
final class PathManager {
|
||||
fileprivate static let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String(describing: PathManager.self)
|
||||
)
|
||||
|
||||
static let shared = PathManager()
|
||||
|
||||
// TODO: Filesystem events to watch changes on these directories and
|
||||
@@ -80,7 +81,7 @@ final class PathManager {
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Self.logger.error("Error reading directory: \(error.localizedDescription, privacy: .public)")
|
||||
print("Error reading directory: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import AppKit
|
||||
|
||||
protocol MyTableCellViewDelegate: AnyObject {
|
||||
protocol PathsTableCellViewDelegate: AnyObject {
|
||||
func selectionButtonClicked(tag: Int)
|
||||
func titleFieldTextChanged(tag: Int, text: String)
|
||||
func titleFieldFinishedEditing(tag: Int, text: String)
|
||||
}
|
||||
|
||||
class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
|
||||
class PathsTableCellView: NSTableCellView, NSTextFieldDelegate,
|
||||
EditableNSTextFieldDelegate
|
||||
{
|
||||
var id: Int = -1
|
||||
weak var delegate: MyTableCellViewDelegate?
|
||||
weak var delegate: PathsTableCellViewDelegate?
|
||||
|
||||
private(set) var isEditing = false
|
||||
|
||||
public var titleField: NSTextField = {
|
||||
let field = NSTextField()
|
||||
public var titleField: EditableNSTextField = {
|
||||
let field = EditableNSTextField()
|
||||
field.isEditable = false
|
||||
field.maximumNumberOfLines = 1
|
||||
field.lineBreakMode = .byTruncatingTail
|
||||
@@ -38,6 +40,7 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
|
||||
super.init(frame: frameRect)
|
||||
|
||||
titleField.delegate = self
|
||||
titleField.auxiliaryDelegate = self
|
||||
|
||||
selectionButton.target = self
|
||||
selectionButton.action = #selector(makeSelection)
|
||||
@@ -74,15 +77,19 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
|
||||
}
|
||||
|
||||
public func startEditing() {
|
||||
guard !isEditing else { return }
|
||||
isEditing = true
|
||||
titleField.isEditable = true
|
||||
window?.makeFirstResponder(titleField)
|
||||
}
|
||||
|
||||
public func stopEditing() {
|
||||
guard isEditing else { return }
|
||||
isEditing = false
|
||||
titleField.isEditable = false
|
||||
window?.makeFirstResponder(nil)
|
||||
delegate?.titleFieldFinishedEditing(tag: id,
|
||||
text: titleField.stringValue)
|
||||
}
|
||||
|
||||
func controlTextDidChange(_ obj: Notification) {
|
||||
@@ -95,8 +102,6 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
|
||||
{
|
||||
if commandSelector == #selector(NSResponder.insertNewline(_:)) {
|
||||
stopEditing()
|
||||
delegate?.titleFieldFinishedEditing(tag: id,
|
||||
text: titleField.stringValue)
|
||||
return true
|
||||
} else if commandSelector == #selector(NSResponder.insertTab(_:)) {
|
||||
return true
|
||||
@@ -104,4 +109,8 @@ class MyTableCellView: NSTableCellView, NSTextFieldDelegate {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func lostFocus() {
|
||||
stopEditing()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
import Cocoa
|
||||
import Carbon
|
||||
import OSLog
|
||||
|
||||
class PopoverPanel: NSPanel {
|
||||
fileprivate static let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String(describing: PopoverPanel.self)
|
||||
)
|
||||
|
||||
override var canBecomeKey: Bool { true }
|
||||
|
||||
init(viewController: NSViewController) {
|
||||
|
||||
5
src/ProgramsTableView.swift
Normal file
5
src/ProgramsTableView.swift
Normal file
@@ -0,0 +1,5 @@
|
||||
import AppKit
|
||||
|
||||
final class ProgramsTableView: NSTableView {
|
||||
override var acceptsFirstResponder: Bool { false }
|
||||
}
|
||||
@@ -1,15 +1,9 @@
|
||||
import AppKit
|
||||
import Carbon
|
||||
import OSLog
|
||||
|
||||
class SearchViewController: NSViewController, NSTextFieldDelegate,
|
||||
NSPopoverDelegate, NSTableViewDataSource, NSTableViewDelegate
|
||||
{
|
||||
fileprivate static let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String(describing: SearchViewController.self)
|
||||
)
|
||||
|
||||
private var keyboardEvents: EventMonitor?
|
||||
|
||||
private var foundProgram: Program? = nil
|
||||
@@ -65,8 +59,8 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
|
||||
return scroll
|
||||
}()
|
||||
|
||||
private var programsTableView: MyNSTableView = {
|
||||
let table = MyNSTableView()
|
||||
private var programsTableView: ProgramsTableView = {
|
||||
let table = ProgramsTableView()
|
||||
|
||||
table.style = NSTableView.Style.plain
|
||||
table.backgroundColor = .clear
|
||||
@@ -262,9 +256,9 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
|
||||
configuration: config)
|
||||
{ [weak self] application, error in
|
||||
if let error = error {
|
||||
Self.logger.debug("\(error.localizedDescription)")
|
||||
print("\(error.localizedDescription)")
|
||||
} else {
|
||||
Self.logger.debug("Program opened successfully")
|
||||
print("Program opened successfully")
|
||||
// NOTE: This needs a window! Do not just copy-paste
|
||||
// this block elsewhere.
|
||||
DispatchQueue.main.async {
|
||||
@@ -384,7 +378,3 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
final class MyNSTableView: NSTableView {
|
||||
override var acceptsFirstResponder: Bool { false }
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import AppKit
|
||||
import Carbon
|
||||
import ServiceManagement
|
||||
import OSLog
|
||||
|
||||
class SettingsViewController: NSViewController, NSTextFieldDelegate,
|
||||
KeyDetectorButtonDelegate, NSTableViewDataSource, NSTableViewDelegate,
|
||||
MyTableCellViewDelegate
|
||||
PathsTableCellViewDelegate
|
||||
{
|
||||
fileprivate static let logger = Logger(
|
||||
subsystem: Bundle.main.bundleIdentifier!,
|
||||
category: String(describing: SettingsViewController.self)
|
||||
)
|
||||
|
||||
private var recording = false
|
||||
|
||||
// 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.headerView = nil
|
||||
table.allowsMultipleSelection = true
|
||||
table.allowsMultipleSelection = false
|
||||
table.allowsColumnReordering = false
|
||||
table.allowsColumnResizing = false
|
||||
table.allowsColumnSelection = false
|
||||
@@ -260,7 +254,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
|
||||
equalTo: shortcutsLabel.leadingAnchor),
|
||||
|
||||
tableScrollView.widthAnchor.constraint(equalToConstant: 350),
|
||||
tableScrollView.heightAnchor.constraint(equalToConstant: 100),
|
||||
tableScrollView.heightAnchor.constraint(equalToConstant: 150),
|
||||
tableScrollView.topAnchor.constraint(
|
||||
equalTo: pathsLabel.bottomAnchor),
|
||||
tableScrollView.leadingAnchor.constraint(
|
||||
@@ -336,6 +330,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
|
||||
modifiers = mods
|
||||
}
|
||||
|
||||
pathsTableView.reloadData()
|
||||
syncModifierButtons()
|
||||
launchAtLoginStatus()
|
||||
}
|
||||
@@ -355,6 +350,7 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
|
||||
UserDefaults.standard.set(keyCode, forKey: "keyCode")
|
||||
UserDefaults.standard.set(modifiers, forKey: "keyModifiers")
|
||||
|
||||
PathManager.shared.removeEmpty()
|
||||
PathManager.shared.savePaths()
|
||||
PathManager.shared.rebuildIndex()
|
||||
}
|
||||
@@ -454,29 +450,30 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
|
||||
|
||||
@objc
|
||||
private func affectPaths(_ sender: NSSegmentedControl) {
|
||||
// PERF: All of this could be written better.
|
||||
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.reloadData()
|
||||
|
||||
let row = PathManager.shared.userPaths.count-1
|
||||
pathsTableView.insertRows(at: IndexSet(integer: row),
|
||||
withAnimation: [])
|
||||
}
|
||||
pathsTableView.scrollRowToVisible(row)
|
||||
pathsTableView.selectRowIndexes(IndexSet(integer: row),
|
||||
byExtendingSelection: false)
|
||||
pathsTableView.scrollRowToVisible(row)
|
||||
(pathsTableView.view(atColumn: 0, row: row,
|
||||
makeIfNecessary: false) as? MyTableCellView)?
|
||||
makeIfNecessary: false) as? PathsTableCellView)?
|
||||
.startEditing()
|
||||
break
|
||||
case 1:
|
||||
var toRemove: [String] = []
|
||||
for row in pathsTableView.selectedRowIndexes {
|
||||
toRemove.append(PathManager.shared.userPaths[row])
|
||||
}
|
||||
PathManager.shared.userPaths.removeAll(
|
||||
where: { toRemove.contains($0) })
|
||||
if pathsTableView.selectedRow > -1 {
|
||||
PathManager.shared.userPaths
|
||||
.remove(at: pathsTableView.selectedRow)
|
||||
pathsTableView.reloadData()
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
@@ -492,19 +489,19 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
|
||||
|
||||
if let cell = pathsTableView.view(atColumn: 0,
|
||||
row: pathsTableView.clickedRow,
|
||||
makeIfNecessary: false) as? MyTableCellView
|
||||
makeIfNecessary: false) as? PathsTableCellView
|
||||
{
|
||||
cell.startEditing()
|
||||
}
|
||||
}
|
||||
|
||||
func titleFieldFinishedEditing(tag: Int, text: String) {
|
||||
PathManager.shared.userPaths[tag] = text
|
||||
if PathManager.shared.userPaths[tag].isEmpty {
|
||||
if text.isEmpty {
|
||||
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) {
|
||||
@@ -543,20 +540,13 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate,
|
||||
{
|
||||
let rect = NSRect(x: 0, y: 0,
|
||||
width: tableColumn!.width, height: 20)
|
||||
let cell = MyTableCellView(frame: rect)
|
||||
let cell = PathsTableCellView(frame: rect)
|
||||
cell.titleField.stringValue = PathManager.shared.userPaths[row]
|
||||
cell.delegate = self
|
||||
cell.id = row
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
/*
|
||||
let selectedRow = tableView.selectedRow
|
||||
if selectedRow >= 0 {
|
||||
print("Selected: \(items[selectedRow])")
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user