parent
5fa1a991d0
commit
e0516bba83
|
|
@ -760,6 +760,7 @@ typedef enum {
|
|||
GHOSTTY_ACTION_COLOR_KIND_FOREGROUND = -1,
|
||||
GHOSTTY_ACTION_COLOR_KIND_BACKGROUND = -2,
|
||||
GHOSTTY_ACTION_COLOR_KIND_CURSOR = -3,
|
||||
GHOSTTY_ACTION_COLOR_KIND_TAB = -4,
|
||||
} ghostty_action_color_kind_e;
|
||||
|
||||
// apprt.action.ColorChange
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@ struct TerminalCommandPaletteView: View {
|
|||
guard let window = controller.window else { return [] }
|
||||
|
||||
let color = (window as? TerminalWindow)?.tabColor
|
||||
let displayColor = color != TerminalTabColor.none ? color : nil
|
||||
|
||||
return controller.surfaceTree.map { surface in
|
||||
let terminalTitle = surface.title.isEmpty ? window.title : surface.title
|
||||
|
|
@ -163,7 +162,7 @@ struct TerminalCommandPaletteView: View {
|
|||
title: "Focus: \(displayTitle)",
|
||||
subtitle: subtitle,
|
||||
leadingIcon: "rectangle.on.rectangle",
|
||||
leadingColor: displayColor?.displayColor.map { Color($0) },
|
||||
leadingColor: color?.color.map { Color($0) },
|
||||
sortKey: AnySortKey(ObjectIdentifier(surface))
|
||||
) {
|
||||
NotificationCenter.default.post(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
enum TerminalTabColor: Int, CaseIterable, Codable {
|
||||
enum TerminalTabColorPreset: Int, CaseIterable, Codable {
|
||||
case none
|
||||
case blue
|
||||
case purple
|
||||
|
|
@ -67,6 +67,8 @@ enum TerminalTabColor: Int, CaseIterable, Codable {
|
|||
}
|
||||
}
|
||||
|
||||
var tabColor: TerminalTabColor { TerminalTabColor(color: displayColor) }
|
||||
|
||||
func swatchImage(selected: Bool) -> NSImage {
|
||||
let size = NSSize(width: 18, height: 18)
|
||||
return NSImage(size: size, flipped: false) { rect in
|
||||
|
|
@ -105,15 +107,57 @@ enum TerminalTabColor: Int, CaseIterable, Codable {
|
|||
}
|
||||
}
|
||||
|
||||
struct TerminalTabColor: Equatable, Codable {
|
||||
let color: NSColor?
|
||||
|
||||
static let none = TerminalTabColor(color: nil)
|
||||
|
||||
init(color: NSColor?) {
|
||||
self.color = color
|
||||
}
|
||||
|
||||
init(color: Color) {
|
||||
self.color = NSColor(color)
|
||||
}
|
||||
|
||||
// MARK: Codable
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
// Backward compatibility: attempt to decode the previously stored preset index.
|
||||
if let preset = try? container.decode(TerminalTabColorPreset.self) {
|
||||
self.color = preset.displayColor
|
||||
return
|
||||
}
|
||||
let hex = try container.decode(String.self)
|
||||
self.color = NSColor(hex: hex)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(color?.hexString ?? "")
|
||||
}
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
static func == (lhs: TerminalTabColor, rhs: TerminalTabColor) -> Bool {
|
||||
lhs.color == rhs.color
|
||||
}
|
||||
|
||||
var matchingPreset: TerminalTabColorPreset {
|
||||
TerminalTabColorPreset.allCases.first { $0.displayColor == color } ?? .none
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Menu View
|
||||
|
||||
/// A SwiftUI view displaying a color palette for tab color selection.
|
||||
/// Used as a custom view inside an NSMenuItem in the tab context menu.
|
||||
struct TabColorMenuView: View {
|
||||
@State private var currentSelection: TerminalTabColor
|
||||
let onSelect: (TerminalTabColor) -> Void
|
||||
@State private var currentSelection: TerminalTabColorPreset
|
||||
let onSelect: (TerminalTabColorPreset) -> Void
|
||||
|
||||
init(selectedColor: TerminalTabColor, onSelect: @escaping (TerminalTabColor) -> Void) {
|
||||
init(selectedColor: TerminalTabColorPreset, onSelect: @escaping (TerminalTabColorPreset) -> Void) {
|
||||
self._currentSelection = State(initialValue: selectedColor)
|
||||
self.onSelect = onSelect
|
||||
}
|
||||
|
|
@ -143,7 +187,7 @@ struct TabColorMenuView: View {
|
|||
.padding(.bottom, 4)
|
||||
}
|
||||
|
||||
static let paletteRows: [[TerminalTabColor]] = [
|
||||
static let paletteRows: [[TerminalTabColorPreset]] = [
|
||||
[.none, .blue, .purple, .pink, .red],
|
||||
[.orange, .yellow, .green, .teal, .graphite],
|
||||
]
|
||||
|
|
@ -161,7 +205,7 @@ struct TabColorMenuView: View {
|
|||
|
||||
/// A single color swatch button in the tab color palette.
|
||||
private struct TabColorSwatch: View {
|
||||
let color: TerminalTabColor
|
||||
let color: TerminalTabColorPreset
|
||||
let isSelected: Bool
|
||||
let action: () -> Void
|
||||
|
||||
|
|
|
|||
|
|
@ -682,7 +682,7 @@ private struct TabColorIndicatorView: View {
|
|||
let tabColor: TerminalTabColor
|
||||
|
||||
var body: some View {
|
||||
if let color = tabColor.displayColor {
|
||||
if let color = tabColor.color {
|
||||
Circle()
|
||||
.fill(Color(color))
|
||||
.frame(width: 6, height: 6)
|
||||
|
|
@ -773,17 +773,17 @@ extension TerminalWindow {
|
|||
let paletteItem = NSMenuItem()
|
||||
paletteItem.identifier = Self.tabColorPaletteIdentifier
|
||||
paletteItem.view = makeTabColorPaletteView(
|
||||
selectedColor: (target?.window as? TerminalWindow)?.tabColor ?? .none
|
||||
selectedColor: (target?.window as? TerminalWindow)?.tabColor.matchingPreset ?? .none
|
||||
) { [weak target] color in
|
||||
(target?.window as? TerminalWindow)?.tabColor = color
|
||||
(target?.window as? TerminalWindow)?.tabColor = color.tabColor
|
||||
}
|
||||
menu.addItem(paletteItem)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeTabColorPaletteView(
|
||||
selectedColor: TerminalTabColor,
|
||||
selectionHandler: @escaping (TerminalTabColor) -> Void
|
||||
selectedColor: TerminalTabColorPreset,
|
||||
selectionHandler: @escaping (TerminalTabColorPreset) -> Void
|
||||
) -> NSView {
|
||||
let hostingView = NSHostingView(rootView: TabColorMenuView(
|
||||
selectedColor: selectedColor,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ extension Ghostty.Action {
|
|||
case foreground
|
||||
case background
|
||||
case cursor
|
||||
case tab
|
||||
case palette(index: UInt8)
|
||||
}
|
||||
|
||||
|
|
@ -25,6 +26,8 @@ extension Ghostty.Action {
|
|||
self.kind = .background
|
||||
case GHOSTTY_ACTION_COLOR_KIND_CURSOR:
|
||||
self.kind = .cursor
|
||||
case GHOSTTY_ACTION_COLOR_KIND_TAB:
|
||||
self.kind = .tab
|
||||
default:
|
||||
self.kind = .palette(index: UInt8(c.kind.rawValue))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -764,6 +764,11 @@ extension Ghostty {
|
|||
self?.backgroundColor = change.color
|
||||
}
|
||||
|
||||
case .tab:
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
(self?.window as? TerminalWindow)?.tabColor = TerminalTabColor(color: change.color)
|
||||
}
|
||||
|
||||
default:
|
||||
// We don't do anything for the other colors yet.
|
||||
break
|
||||
|
|
|
|||
|
|
@ -5915,6 +5915,16 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||
.io => self.queueIo(.{ .crash = {} }, .unlocked),
|
||||
},
|
||||
|
||||
.colorize_tab => |hex| {
|
||||
const color = try terminal.color.RGB.parse(hex);
|
||||
return try self.rt_app.performAction(.{ .surface = self }, .color_change, .{
|
||||
.kind = .tab,
|
||||
.r = color.r,
|
||||
.g = color.g,
|
||||
.b = color.b,
|
||||
});
|
||||
},
|
||||
|
||||
.adjust_selection => |direction| {
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
|
|
|
|||
|
|
@ -845,6 +845,7 @@ pub const ColorKind = enum(c_int) {
|
|||
foreground = -1,
|
||||
background = -2,
|
||||
cursor = -3,
|
||||
tab = -4,
|
||||
|
||||
// 0+ values indicate a palette index
|
||||
_,
|
||||
|
|
|
|||
|
|
@ -934,6 +934,29 @@ pub const Action = union(enum) {
|
|||
///
|
||||
crash: CrashThread,
|
||||
|
||||
/// Mark the current tab with the color defined.
|
||||
/// This is equivalent to right-click a tab and selecting one of the predefine color.
|
||||
///
|
||||
/// For example, `colorize_tab:#632CA6`
|
||||
///
|
||||
/// Any of the following forms are accepted:
|
||||
///
|
||||
/// - rgb:<red>/<green>/<blue>
|
||||
/// <red>, <green>, <blue> := h | hh | hhh | hhhh
|
||||
/// where `h` is a single hexadecimal digit.
|
||||
///
|
||||
/// - rgbi:<red>/<green>/<blue>
|
||||
/// where <red>, <green>, and <blue> are floating point values between
|
||||
/// 0.0 and 1.0 (inclusive).
|
||||
///
|
||||
/// - #rgb, #rrggbb, #rrrgggbbb #rrrrggggbbbb
|
||||
/// where `r`, `g`, and `b` are a single hexadecimal digit.
|
||||
/// These specify a color with 4, 8, 12, and 16 bits of precision
|
||||
/// per color channel.
|
||||
///
|
||||
/// Only implemented on macOS.
|
||||
colorize_tab: []const u8,
|
||||
|
||||
pub const Key = @typeInfo(Action).@"union".tag_type.?;
|
||||
|
||||
/// Make this a valid gobject if we're in a GTK environment.
|
||||
|
|
@ -1391,6 +1414,7 @@ pub const Action = union(enum) {
|
|||
.resize_split,
|
||||
.equalize_splits,
|
||||
.inspector,
|
||||
.colorize_tab,
|
||||
=> .surface,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -706,6 +706,7 @@ fn actionCommands(action: Action.Key) []const Command {
|
|||
.deactivate_all_key_tables,
|
||||
.end_key_sequence,
|
||||
.crash,
|
||||
.colorize_tab,
|
||||
=> comptime &.{},
|
||||
|
||||
// No commands because I'm not sure they make sense in a command
|
||||
|
|
|
|||
Loading…
Reference in New Issue