macos: stable sort for surfaces
parent
1fd3f27e26
commit
d23f7e051f
|
|
@ -1,15 +1,27 @@
|
|||
import SwiftUI
|
||||
|
||||
struct CommandOption: Identifiable, Hashable {
|
||||
/// Unique identifier for this option.
|
||||
let id = UUID()
|
||||
/// The primary text displayed for this command.
|
||||
let title: String
|
||||
/// Secondary text displayed below the title.
|
||||
let subtitle: String?
|
||||
/// Tooltip text shown on hover.
|
||||
let description: String?
|
||||
/// Keyboard shortcut symbols to display.
|
||||
let symbols: [String]?
|
||||
/// SF Symbol name for the leading icon.
|
||||
let leadingIcon: String?
|
||||
/// Color for the leading indicator circle.
|
||||
let leadingColor: Color?
|
||||
/// Badge text displayed as a pill.
|
||||
let badge: String?
|
||||
/// Whether to visually emphasize this option.
|
||||
let emphasis: Bool
|
||||
/// Sort key for stable ordering when titles are equal.
|
||||
let sortKey: AnySortKey?
|
||||
/// The action to perform when this option is selected.
|
||||
let action: () -> Void
|
||||
|
||||
init(
|
||||
|
|
@ -21,6 +33,7 @@ struct CommandOption: Identifiable, Hashable {
|
|||
leadingColor: Color? = nil,
|
||||
badge: String? = nil,
|
||||
emphasis: Bool = false,
|
||||
sortKey: AnySortKey? = nil,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.title = title
|
||||
|
|
@ -31,6 +44,7 @@ struct CommandOption: Identifiable, Hashable {
|
|||
self.leadingColor = leadingColor
|
||||
self.badge = badge
|
||||
self.emphasis = emphasis
|
||||
self.sortKey = sortKey
|
||||
self.action = action
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,11 +67,20 @@ struct TerminalCommandPaletteView: View {
|
|||
options.append(contentsOf: updateOptions)
|
||||
|
||||
// Sort the rest. We replace ":" with a character that sorts before space
|
||||
// so that "Foo:" sorts before "Foo Bar:".
|
||||
// so that "Foo:" sorts before "Foo Bar:". Use sortKey as a tie-breaker
|
||||
// for stable ordering when titles are equal.
|
||||
options.append(contentsOf: (jumpOptions + terminalOptions).sorted { a, b in
|
||||
let aNormalized = a.title.replacingOccurrences(of: ":", with: "\t")
|
||||
let bNormalized = b.title.replacingOccurrences(of: ":", with: "\t")
|
||||
return aNormalized.localizedCaseInsensitiveCompare(bNormalized) == .orderedAscending
|
||||
let comparison = aNormalized.localizedCaseInsensitiveCompare(bNormalized)
|
||||
if comparison != .orderedSame {
|
||||
return comparison == .orderedAscending
|
||||
}
|
||||
// Tie-breaker: use sortKey if both have one
|
||||
if let aSortKey = a.sortKey, let bSortKey = b.sortKey {
|
||||
return aSortKey < bSortKey
|
||||
}
|
||||
return false
|
||||
})
|
||||
return options
|
||||
}
|
||||
|
|
@ -153,7 +162,8 @@ struct TerminalCommandPaletteView: View {
|
|||
title: "Focus: \(displayTitle)",
|
||||
subtitle: subtitle,
|
||||
leadingIcon: "rectangle.on.rectangle",
|
||||
leadingColor: displayColor?.displayColor.map { Color($0) }
|
||||
leadingColor: displayColor?.displayColor.map { Color($0) },
|
||||
sortKey: AnySortKey(ObjectIdentifier(surface))
|
||||
) {
|
||||
NotificationCenter.default.post(
|
||||
name: Ghostty.Notification.ghosttyPresentTerminal,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import Foundation
|
||||
|
||||
/// Type-erased wrapper for any Comparable type to use as a sort key.
|
||||
struct AnySortKey: Comparable {
|
||||
private let value: Any
|
||||
private let comparator: (Any, Any) -> ComparisonResult
|
||||
|
||||
init<T: Comparable>(_ value: T) {
|
||||
self.value = value
|
||||
self.comparator = { lhs, rhs in
|
||||
guard let l = lhs as? T, let r = rhs as? T else { return .orderedSame }
|
||||
if l < r { return .orderedAscending }
|
||||
if l > r { return .orderedDescending }
|
||||
return .orderedSame
|
||||
}
|
||||
}
|
||||
|
||||
static func < (lhs: AnySortKey, rhs: AnySortKey) -> Bool {
|
||||
lhs.comparator(lhs.value, rhs.value) == .orderedAscending
|
||||
}
|
||||
|
||||
static func == (lhs: AnySortKey, rhs: AnySortKey) -> Bool {
|
||||
lhs.comparator(lhs.value, rhs.value) == .orderedSame
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue