mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-05-05 15:33:00 +00:00
feat: beautify command palette
This commit is contained in:
parent
4e91d11a60
commit
3827ce9e4c
@ -4,7 +4,7 @@ struct CommandOption: Identifiable, Hashable {
|
||||
let id = UUID()
|
||||
let title: String
|
||||
let description: String?
|
||||
let shortcut: String?
|
||||
let symbols: [String]?
|
||||
let action: () -> Void
|
||||
|
||||
static func == (lhs: CommandOption, rhs: CommandOption) -> Bool {
|
||||
@ -18,7 +18,6 @@ struct CommandOption: Identifiable, Hashable {
|
||||
|
||||
struct CommandPaletteView: View {
|
||||
@Binding var isPresented: Bool
|
||||
var backgroundColor: Color = Color(nsColor: .windowBackgroundColor)
|
||||
var options: [CommandOption]
|
||||
@State private var query = ""
|
||||
@State private var selectedIndex: UInt?
|
||||
@ -89,7 +88,6 @@ struct CommandPaletteView: View {
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding(.bottom, 4)
|
||||
|
||||
CommandTable(
|
||||
options: filteredOptions,
|
||||
@ -100,15 +98,13 @@ struct CommandPaletteView: View {
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: 500)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(backgroundColor)
|
||||
.shadow(color: .black.opacity(0.4), radius: 10, x: 0, y: 10)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(Color.black.opacity(0.1), lineWidth: 1)
|
||||
)
|
||||
.background(BackgroundVisualEffectView())
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.stroke(Color(nsColor: .tertiaryLabelColor).opacity(0.75))
|
||||
)
|
||||
.shadow(radius: 32, x: 0, y: 12)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
@ -147,8 +143,9 @@ fileprivate struct CommandPaletteQuery: View {
|
||||
|
||||
TextField("Execute a command…", text: $query)
|
||||
.padding()
|
||||
.font(.system(size: 14))
|
||||
.textFieldStyle(PlainTextFieldStyle())
|
||||
.font(.system(size: 20, weight: .light))
|
||||
.frame(height: 48)
|
||||
.textFieldStyle(.plain)
|
||||
.focused($isTextFieldFocused)
|
||||
.onAppear {
|
||||
isTextFieldFocused = true
|
||||
@ -178,7 +175,7 @@ fileprivate struct CommandTable: View {
|
||||
.padding()
|
||||
} else {
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView(showsIndicators: false) {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(options.enumerated()), id: \.1.id) { index, option in
|
||||
CommandRow(
|
||||
@ -198,6 +195,7 @@ fileprivate struct CommandTable: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
}
|
||||
.frame(maxHeight: 200)
|
||||
.onChange(of: selectedIndex) { _ in
|
||||
@ -223,20 +221,12 @@ fileprivate struct CommandRow: View {
|
||||
HStack {
|
||||
Text(option.title)
|
||||
Spacer()
|
||||
if let shortcut = option.shortcut {
|
||||
Text(shortcut)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
.kerning(1.5)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(Color.gray.opacity(0.2))
|
||||
)
|
||||
if let symbols = option.symbols {
|
||||
ShortcutSymbolsView(symbols: symbols)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 8)
|
||||
.padding(8)
|
||||
.background(
|
||||
isSelected
|
||||
? Color.accentColor.opacity(0.2)
|
||||
@ -244,14 +234,43 @@ fileprivate struct CommandRow: View {
|
||||
? Color.secondary.opacity(0.2)
|
||||
: Color.clear)
|
||||
)
|
||||
.cornerRadius(6)
|
||||
.cornerRadius(5)
|
||||
}
|
||||
.help(option.description ?? "")
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.buttonStyle(.plain)
|
||||
.onHover { hovering in
|
||||
hoveredID = hovering ? option.id : nil
|
||||
}
|
||||
.padding(.horizontal, 4)
|
||||
.padding(.vertical, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// A view that creates a semi-transparent blurry background.
|
||||
fileprivate struct BackgroundVisualEffectView: NSViewRepresentable {
|
||||
func makeNSView(context: Context) -> NSVisualEffectView {
|
||||
let view = NSVisualEffectView()
|
||||
|
||||
view.blendingMode = .withinWindow
|
||||
view.state = .active
|
||||
view.material = .sidebar
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
/// A row of Text representing a shortcut.
|
||||
fileprivate struct ShortcutSymbolsView: View {
|
||||
let symbols: [String]
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 1) {
|
||||
ForEach(symbols, id: \.self) { symbol in
|
||||
Text(symbol)
|
||||
.frame(minWidth: 13)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ struct TerminalCommandPaletteView: View {
|
||||
return CommandOption(
|
||||
title: String(cString: c.title),
|
||||
description: String(cString: c.description),
|
||||
shortcut: ghosttyConfig.keyboardShortcut(for: action)?.description
|
||||
symbols: ghosttyConfig.keyboardShortcut(for: action)?.keyList
|
||||
) {
|
||||
onAction(action)
|
||||
}
|
||||
@ -59,7 +59,6 @@ struct TerminalCommandPaletteView: View {
|
||||
|
||||
CommandPaletteView(
|
||||
isPresented: $isPresented,
|
||||
backgroundColor: ghosttyConfig.backgroundColor,
|
||||
options: commandOptions
|
||||
)
|
||||
.transition(
|
||||
|
@ -1,12 +1,9 @@
|
||||
import SwiftUI
|
||||
|
||||
extension KeyboardShortcut: @retroactive CustomStringConvertible {
|
||||
public var description: String {
|
||||
var result = ""
|
||||
public var keyList: [String] {
|
||||
var result: [String] = []
|
||||
|
||||
if modifiers.contains(.command) {
|
||||
result.append("⌘")
|
||||
}
|
||||
if modifiers.contains(.control) {
|
||||
result.append("⌃")
|
||||
}
|
||||
@ -16,6 +13,9 @@ extension KeyboardShortcut: @retroactive CustomStringConvertible {
|
||||
if modifiers.contains(.shift) {
|
||||
result.append("⇧")
|
||||
}
|
||||
if modifiers.contains(.command) {
|
||||
result.append("⌘")
|
||||
}
|
||||
|
||||
let keyString: String
|
||||
switch key {
|
||||
@ -24,14 +24,14 @@ extension KeyboardShortcut: @retroactive CustomStringConvertible {
|
||||
case .delete: keyString = "⌫"
|
||||
case .space: keyString = "␣"
|
||||
case .tab: keyString = "⇥"
|
||||
case .upArrow: keyString = "↑"
|
||||
case .downArrow: keyString = "↓"
|
||||
case .leftArrow: keyString = "←"
|
||||
case .rightArrow: keyString = "→"
|
||||
case .pageUp: keyString = "PgUp"
|
||||
case .pageDown: keyString = "PgDown"
|
||||
case .end: keyString = "End"
|
||||
case .home: keyString = "Home"
|
||||
case .upArrow: keyString = "▲"
|
||||
case .downArrow: keyString = "▼"
|
||||
case .leftArrow: keyString = "◀"
|
||||
case .rightArrow: keyString = "▶"
|
||||
case .pageUp: keyString = "↑"
|
||||
case .pageDown: keyString = "↓"
|
||||
case .home: keyString = "⤒"
|
||||
case .end: keyString = "⤓"
|
||||
default:
|
||||
keyString = String(key.character.uppercased())
|
||||
}
|
||||
@ -39,6 +39,10 @@ extension KeyboardShortcut: @retroactive CustomStringConvertible {
|
||||
result.append(keyString)
|
||||
return result
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self.keyList.joined()
|
||||
}
|
||||
}
|
||||
|
||||
// This is available in macOS 14 so this only applies to early macOS versions.
|
||||
|
Loading…
x
Reference in New Issue
Block a user