feat: add Finder, ranking reset, and quit actions
This commit is contained in:
@@ -18,4 +18,17 @@ enum AppLauncher {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func quit(_ url: URL) throws {
|
||||
let path = url.standardizedFileURL.resolvingSymlinksInPath().path
|
||||
for application in NSWorkspace.shared.runningApplications {
|
||||
guard application.bundleURL?.standardizedFileURL.resolvingSymlinksInPath().path == path,
|
||||
!application.isTerminated else { continue }
|
||||
guard application.terminate() else {
|
||||
throw NSError(domain: "Conjure", code: 1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "The application refused the quit request. Save your work and quit it manually."
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
|
||||
private let search = ActionsSearchField()
|
||||
private let entries = [
|
||||
ActionEntry(title: "Open", symbol: "arrow.up.forward.app", color: SunsetPalette.text),
|
||||
ActionEntry(title: "Show in Finder", symbol: "folder", color: SunsetPalette.text),
|
||||
ActionEntry(title: "Reset ranking", symbol: "arrow.counterclockwise", color: SunsetPalette.text),
|
||||
ActionEntry(title: "Quit application", symbol: "stop.circle", color: SunsetPalette.text),
|
||||
ActionEntry(title: "Uninstall application", symbol: "trash", color: .systemRed)
|
||||
]
|
||||
private var visibleEntries = [0, 1]
|
||||
private var visibleEntries: [Int] = []
|
||||
private var selectedEntry: Int? = 0
|
||||
private var rowTopConstraints: [NSLayoutConstraint] = []
|
||||
private var heightConstraint: NSLayoutConstraint?
|
||||
@@ -21,6 +24,9 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
|
||||
private var transitionGeneration = 0
|
||||
private var panel: ActionsPanelView { view as! ActionsPanelView }
|
||||
var open: (() -> Void)?
|
||||
var showInFinder: (() -> Void)?
|
||||
var resetRanking: (() -> Void)?
|
||||
var quit: (() -> Void)?
|
||||
var uninstall: (() -> Void)?
|
||||
var closed: (() -> Void)?
|
||||
var isShown: Bool { viewIfLoaded?.superview != nil && !isClosing }
|
||||
@@ -88,7 +94,7 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
|
||||
top
|
||||
])
|
||||
}
|
||||
let height = view.heightAnchor.constraint(equalToConstant: 116 + LauncherRowView.rowHeight)
|
||||
let height = view.heightAnchor.constraint(equalToConstant: 116 + CGFloat(entries.count - 1) * LauncherRowView.rowHeight)
|
||||
heightConstraint = height
|
||||
NSLayoutConstraint.activate([
|
||||
view.widthAnchor.constraint(equalToConstant: 360), height,
|
||||
@@ -234,7 +240,14 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
|
||||
private func chooseAction(at index: Int) {
|
||||
guard isShown, visibleEntries.contains(index) else { return }
|
||||
close()
|
||||
if index == 0 { open?() } else { uninstall?() }
|
||||
switch index {
|
||||
case 0: open?()
|
||||
case 1: showInFinder?()
|
||||
case 2: resetRanking?()
|
||||
case 3: quit?()
|
||||
case 4: uninstall?()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -505,6 +505,16 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
guard !isLaunching, launcherFrameBeforeReview == nil, window?.attachedSheet == nil, results.indices.contains(tableView.selectedRow) else { return }
|
||||
let application = results[tableView.selectedRow]
|
||||
actions.open = { [weak self] in self?.openApplication(application) }
|
||||
actions.showInFinder = { [weak self] in
|
||||
self?.dismiss(restoringFocus: false)
|
||||
NSWorkspace.shared.activateFileViewerSelecting([application.url])
|
||||
}
|
||||
actions.resetRanking = { [weak self] in
|
||||
guard let self else { return }
|
||||
self.history.resetRanking(of: application)
|
||||
self.updateResults(preservingSelection: true)
|
||||
}
|
||||
actions.quit = { [weak self] in self?.quitApplication(application) }
|
||||
actions.uninstall = { [weak self] in self?.reviewUninstall(application) }
|
||||
actions.show(for: application, relativeTo: actionsButton)
|
||||
openButton.keyEquivalent = actions.isShown ? "" : "\r"
|
||||
@@ -576,6 +586,17 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
openApplication(results[tableView.selectedRow])
|
||||
}
|
||||
|
||||
private func quitApplication(_ application: Application) {
|
||||
do {
|
||||
try AppLauncher.quit(application.url)
|
||||
dismiss()
|
||||
} catch {
|
||||
let alert = NSAlert(error: error)
|
||||
alert.messageText = "Couldn’t quit \(application.name)"
|
||||
if let window { alert.beginSheetModal(for: window) }
|
||||
}
|
||||
}
|
||||
|
||||
private func openApplication(_ application: Application) {
|
||||
guard !isLaunching else { return }
|
||||
isLaunching = true
|
||||
|
||||
@@ -31,6 +31,15 @@ public final class LaunchHistory {
|
||||
if record.count < Int.max { record.count += 1 }
|
||||
record.lastLaunch = timestamp
|
||||
usage[application.id] = record
|
||||
save()
|
||||
}
|
||||
|
||||
public func resetRanking(of application: Application) {
|
||||
usage.removeValue(forKey: application.id)
|
||||
save()
|
||||
}
|
||||
|
||||
private func save() {
|
||||
var saved: [String: [String: Any]] = [:]
|
||||
saved.reserveCapacity(usage.count)
|
||||
for (url, record) in usage {
|
||||
|
||||
Reference in New Issue
Block a user