feat: distinguish duplicate application names by folder
This commit is contained in:
@@ -11,6 +11,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
private let history: LaunchHistory
|
||||
private var applications: [Application] = []
|
||||
private var results: [Application] = []
|
||||
private var locationLabels: [URL: String] = [:]
|
||||
private var discoveryTask: Task<Void, Never>?
|
||||
private var isLaunching = false
|
||||
|
||||
@@ -228,11 +229,34 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
}.value
|
||||
guard let self else { return }
|
||||
self.applications = applications
|
||||
self.updateLocationLabels()
|
||||
self.discoveryTask = nil
|
||||
self.updateResults(preservingSelection: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateLocationLabels() {
|
||||
var nameCounts: [String: Int] = [:]
|
||||
nameCounts.reserveCapacity(applications.count)
|
||||
for application in applications { nameCounts[application.name, default: 0] += 1 }
|
||||
var groups: [String: [(url: URL, folder: URL)]] = [:]
|
||||
for application in applications where (nameCounts[application.name] ?? 0) > 1 {
|
||||
groups[application.name, default: []].append(
|
||||
(application.url, application.url.deletingLastPathComponent())
|
||||
)
|
||||
}
|
||||
locationLabels.removeAll(keepingCapacity: true)
|
||||
for group in groups.values {
|
||||
var folderCounts: [URL: Int] = [:]
|
||||
folderCounts.reserveCapacity(group.count)
|
||||
for entry in group { folderCounts[entry.folder, default: 0] += 1 }
|
||||
for entry in group {
|
||||
let location = (folderCounts[entry.folder] ?? 0) > 1 ? entry.url : entry.folder
|
||||
locationLabels[entry.url] = (location.path as NSString).abbreviatingWithTildeInPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateResults(preservingSelection: Bool = false) {
|
||||
let selectedURL = preservingSelection && results.indices.contains(tableView.selectedRow)
|
||||
? results[tableView.selectedRow].id : nil
|
||||
@@ -299,10 +323,13 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
let identifier = NSUserInterfaceItemIdentifier("applicationCell")
|
||||
let cell = (tableView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView)
|
||||
let cell = (tableView.makeView(withIdentifier: identifier, owner: self) as? ApplicationCell)
|
||||
?? makeCell(identifier: identifier)
|
||||
let application = results[row]
|
||||
cell.textField?.stringValue = application.name
|
||||
let location = locationLabels[application.id]
|
||||
cell.locationLabel.stringValue = location ?? ""
|
||||
cell.locationLabel.isHidden = location == nil
|
||||
cell.imageView?.image = NSWorkspace.shared.icon(forFile: application.url.path)
|
||||
cell.toolTip = application.url.path
|
||||
return cell
|
||||
@@ -316,8 +343,8 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
return view
|
||||
}
|
||||
|
||||
private func makeCell(identifier: NSUserInterfaceItemIdentifier) -> NSTableCellView {
|
||||
let cell = NSTableCellView()
|
||||
private func makeCell(identifier: NSUserInterfaceItemIdentifier) -> ApplicationCell {
|
||||
let cell = ApplicationCell()
|
||||
cell.identifier = identifier
|
||||
let icon = NSImageView()
|
||||
icon.imageScaling = .scaleProportionallyDown
|
||||
@@ -326,6 +353,16 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
label.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
label.textColor = SunsetPalette.text
|
||||
label.lineBreakMode = .byTruncatingTail
|
||||
let location = cell.locationLabel
|
||||
location.font = .systemFont(ofSize: 12)
|
||||
location.textColor = SunsetPalette.secondaryText
|
||||
location.lineBreakMode = .byTruncatingMiddle
|
||||
location.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
location.isHidden = true
|
||||
let title = NSStackView(views: [label, location])
|
||||
title.orientation = .horizontal
|
||||
title.alignment = .centerY
|
||||
title.spacing = 8
|
||||
let kind = NSTextField(labelWithString: "Application")
|
||||
kind.font = .systemFont(ofSize: 14)
|
||||
kind.textColor = SunsetPalette.secondaryText
|
||||
@@ -333,7 +370,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
kind.setAccessibilityElement(false)
|
||||
cell.imageView = icon
|
||||
cell.textField = label
|
||||
for view in [icon, label, kind] {
|
||||
for view in [icon, title, kind] {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
cell.addSubview(view)
|
||||
}
|
||||
@@ -342,9 +379,9 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
icon.centerYAnchor.constraint(equalTo: cell.centerYAnchor),
|
||||
icon.widthAnchor.constraint(equalToConstant: 22),
|
||||
icon.heightAnchor.constraint(equalToConstant: 22),
|
||||
label.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 12),
|
||||
label.centerYAnchor.constraint(equalTo: cell.centerYAnchor),
|
||||
label.trailingAnchor.constraint(lessThanOrEqualTo: kind.leadingAnchor, constant: -12),
|
||||
title.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 12),
|
||||
title.centerYAnchor.constraint(equalTo: cell.centerYAnchor),
|
||||
title.trailingAnchor.constraint(lessThanOrEqualTo: kind.leadingAnchor, constant: -12),
|
||||
kind.centerYAnchor.constraint(equalTo: cell.centerYAnchor),
|
||||
kind.trailingAnchor.constraint(equalTo: cell.trailingAnchor, constant: -8),
|
||||
])
|
||||
@@ -374,6 +411,11 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class ApplicationCell: NSTableCellView {
|
||||
let locationLabel = NSTextField(labelWithString: "")
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class ApplicationTableView: NSTableView {
|
||||
var openSelection: (() -> Void)?
|
||||
|
||||
Reference in New Issue
Block a user