176 lines
6.6 KiB
Swift
176 lines
6.6 KiB
Swift
import AppKit
|
|
import OSLog
|
|
|
|
fileprivate enum ViewConstants {
|
|
static let spacing2: CGFloat = 2
|
|
static let spacing10: CGFloat = 10
|
|
static let spacing20: CGFloat = 20
|
|
static let spacing40: CGFloat = 40
|
|
}
|
|
|
|
class SearchViewController: NSViewController, NSTextFieldDelegate {
|
|
fileprivate static let logger = Logger(
|
|
subsystem: Bundle.main.bundleIdentifier!,
|
|
category: String(describing: SearchViewController.self)
|
|
)
|
|
|
|
var foundProgram: Program? = nil
|
|
|
|
private var appIconImage: NSImageView = {
|
|
//let image = NSImageView(image: NSApp.applicationIconImage)
|
|
let image = NSImageView()
|
|
image.image = NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath)
|
|
image.imageScaling = .scaleAxesIndependently
|
|
image.translatesAutoresizingMaskIntoConstraints = false
|
|
return image
|
|
}()
|
|
|
|
private var searchInput: EditableNSTextField = {
|
|
let textField = EditableNSTextField()
|
|
textField.placeholderString = "Search programs . . ."
|
|
textField.bezelStyle = .roundedBezel
|
|
textField.translatesAutoresizingMaskIntoConstraints = false
|
|
return textField
|
|
}()
|
|
|
|
private var programsLabel: NSTextField = {
|
|
let textField = NSTextField()
|
|
textField.stringValue = ""
|
|
textField.isEditable = false
|
|
textField.isBezeled = false
|
|
textField.drawsBackground = false
|
|
textField.alignment = .left
|
|
textField.font = NSFont.systemFont(ofSize: NSFontDescriptor.preferredFontDescriptor(forTextStyle: .body).pointSize, weight: .bold)
|
|
textField.translatesAutoresizingMaskIntoConstraints = false
|
|
return textField
|
|
}()
|
|
|
|
private var settingsButton: NSButton = {
|
|
let button = NSButton()
|
|
button.image = systemImage("gearshape.fill", .title2, .large, .init(paletteColors: [.white, .systemRed]))
|
|
button.isBordered = false
|
|
button.action = #selector(openSettings)
|
|
button.sizeToFit()
|
|
button.toolTip = "Quit"
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
return button
|
|
}()
|
|
|
|
|
|
private func addSubviews() {
|
|
view.addSubview(appIconImage)
|
|
view.addSubview(searchInput)
|
|
view.addSubview(programsLabel)
|
|
view.addSubview(settingsButton)
|
|
}
|
|
|
|
private func setConstraints() {
|
|
NSLayoutConstraint.activate([
|
|
appIconImage.widthAnchor.constraint(equalToConstant: 70),
|
|
appIconImage.heightAnchor.constraint(equalTo: appIconImage.widthAnchor, multiplier: 1),
|
|
|
|
appIconImage.topAnchor.constraint(equalTo: view.topAnchor, constant: ViewConstants.spacing20),
|
|
appIconImage.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -ViewConstants.spacing10),
|
|
appIconImage.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: ViewConstants.spacing10),
|
|
|
|
searchInput.widthAnchor.constraint(equalToConstant: 300),
|
|
searchInput.topAnchor.constraint(equalTo: appIconImage.topAnchor),
|
|
searchInput.leadingAnchor.constraint(equalTo: appIconImage.trailingAnchor, constant: ViewConstants.spacing10),
|
|
|
|
settingsButton.firstBaselineAnchor.constraint(equalTo: searchInput.firstBaselineAnchor),
|
|
settingsButton.leadingAnchor.constraint(equalTo: searchInput.trailingAnchor, constant: ViewConstants.spacing10),
|
|
settingsButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -ViewConstants.spacing10),
|
|
|
|
programsLabel.topAnchor.constraint(equalTo: searchInput.bottomAnchor, constant: ViewConstants.spacing10),
|
|
programsLabel.leadingAnchor.constraint(equalTo: appIconImage.trailingAnchor, constant: ViewConstants.spacing10),
|
|
programsLabel.trailingAnchor.constraint(equalTo: searchInput.trailingAnchor),
|
|
|
|
])
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
searchInput.delegate = self
|
|
|
|
addSubviews()
|
|
setConstraints()
|
|
}
|
|
|
|
override func viewDidAppear() {
|
|
super.viewDidAppear()
|
|
|
|
self.view.window?.center()
|
|
}
|
|
|
|
override func loadView() {
|
|
self.view = NSView()
|
|
}
|
|
|
|
//private func fetchIcon() {
|
|
// for key in resultPaths.keys {
|
|
// }
|
|
//}
|
|
|
|
@objc
|
|
private func openSettings() {
|
|
}
|
|
|
|
func controlTextDidChange(_ obj: Notification) {
|
|
var list = ""
|
|
|
|
let programs = delegate.programs
|
|
for program in programs {
|
|
if program.name.lowercased().contains(searchInput.stringValue.lowercased()) {
|
|
if !list.isEmpty {
|
|
list += ", "
|
|
}
|
|
list += program.name + program.ext
|
|
foundProgram = program
|
|
break
|
|
} else {
|
|
foundProgram = nil
|
|
}
|
|
}
|
|
|
|
if let program = foundProgram {
|
|
programsLabel.stringValue = program.name + program.ext
|
|
|
|
let url = URL(fileURLWithPath: program.path).appendingPathComponent(program.name+program.ext)
|
|
appIconImage.image = NSWorkspace.shared.icon(forFile: url.path)
|
|
} else {
|
|
programsLabel.stringValue = ""
|
|
|
|
appIconImage.image = NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath)
|
|
}
|
|
}
|
|
|
|
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
|
|
if commandSelector == #selector(NSResponder.insertNewline(_:)) {
|
|
if let program = foundProgram {
|
|
let url = URL(fileURLWithPath: program.path).appendingPathComponent(program.name+program.ext)
|
|
let config = NSWorkspace.OpenConfiguration()
|
|
NSWorkspace.shared.openApplication(at: url, configuration: config, completionHandler: { [weak self] application, error in
|
|
if let error = error {
|
|
Self.logger.debug("Failed to open application: \(error.localizedDescription)")
|
|
} else {
|
|
Self.logger.debug("Application opened successfully")
|
|
DispatchQueue.main.async {
|
|
if let window = self?.view.window {
|
|
window.resignKey()
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
// TODO: Send this whenever SearchViewController becomes visible.
|
|
NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self)
|
|
return true
|
|
} else if commandSelector == #selector(NSResponder.insertTab(_:)) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
}
|