mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-20 00:18:53 +03:00
Add tab title rename feature to macos
This commit is contained in:

committed by
Mitchell Hashimoto

parent
228b4dbd60
commit
a581955b9b
@ -586,6 +586,7 @@ typedef enum {
|
||||
GHOSTTY_ACTION_RENDER_INSPECTOR,
|
||||
GHOSTTY_ACTION_DESKTOP_NOTIFICATION,
|
||||
GHOSTTY_ACTION_SET_TITLE,
|
||||
GHOSTTY_ACTION_PROMPT_TITLE,
|
||||
GHOSTTY_ACTION_PWD,
|
||||
GHOSTTY_ACTION_MOUSE_SHAPE,
|
||||
GHOSTTY_ACTION_MOUSE_VISIBILITY,
|
||||
|
@ -52,6 +52,7 @@ class AppDelegate: NSObject,
|
||||
@IBOutlet private var menuIncreaseFontSize: NSMenuItem?
|
||||
@IBOutlet private var menuDecreaseFontSize: NSMenuItem?
|
||||
@IBOutlet private var menuResetFontSize: NSMenuItem?
|
||||
@IBOutlet private var menuChangeTitle: NSMenuItem?
|
||||
@IBOutlet private var menuQuickTerminal: NSMenuItem?
|
||||
@IBOutlet private var menuTerminalInspector: NSMenuItem?
|
||||
|
||||
@ -384,6 +385,7 @@ class AppDelegate: NSObject,
|
||||
syncMenuShortcut(config, action: "increase_font_size:1", menuItem: self.menuIncreaseFontSize)
|
||||
syncMenuShortcut(config, action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize)
|
||||
syncMenuShortcut(config, action: "reset_font_size", menuItem: self.menuResetFontSize)
|
||||
syncMenuShortcut(config, action: "change_title_prompt", menuItem: self.menuChangeTitle)
|
||||
syncMenuShortcut(config, action: "toggle_quick_terminal", menuItem: self.menuQuickTerminal)
|
||||
syncMenuShortcut(config, action: "toggle_visibility", menuItem: self.menuToggleVisibility)
|
||||
syncMenuShortcut(config, action: "inspector:toggle", menuItem: self.menuTerminalInspector)
|
||||
|
@ -14,6 +14,7 @@
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="bbz-4X-AYv" userLabel="AppDelegate" customClass="AppDelegate" customModule="Ghostty" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="menuChangeTitle" destination="24I-xg-qIq" id="kg6-kT-jNL"/>
|
||||
<outlet property="menuCheckForUpdates" destination="GEA-5y-yzH" id="0nV-Tf-nJQ"/>
|
||||
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
|
||||
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
|
||||
@ -232,6 +233,13 @@
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="L3L-I8-sqk"/>
|
||||
<menuItem title="Change Title..." id="24I-xg-qIq">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="changeTitle:" target="-1" id="XuL-QB-Q9l"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Vkj-tP-dMZ"/>
|
||||
<menuItem title="Quick Terminal" id="1pv-LF-NBJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
|
@ -484,6 +484,9 @@ extension Ghostty {
|
||||
case GHOSTTY_ACTION_SET_TITLE:
|
||||
setTitle(app, target: target, v: action.action.set_title)
|
||||
|
||||
case GHOSTTY_ACTION_PROMPT_TITLE:
|
||||
return promptTitle(app, target: target)
|
||||
|
||||
case GHOSTTY_ACTION_PWD:
|
||||
pwdChanged(app, target: target, v: action.action.pwd)
|
||||
|
||||
@ -1007,6 +1010,26 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
private static func promptTitle(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s) -> Bool {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("set title prompt does nothing with an app target")
|
||||
return false
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return false }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||
surfaceView.promptTitle()
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private static func pwdChanged(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
|
@ -124,6 +124,11 @@ extension Ghostty {
|
||||
// A timer to fallback to ghost emoji if no title is set within the grace period
|
||||
private var titleFallbackTimer: Timer?
|
||||
|
||||
// This is the title from the terminal. This is nil if we're currently using
|
||||
// the terminal title as the main title property. If the title is set manually
|
||||
// by the user, this is set to the prior value (which may be empty, but non-nil).
|
||||
private var titleFromTerminal: String?
|
||||
|
||||
/// Event monitor (see individual events for why)
|
||||
private var eventMonitor: Any? = nil
|
||||
|
||||
@ -380,6 +385,45 @@ extension Ghostty {
|
||||
NSCursor.setHiddenUntilMouseMoves(!visible)
|
||||
}
|
||||
|
||||
/// Set the title by prompting the user.
|
||||
func promptTitle() {
|
||||
// Create an alert dialog
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Change Terminal Title"
|
||||
alert.informativeText = "Leave blank to restore the default."
|
||||
alert.alertStyle = .informational
|
||||
|
||||
// Add a text field to the alert
|
||||
let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24))
|
||||
textField.stringValue = title
|
||||
alert.accessoryView = textField
|
||||
|
||||
// Add buttons
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
|
||||
let response = alert.runModal()
|
||||
|
||||
// Check if the user clicked "OK"
|
||||
if response == .alertFirstButtonReturn {
|
||||
// Get the input text
|
||||
let newTitle = textField.stringValue
|
||||
|
||||
if newTitle.isEmpty {
|
||||
// Empty means that user wants the title to be set automatically
|
||||
// We also need to reload the config for the "title" property to be
|
||||
// used again by this tab.
|
||||
let prevTitle = titleFromTerminal ?? "👻"
|
||||
titleFromTerminal = nil
|
||||
setTitle(prevTitle)
|
||||
} else {
|
||||
// Set the title and prevent it from being changed automatically
|
||||
titleFromTerminal = title
|
||||
title = newTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setTitle(_ title: String) {
|
||||
// This fixes an issue where very quick changes to the title could
|
||||
// cause an unpleasant flickering. We set a timer so that we can
|
||||
@ -390,6 +434,11 @@ extension Ghostty {
|
||||
withTimeInterval: 0.075,
|
||||
repeats: false
|
||||
) { [weak self] _ in
|
||||
// Set the title if it wasn't manually set.
|
||||
guard self?.titleFromTerminal == nil else {
|
||||
self?.titleFromTerminal = title
|
||||
return
|
||||
}
|
||||
self?.title = title
|
||||
}
|
||||
}
|
||||
@ -1117,6 +1166,8 @@ extension Ghostty {
|
||||
menu.addItem(.separator())
|
||||
menu.addItem(withTitle: "Reset Terminal", action: #selector(resetTerminal(_:)), keyEquivalent: "")
|
||||
menu.addItem(withTitle: "Toggle Terminal Inspector", action: #selector(toggleTerminalInspector(_:)), keyEquivalent: "")
|
||||
menu.addItem(.separator())
|
||||
menu.addItem(withTitle: "Change Title...", action: #selector(changeTitle(_:)), keyEquivalent: "")
|
||||
|
||||
return menu
|
||||
}
|
||||
@ -1189,6 +1240,10 @@ extension Ghostty {
|
||||
AppDelegate.logger.warning("action failed action=\(action)")
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func changeTitle(_ sender: Any) {
|
||||
promptTitle()
|
||||
}
|
||||
|
||||
/// Show a user notification and associate it with this surface
|
||||
func showUserNotification(title: String, body: String) {
|
||||
|
@ -4024,6 +4024,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
||||
try self.setFontSize(size);
|
||||
},
|
||||
|
||||
.prompt_surface_title => return try self.rt_app.performAction(
|
||||
.{ .surface = self },
|
||||
.prompt_title,
|
||||
{},
|
||||
),
|
||||
|
||||
.clear_screen => {
|
||||
// This is a duplicate of some of the logic in termio.clearScreen
|
||||
// but we need to do this here so we can know the answer before
|
||||
|
@ -158,9 +158,13 @@ pub const Action = union(Key) {
|
||||
/// Show a desktop notification.
|
||||
desktop_notification: DesktopNotification,
|
||||
|
||||
/// Set the title of the target.
|
||||
/// Set the title of the target to the requested value.
|
||||
set_title: SetTitle,
|
||||
|
||||
/// Set the title of the target to a prompted value. It is up to
|
||||
/// the apprt to prompt.
|
||||
prompt_title,
|
||||
|
||||
/// The current working directory has changed for the target terminal.
|
||||
pwd: Pwd,
|
||||
|
||||
@ -254,6 +258,7 @@ pub const Action = union(Key) {
|
||||
render_inspector,
|
||||
desktop_notification,
|
||||
set_title,
|
||||
prompt_title,
|
||||
pwd,
|
||||
mouse_shape,
|
||||
mouse_visibility,
|
||||
|
@ -239,6 +239,7 @@ pub const App = struct {
|
||||
.pwd,
|
||||
.config_change,
|
||||
.toggle_maximize,
|
||||
.prompt_title,
|
||||
=> {
|
||||
log.info("unimplemented action={}", .{action});
|
||||
return false;
|
||||
|
@ -560,6 +560,7 @@ pub fn performAction(
|
||||
.render_inspector,
|
||||
.renderer_health,
|
||||
.color_change,
|
||||
.prompt_title,
|
||||
=> {
|
||||
log.warn("unimplemented action={}", .{action});
|
||||
return false;
|
||||
|
@ -348,6 +348,10 @@ pub const Action = union(enum) {
|
||||
/// This only works with libadwaita enabled currently.
|
||||
toggle_tab_overview: void,
|
||||
|
||||
/// Change the title of the current focused surface via a prompt.
|
||||
/// This only works on macOS currently.
|
||||
prompt_surface_title: void,
|
||||
|
||||
/// Create a new split in the given direction.
|
||||
///
|
||||
/// Arguments:
|
||||
@ -748,6 +752,7 @@ pub const Action = union(enum) {
|
||||
.increase_font_size,
|
||||
.decrease_font_size,
|
||||
.reset_font_size,
|
||||
.prompt_surface_title,
|
||||
.clear_screen,
|
||||
.select_all,
|
||||
.scroll_to_top,
|
||||
|
Reference in New Issue
Block a user