diff options
Diffstat (limited to 'kies')
| -rw-r--r-- | kies/DataSource.swift | 43 | ||||
| -rw-r--r-- | kies/Info.plist | 32 | ||||
| -rw-r--r-- | kies/InputField.swift | 67 | ||||
| -rw-r--r-- | kies/Layout.swift | 44 | ||||
| -rw-r--r-- | kies/PromptText.swift | 28 | ||||
| -rw-r--r-- | kies/Settings.swift | 48 | ||||
| -rw-r--r-- | kies/SwiftLCS.swift | 268 | ||||
| -rw-r--r-- | kies/TableView.swift | 89 | ||||
| -rw-r--r-- | kies/Window.swift | 51 | ||||
| -rw-r--r-- | kies/constants.swift | 13 | ||||
| -rw-r--r-- | kies/main.swift | 132 |
11 files changed, 815 insertions, 0 deletions
diff --git a/kies/DataSource.swift b/kies/DataSource.swift new file mode 100644 index 0000000..23c1f8e --- /dev/null +++ b/kies/DataSource.swift @@ -0,0 +1,43 @@ +// +// Sorter.swift +// kies +// +// Created by Thomas Billiet on 11/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa + +class DataSource: NSObject, NSTableViewDataSource { + var items = [String]() + var sortedItems = [String]() + var matches = [String: String]() + var scores = [String: Float]() + + func updateItems(_ items: [String]) { + self.items = items + self.sortedItems = items + } + + func updateSort(query: String) { + for item in items { + let match = item.lowercased().longestCommonSubsequence(query.lowercased()) + matches[item] = match + scores[item] = Float(match.count) / Float(query.count) + } + + sortedItems = items.sorted(by: { (a, b) -> Bool in + scores[a]! > scores[b]! + }) + } + + func numberOfRows(in tableView: NSTableView) -> Int { + return sortedItems.count + } + + func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { + return sortedItems[row] + } + +} diff --git a/kies/Info.plist b/kies/Info.plist new file mode 100644 index 0000000..bfedb9a --- /dev/null +++ b/kies/Info.plist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSApplicationCategoryType</key> + <string>public.app-category.productivity</string> + <key>LSMinimumSystemVersion</key> + <string>$(MACOSX_DEPLOYMENT_TARGET)</string> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2019 Thomas Billiet. All rights reserved.</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> +</dict> +</plist> diff --git a/kies/InputField.swift b/kies/InputField.swift new file mode 100644 index 0000000..6e8e27c --- /dev/null +++ b/kies/InputField.swift @@ -0,0 +1,67 @@ +// +// KiesInputField.swift +// kies +// +// Created by Thomas Billiet on 08/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa + +class InputField: NSTextField, NSTextFieldDelegate { + var appDelegate: AppDelegate! + + init(appDelegate: AppDelegate) { + super.init(frame: layouts.inputRect) + self.appDelegate = appDelegate + stringValue = "" + isEditable = true + isSelectable = true + delegate = self + bezelStyle = .squareBezel + isBordered = false + drawsBackground = false + focusRingType = .none + target = self + font = settings.font + allowsEditingTextAttributes = false + isAutomaticTextCompletionEnabled = false + allowsDefaultTighteningForTruncation = false + maximumNumberOfLines = 1 + cell?.wraps = false + cell?.isScrollable = true + autoresizingMask = [NSView.AutoresizingMask.width] + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { + if (commandSelector == #selector(NSStandardKeyBindingResponding.cancelOperation(_:))){ + appDelegate.cancel() + return true + } + if (commandSelector == #selector(NSStandardKeyBindingResponding.insertNewline(_:))){ + appDelegate.handleSelect() + return true + } + if (commandSelector == #selector(NSStandardKeyBindingResponding.moveUp(_:))){ + appDelegate.handleMoveUp() + return true + } + if (commandSelector == #selector(NSStandardKeyBindingResponding.moveDown(_:))){ + appDelegate.handleMoveDown() + return true + } + if (commandSelector == #selector(NSStandardKeyBindingResponding.insertTab(_:))){ + return true + } + return false + } + + func controlTextDidChange(_ obj: Notification) { + appDelegate.handleInputChange(input: stringValue) + } +} diff --git a/kies/Layout.swift b/kies/Layout.swift new file mode 100644 index 0000000..cf04eb0 --- /dev/null +++ b/kies/Layout.swift @@ -0,0 +1,44 @@ +// +// Layout.swift +// keuze +// +// Created by Thomas Billiet on 14/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation + +class Layout { + let DIVIDER_HEIGHT: CGFloat = 9 + + var lineHeight: CGFloat { + return ceil(NSString(string: "TEST").size(withAttributes: [.font: settings.font]).height) + 0; + } + + var windowRect: NSRect { + return NSMakeRect(0, 0, settings.windowWidth, settings.windowHeight) + } + + var containerRect: NSRect { + return NSMakeRect(settings.windowPadding, settings.windowPadding, windowRect.width - settings.windowPadding * 2, windowRect.height - settings.windowPadding * 2) + } + + var listRect: NSRect { + return NSMakeRect(containerRect.minX, containerRect.minY, containerRect.width, containerRect.height - lineHeight - DIVIDER_HEIGHT) + } + + var dividerRect: NSRect { + return NSMakeRect(containerRect.minX, listRect.maxY + DIVIDER_HEIGHT / 2, containerRect.width, 1) + } + + var promptRect: NSRect { + let stringWidth = ceil(NSString(string: settings.promptText!).size(withAttributes: [.font: settings.font]).width) + 4; + return NSMakeRect(containerRect.minX, containerRect.maxY - lineHeight, stringWidth, lineHeight) + } + + var inputRect: NSRect { + return NSMakeRect(promptRect.maxX, containerRect.maxY - lineHeight, containerRect.width - promptRect.width, lineHeight) + } +} + +let layouts = Layout() diff --git a/kies/PromptText.swift b/kies/PromptText.swift new file mode 100644 index 0000000..7cd6750 --- /dev/null +++ b/kies/PromptText.swift @@ -0,0 +1,28 @@ +// +// KiesPromptText.swift +// kies +// +// Created by Thomas Billiet on 08/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa + +class PromptText: NSTextField { + init(text: String) { + super.init(frame: layouts.promptRect) + stringValue = text + isEditable = false + drawsBackground = false + isSelectable = false + bezelStyle = .squareBezel + font = settings.font + isBordered = false + autoresizingMask = [NSView.AutoresizingMask.width] + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/kies/Settings.swift b/kies/Settings.swift new file mode 100644 index 0000000..ab2c3df --- /dev/null +++ b/kies/Settings.swift @@ -0,0 +1,48 @@ +// +// Settings.swift +// keuze +// +// Created by Thomas Billiet on 14/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa + +class Settings { + + var promptText: String? { + var prompt = UserDefaults.standard.string(forKey: "p") + if (prompt != nil) { + prompt = prompt! + ":" + } + return prompt + } + + var font: NSFont? { + let fn = UserDefaults.standard.string(forKey: "fn") + let fs = UserDefaults.standard.integer(forKey: "fs") + let fontSize = fs == 0 ? 14 : CGFloat(fs) + if (fn == nil) { + return NSFont.systemFont(ofSize: fontSize) + } + return NSFont(name: fn!, size: fontSize) + } + + var windowPadding: CGFloat { + let val = UserDefaults.standard.integer(forKey: "pd") + return val == 0 ? 12 : CGFloat(val) + } + + var windowHeight: CGFloat { + let val = UserDefaults.standard.integer(forKey: "h") + return val == 0 ? 310 : CGFloat(val) + } + + var windowWidth: CGFloat { + let val = UserDefaults.standard.integer(forKey: "w") + return val == 0 ? 500 : CGFloat(val) + } +} + +let settings = Settings() diff --git a/kies/SwiftLCS.swift b/kies/SwiftLCS.swift new file mode 100644 index 0000000..2587c93 --- /dev/null +++ b/kies/SwiftLCS.swift @@ -0,0 +1,268 @@ +// +// The MIT License (MIT) +// +// Copyright (c) 2015 Tommaso Madonia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +import Foundation + +/** + A generic struct that represents a diff between two collections. + */ +public struct Diff<Index> { + + /// The indexes whose corresponding values in the old collection are in the LCS. + public var commonIndexes: [Index] { + return self._commonIndexSet.indexes + } + + /// The indexes whose corresponding values in the new collection are not in the LCS. + public var addedIndexes: [Index] { + return self._addedIndexSet.indexes + } + + /// The indexes whose corresponding values in the old collection are not in the LCS. + public var removedIndexes: [Index] { + return self._removedIndexSet.indexes + } + + /// Construct the `Diff` between two given collections. + public init<C: Collection>(_ old: C, _ new: C) where C.Index == Index, C.Element: Equatable { + self = old.diff(new) + } + + fileprivate let _commonIndexSet: DiffIndexSet<Index> + fileprivate let _addedIndexSet: DiffIndexSet<Index> + fileprivate let _removedIndexSet: DiffIndexSet<Index> + + fileprivate init(commonIndexes: DiffIndexSet<Index>, addedIndexes: DiffIndexSet<Index>, removedIndexes: DiffIndexSet<Index>) { + self._commonIndexSet = commonIndexes + self._addedIndexSet = addedIndexes + self._removedIndexSet = removedIndexes + } + +} + +/** + An extension of `Diff`, which adds support for `IndexSet`. + */ +public extension Diff where Index: Strideable, Index.Stride: SignedInteger { + + /// The indexes whose corresponding values in the old collection are in the LCS. + var commonIndexSet: IndexSet { + return self._commonIndexSet.indexSet + } + + /// The indexes whose corresponding values in the new collection are not in the LCS. + var addedIndexSet: IndexSet { + return self._addedIndexSet.indexSet + } + + /// The indexes whose corresponding values in the old collection are not in the LCS. + var removedIndexSet: IndexSet { + return self._removedIndexSet.indexSet + } + +} + +private struct DiffIndexSet<Index> { + + let startIndex: Index + let indexes: [Index] + + init(_ indexes: [Index], startIndex: Index) { + self.indexes = indexes + self.startIndex = startIndex + } + +} + +private extension DiffIndexSet where Index: Strideable, Index.Stride: SignedInteger { + + var indexSet: IndexSet { + let indexes = self.indexes.map { Int((self.startIndex..<$0).count) } + + return IndexSet(indexes) + } + +} + +// MARK: - +/** + An extension of `Collection`, which calculates the diff between two collections. + */ +public extension Collection where Element: Equatable { + + /** + Returns the diff between two collections. + + - complexity: O(mn) where `m` and `n` are the lengths of the receiver and the given collection. + - parameter collection: The collection with which to compare the receiver. + - returns: The diff between the receiver and the given collection. + */ + func diff(_ otherCollection: Self) -> Diff<Index> { + let count = self.count + let commonIndexes = self.longestCommonSubsequence(otherCollection, selfCount: count) + + var removedIndexes: [Index] = [] + removedIndexes.reserveCapacity(count - commonIndexes.count) + + var addedIndexes: [Index] = [] + + var index = self.startIndex + var otherIndex = otherCollection.startIndex + var commonIndexesIterator = commonIndexes.makeIterator() + var commonIndex = commonIndexesIterator.next() + while index != self.endIndex { + if commonIndex == index { + commonIndex = commonIndexesIterator.next() + + while otherIndex != otherCollection.endIndex && otherCollection[otherIndex] != self[index] { + addedIndexes.append(otherIndex) + otherIndex = otherCollection.index(after: otherIndex) + } + + if otherIndex != otherCollection.endIndex { + otherIndex = otherCollection.index(after: otherIndex) + } + } else { + removedIndexes.append(index) + } + + index = self.index(after: index) + } + + while otherIndex != otherCollection.endIndex { + addedIndexes.append(otherIndex) + otherIndex = otherCollection.index(after: otherIndex) + } + + return Diff(commonIndexes: DiffIndexSet(commonIndexes, startIndex: self.startIndex), + addedIndexes: DiffIndexSet(addedIndexes, startIndex: otherCollection.startIndex), + removedIndexes: DiffIndexSet(removedIndexes, startIndex: self.startIndex)) + } + + // MARK: Private functions + + private func prefix(_ otherCollection: Self, count: Int, suffix: Int) -> (Int, [Index]) { + var iterator = self.makeIterator() + var otherIterator = otherCollection.makeIterator() + + var prefix = self.startIndex + let endIndex = self.index(self.startIndex, offsetBy: count - suffix) + while let lhs = iterator.next(), let rhs = otherIterator.next(), lhs == rhs, prefix < endIndex { + prefix = self.index(after: prefix) + } + + let prefixLength = self.distance(from: self.startIndex, to: prefix) + return (prefixLength, Array(self.indices.prefix(prefixLength))) + } + + private func suffix(_ otherCollection: Self, count: Int) -> (Int, [Index]) { + var iterator = self.reversed().makeIterator() + var otherIterator = otherCollection.reversed().makeIterator() + + var offset = count + var suffix = self.index(self.startIndex, offsetBy: offset) + while let lhs = iterator.next(), let rhs = otherIterator.next(), lhs == rhs { + offset &-= 1 + suffix = self.index(self.startIndex, offsetBy: offset) + } + + let suffixLength = self.distance(from: suffix, to: self.endIndex) + return (suffixLength, Array(self.indices.suffix(suffixLength))) + } + + private func computeLCS(_ otherCollection: Self, prefixLength: Int, suffixLength: Int, count: Int) -> [Index] { + let rows = Int(count - prefixLength - suffixLength) + 1 + let columns = Int(otherCollection.count - prefixLength - suffixLength) + 1 + + guard rows > 1 && columns > 1 else { + return [] + } + + var lengths = Array(repeating: 0, count: rows * columns) + var index = self.index(self.startIndex, offsetBy: prefixLength) + for i in 0..<rows &- 1 { + var otherIndex = otherCollection.index(otherCollection.startIndex, offsetBy: prefixLength) + for j in 0..<columns &- 1 { + if self[index] == otherCollection[otherIndex] { + lengths[(i &+ 1) &* columns &+ j &+ 1] = lengths[i &* columns &+ j] &+ 1 + } else { + let lhs = lengths[(i &+ 1) &* columns &+ j] + let rhs = lengths[i &* columns &+ j &+ 1] + lengths[(i &+ 1) &* columns &+ j &+ 1] = Swift.max(lhs, rhs) + } + + otherIndex = otherCollection.index(after: otherIndex) + } + + index = self.index(after: index) + } + + var commonIndexes: [Index] = [] + + var indexOffset = count - suffixLength + index = self.index(self.startIndex, offsetBy: indexOffset) + var (i, j) = (rows &- 1, columns &- 1) + while i != 0 && j != 0 { + if lengths[i &* columns &+ j] == lengths[(i &- 1) &* columns &+ j] { + i = i &- 1 + indexOffset &-= 1 + index = self.index(self.startIndex, offsetBy: indexOffset) + } else if lengths[i &* columns &+ j] == lengths[i &* columns &+ j &- 1] { + j = j &- 1 + } else { + indexOffset &-= 1 + index = self.index(self.startIndex, offsetBy: indexOffset) + commonIndexes.append(index) + (i, j) = (i &- 1, j &- 1) + } + } + + return commonIndexes.reversed() + } + + fileprivate func longestCommonSubsequence(_ otherCollection: Self, selfCount count: Int) -> [Index] { + let (suffix, suffixIndexes) = self.suffix(otherCollection, count: count) + let (prefix, prefixIndexes) = self.prefix(otherCollection, count: count, suffix: suffix) + + return prefixIndexes + self.computeLCS(otherCollection, prefixLength: prefix, suffixLength: suffix, count: count) + suffixIndexes + } + +} + +// MARK: - +/** + An extension of `RangeReplaceableCollection`, which calculates the longest common subsequence between two collections. + */ +public extension RangeReplaceableCollection where Element: Equatable { + + /** + Returns the longest common subsequence between two collections. + + - parameter collection: The collection with which to compare the receiver. + - returns: The longest common subsequence between the receiver and the given collection. + */ + func longestCommonSubsequence(_ collection: Self) -> Self { + return Self(self.longestCommonSubsequence(collection, selfCount: self.count).map { self[$0] }) + } + +} diff --git a/kies/TableView.swift b/kies/TableView.swift new file mode 100644 index 0000000..cd7de9f --- /dev/null +++ b/kies/TableView.swift @@ -0,0 +1,89 @@ +// +// KiesTableView.swift +// kies +// +// Created by Thomas Billiet on 08/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa + +class TableView: NSTableView, NSTableViewDelegate { + + let column = NSTableColumn(identifier: LIST_COL_ID) + + init(dataSource: DataSource) { + super.init(frame: .zero) + self.dataSource = dataSource + headerView = nil + allowsEmptySelection = false + allowsMultipleSelection = false + allowsTypeSelect = false + selectionHighlightStyle = .none + delegate = self + backgroundColor = .clear + + column.isEditable = false + column.width = layouts.listRect.width + addTableColumn(column) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func reloadData() { + super.reloadData() + scrollToBeginningOfDocument(self) + selectRow(0) + } + + func selectRow(_ row: Int) { + let indexSet = IndexSet(integer: row) + selectRowIndexes(indexSet, byExtendingSelection: false) + } + + func selectRowAbove() { + let row = selectedRow - 1 + if (row >= 0) { + selectRow(row) + scrollRowToVisible(row) + } + } + + func selectRowBelow() { + let row = selectedRow + 1 + if (row < numberOfRows) { + selectRow(row) + scrollRowToVisible(row) + } + } + + func tableView(_ tableView: NSTableView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, row: Int) { + let c = cell as! NSTextFieldCell + let value = c.stringValue + c.font = settings.font + c.usesSingleLineMode = true + if (row == selectedRow) { + c.backgroundColor = .alternateSelectedControlColor + c.textColor = .alternateSelectedControlTextColor + c.drawsBackground = true + } else { + c.drawsBackground = false + c.textColor = .textColor + } + let match = (dataSource as! DataSource).matches[value] + if (match != nil && !match!.isEmpty) { + let range = (value.lowercased() as NSString).range(of: match!) + let attribute = NSMutableAttributedString.init(string: value) + attribute.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue , range: range) + c.attributedStringValue = attribute + } + + } + + func tableViewSelectionDidChange(_ notification: Notification) { + } + +} diff --git a/kies/Window.swift b/kies/Window.swift new file mode 100644 index 0000000..078bdad --- /dev/null +++ b/kies/Window.swift @@ -0,0 +1,51 @@ +// +// NSWindowExtension.swift +// kies +// +// Created by Thomas Billiet on 08/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa + +class Window: NSWindow { + override var canBecomeKey: Bool { + return true + } + + override var canBecomeMain: Bool { + return true + } + + init() { + super.init(contentRect: layouts.windowRect, styleMask: .borderless, backing: .buffered, defer: false) + isOpaque = true + canHide = false + isMovable = false + title = WINDOW_TITLE + level = NSWindow.Level.floating + hasShadow = true + backgroundColor = .clear + + let visualEffect = NSVisualEffectView() + visualEffect.blendingMode = .withinWindow + visualEffect.state = .active + visualEffect.material = .appearanceBased + visualEffect.frame = layouts.windowRect + contentView = visualEffect + } + + public func showWindow() { + positionWindowAtCenter() + makeMain() + makeKeyAndOrderFront(self) + } + + public func positionWindowAtCenter(){ + let xPos = NSWidth((self.screen?.frame)!)/2 - NSWidth(self.frame)/2 + let yPos = NSHeight((self.screen?.frame)!)/2 - NSHeight(self.frame)/2 + let frame = NSMakeRect(xPos, yPos, NSWidth(self.frame), NSHeight(self.frame)) + self.setFrame(frame, display: true) + } +} diff --git a/kies/constants.swift b/kies/constants.swift new file mode 100644 index 0000000..a142eac --- /dev/null +++ b/kies/constants.swift @@ -0,0 +1,13 @@ +// +// Constants.swift +// kies +// +// Created by Thomas Billiet on 08/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa + +let WINDOW_TITLE = "Kies" +let LIST_COL_ID = NSUserInterfaceItemIdentifier(rawValue: "column"); diff --git a/kies/main.swift b/kies/main.swift new file mode 100644 index 0000000..a20922d --- /dev/null +++ b/kies/main.swift @@ -0,0 +1,132 @@ +// +// main.swift +// kies +// +// Created by Thomas Billiet on 08/04/2019. +// Copyright © 2019 Thomas Billiet. All rights reserved. +// + +import Foundation +import Cocoa +import Darwin + +class AppDelegate: NSObject, NSApplicationDelegate { + + var window: Window! + var dataSource: DataSource! + var promptText: PromptText! + var divider: NSBox! + var inputField: InputField! + var tableView: TableView! + var scrollView: NSScrollView! + + func applicationDidBecomeActive(_ notification: Notification) { + + } + + func applicationDidResignActive(_ notification: Notification) { + cancel(); + } + + func receiveStdin() -> [String] { + var lines: [String] = [] + while let line = readLine() { + lines.append(line) + } + return lines + } + + func applicationDidFinishLaunching(_ notification: Notification) + { + if (settings.promptText == nil) { + fputs("Please provide a prompt text\n", stderr) + exit(1) + } + if (settings.font == nil) { + fputs("The provided font could not be loaded\n", stderr) + exit(1) + } + let items = receiveStdin() + if (items.count == 0) { + fputs("No data was received on stdin\n", stderr) + exit(1) + } + + window = Window() + dataSource = DataSource() + dataSource.updateItems(items) + + setupPromptText() + setupDivider() + setupInputField() + setupList() + + window.showWindow() + app.activate(ignoringOtherApps: true) + } + + func setupPromptText() { + promptText = PromptText(text: settings.promptText!) + window.contentView!.addSubview(promptText) + } + + func setupDivider() { + divider = NSBox(frame: layouts.dividerRect) + divider.boxType = .custom + divider.fillColor = .lightGray + divider.borderWidth = 0 + window.contentView!.addSubview(divider) + } + + func setupInputField() { + inputField = InputField(appDelegate: self); + window.contentView!.addSubview(inputField) + window.makeFirstResponder(inputField) + } + + func handleInputChange(input: String) { + dataSource.updateSort(query: input) + tableView.reloadData() + } + + func handleMoveUp() { + tableView.selectRowAbove() + } + + func handleMoveDown() { + tableView.selectRowBelow() + } + + func handleSelect() { + let value = dataSource.sortedItems[tableView.selectedRow] + fputs(value, stdout) + cancel() + } + + func setupList() { + scrollView = NSScrollView(frame: layouts.listRect) + tableView = TableView(dataSource: dataSource) + + scrollView.horizontalScrollElasticity = .none + scrollView.verticalScrollElasticity = .none + scrollView.hasVerticalScroller = true + scrollView.hasHorizontalScroller = false + scrollView.drawsBackground = false + scrollView.autoresizingMask = [NSView.AutoresizingMask.width, NSView.AutoresizingMask.height] + scrollView.documentView = tableView + scrollView.automaticallyAdjustsContentInsets = false + window.contentView!.addSubview(scrollView) + } + + func cancel() { + exit(0) + } +} + + +let app = NSApplication.shared +app.setActivationPolicy(.accessory) +let delegate = AppDelegate() +app.delegate = delegate + +_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) |
