macos: move context menu stuff in terminal window down to an ext
parent
f559bccc38
commit
1073e89a0d
|
|
@ -234,11 +234,6 @@ class TerminalWindow: NSWindow {
|
||||||
/// added.
|
/// added.
|
||||||
static let tabBarIdentifier: NSUserInterfaceItemIdentifier = .init("_ghosttyTabBar")
|
static let tabBarIdentifier: NSUserInterfaceItemIdentifier = .init("_ghosttyTabBar")
|
||||||
|
|
||||||
private static let closeTabsOnRightMenuItemIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.closeTabsOnTheRightMenuItem")
|
|
||||||
private static let tabColorSeparatorIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.tabColorSeparator")
|
|
||||||
private static let tabColorHeaderIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.tabColorHeader")
|
|
||||||
private static let tabColorPaletteIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.tabColorPalette")
|
|
||||||
|
|
||||||
func findTitlebarView() -> NSView? {
|
func findTitlebarView() -> NSView? {
|
||||||
// Find our tab bar. If it doesn't exist we don't do anything.
|
// Find our tab bar. If it doesn't exist we don't do anything.
|
||||||
//
|
//
|
||||||
|
|
@ -314,81 +309,6 @@ class TerminalWindow: NSWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func configureTabContextMenuIfNeeded(_ menu: NSMenu) {
|
|
||||||
guard isTabContextMenu(menu) else { return }
|
|
||||||
|
|
||||||
// Get the target from an existing menu item. The native tab context menu items
|
|
||||||
// target the specific window/controller that was right-clicked, not the focused one.
|
|
||||||
// We need to use that same target so validation and action use the correct tab.
|
|
||||||
let targetController = menu.items
|
|
||||||
.first { $0.action == NSSelectorFromString("performClose:") }
|
|
||||||
.flatMap { $0.target as? NSWindow }
|
|
||||||
.flatMap { $0.windowController as? TerminalController }
|
|
||||||
|
|
||||||
// Close tabs to the right
|
|
||||||
let item = NSMenuItem(title: "Close Tabs to the Right", action: #selector(TerminalController.closeTabsOnTheRight(_:)), keyEquivalent: "")
|
|
||||||
item.identifier = Self.closeTabsOnRightMenuItemIdentifier
|
|
||||||
item.target = targetController
|
|
||||||
item.setImageIfDesired(systemSymbolName: "xmark")
|
|
||||||
if menu.insertItem(item, after: NSSelectorFromString("performCloseOtherTabs:")) == nil,
|
|
||||||
menu.insertItem(item, after: NSSelectorFromString("performClose:")) == nil {
|
|
||||||
menu.addItem(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other close items should have the xmark to match Safari on macOS 26
|
|
||||||
for menuItem in menu.items {
|
|
||||||
if menuItem.action == NSSelectorFromString("performClose:") ||
|
|
||||||
menuItem.action == NSSelectorFromString("performCloseOtherTabs:") {
|
|
||||||
menuItem.setImageIfDesired(systemSymbolName: "xmark")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appendTabColorSection(to: menu, target: targetController)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func isTabContextMenu(_ menu: NSMenu) -> Bool {
|
|
||||||
guard NSApp.keyWindow === self else { return false }
|
|
||||||
|
|
||||||
// These are the target selectors, at least for macOS 26.
|
|
||||||
let tabContextSelectors: Set<String> = [
|
|
||||||
"performClose:",
|
|
||||||
"performCloseOtherTabs:",
|
|
||||||
"moveTabToNewWindow:",
|
|
||||||
"toggleTabOverview:"
|
|
||||||
]
|
|
||||||
|
|
||||||
let selectorNames = Set(menu.items.compactMap { $0.action }.map { NSStringFromSelector($0) })
|
|
||||||
return !selectorNames.isDisjoint(with: tabContextSelectors)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func appendTabColorSection(to menu: NSMenu, target: TerminalController?) {
|
|
||||||
menu.removeItems(withIdentifiers: [
|
|
||||||
Self.tabColorSeparatorIdentifier,
|
|
||||||
Self.tabColorHeaderIdentifier,
|
|
||||||
Self.tabColorPaletteIdentifier
|
|
||||||
])
|
|
||||||
|
|
||||||
let separator = NSMenuItem.separator()
|
|
||||||
separator.identifier = Self.tabColorSeparatorIdentifier
|
|
||||||
menu.addItem(separator)
|
|
||||||
|
|
||||||
let headerItem = NSMenuItem()
|
|
||||||
headerItem.identifier = Self.tabColorHeaderIdentifier
|
|
||||||
headerItem.title = "Tab Color"
|
|
||||||
headerItem.isEnabled = false
|
|
||||||
headerItem.setImageIfDesired(systemSymbolName: "eyedropper")
|
|
||||||
menu.addItem(headerItem)
|
|
||||||
|
|
||||||
let paletteItem = NSMenuItem()
|
|
||||||
paletteItem.identifier = Self.tabColorPaletteIdentifier
|
|
||||||
paletteItem.view = makeTabColorPaletteView(
|
|
||||||
selectedColor: tabColorSelection
|
|
||||||
) { [weak target] color in
|
|
||||||
target?.setTabColor(color)
|
|
||||||
}
|
|
||||||
menu.addItem(paletteItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Tab Key Equivalents
|
// MARK: Tab Key Equivalents
|
||||||
|
|
||||||
var keyEquivalent: String? = nil {
|
var keyEquivalent: String? = nil {
|
||||||
|
|
@ -758,6 +678,90 @@ private final class TabColorIndicator: NSView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Tab Context Menu
|
||||||
|
|
||||||
|
extension TerminalWindow {
|
||||||
|
private static let closeTabsOnRightMenuItemIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.closeTabsOnTheRightMenuItem")
|
||||||
|
private static let tabColorSeparatorIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.tabColorSeparator")
|
||||||
|
private static let tabColorHeaderIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.tabColorHeader")
|
||||||
|
private static let tabColorPaletteIdentifier = NSUserInterfaceItemIdentifier("com.mitchellh.ghostty.tabColorPalette")
|
||||||
|
|
||||||
|
func configureTabContextMenuIfNeeded(_ menu: NSMenu) {
|
||||||
|
guard isTabContextMenu(menu) else { return }
|
||||||
|
|
||||||
|
// Get the target from an existing menu item. The native tab context menu items
|
||||||
|
// target the specific window/controller that was right-clicked, not the focused one.
|
||||||
|
// We need to use that same target so validation and action use the correct tab.
|
||||||
|
let targetController = menu.items
|
||||||
|
.first { $0.action == NSSelectorFromString("performClose:") }
|
||||||
|
.flatMap { $0.target as? NSWindow }
|
||||||
|
.flatMap { $0.windowController as? TerminalController }
|
||||||
|
|
||||||
|
// Close tabs to the right
|
||||||
|
let item = NSMenuItem(title: "Close Tabs to the Right", action: #selector(TerminalController.closeTabsOnTheRight(_:)), keyEquivalent: "")
|
||||||
|
item.identifier = Self.closeTabsOnRightMenuItemIdentifier
|
||||||
|
item.target = targetController
|
||||||
|
item.setImageIfDesired(systemSymbolName: "xmark")
|
||||||
|
if menu.insertItem(item, after: NSSelectorFromString("performCloseOtherTabs:")) == nil,
|
||||||
|
menu.insertItem(item, after: NSSelectorFromString("performClose:")) == nil {
|
||||||
|
menu.addItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other close items should have the xmark to match Safari on macOS 26
|
||||||
|
for menuItem in menu.items {
|
||||||
|
if menuItem.action == NSSelectorFromString("performClose:") ||
|
||||||
|
menuItem.action == NSSelectorFromString("performCloseOtherTabs:") {
|
||||||
|
menuItem.setImageIfDesired(systemSymbolName: "xmark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTabColorSection(to: menu, target: targetController)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func isTabContextMenu(_ menu: NSMenu) -> Bool {
|
||||||
|
guard NSApp.keyWindow === self else { return false }
|
||||||
|
|
||||||
|
// These are the target selectors, at least for macOS 26.
|
||||||
|
let tabContextSelectors: Set<String> = [
|
||||||
|
"performClose:",
|
||||||
|
"performCloseOtherTabs:",
|
||||||
|
"moveTabToNewWindow:",
|
||||||
|
"toggleTabOverview:"
|
||||||
|
]
|
||||||
|
|
||||||
|
let selectorNames = Set(menu.items.compactMap { $0.action }.map { NSStringFromSelector($0) })
|
||||||
|
return !selectorNames.isDisjoint(with: tabContextSelectors)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func appendTabColorSection(to menu: NSMenu, target: TerminalController?) {
|
||||||
|
menu.removeItems(withIdentifiers: [
|
||||||
|
Self.tabColorSeparatorIdentifier,
|
||||||
|
Self.tabColorHeaderIdentifier,
|
||||||
|
Self.tabColorPaletteIdentifier
|
||||||
|
])
|
||||||
|
|
||||||
|
let separator = NSMenuItem.separator()
|
||||||
|
separator.identifier = Self.tabColorSeparatorIdentifier
|
||||||
|
menu.addItem(separator)
|
||||||
|
|
||||||
|
let headerItem = NSMenuItem()
|
||||||
|
headerItem.identifier = Self.tabColorHeaderIdentifier
|
||||||
|
headerItem.title = "Tab Color"
|
||||||
|
headerItem.isEnabled = false
|
||||||
|
headerItem.setImageIfDesired(systemSymbolName: "eyedropper")
|
||||||
|
menu.addItem(headerItem)
|
||||||
|
|
||||||
|
let paletteItem = NSMenuItem()
|
||||||
|
paletteItem.identifier = Self.tabColorPaletteIdentifier
|
||||||
|
paletteItem.view = makeTabColorPaletteView(
|
||||||
|
selectedColor: tabColorSelection
|
||||||
|
) { [weak target] color in
|
||||||
|
target?.setTabColor(color)
|
||||||
|
}
|
||||||
|
menu.addItem(paletteItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func makeTabColorPaletteView(
|
private func makeTabColorPaletteView(
|
||||||
selectedColor: TerminalTabColor,
|
selectedColor: TerminalTabColor,
|
||||||
selectionHandler: @escaping (TerminalTabColor) -> Void
|
selectionHandler: @escaping (TerminalTabColor) -> Void
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue