mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Replace the alert dialog for tab title editing with popover
This commit is contained in:
@ -387,40 +387,45 @@ extension Ghostty {
|
||||
|
||||
/// 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
|
||||
// Create a popover
|
||||
let hostingController = NSHostingController(rootView: TabTitleEditPopover(
|
||||
currentTitle: title,
|
||||
onComplete: { [weak self] newTitle in
|
||||
if newTitle.isEmpty {
|
||||
// Empty means that user wants the title to be set automatically
|
||||
let prevTitle = self?.titleFromTerminal ?? "👻"
|
||||
self?.titleFromTerminal = nil
|
||||
self?.setTitle(prevTitle)
|
||||
} else {
|
||||
// Set the title and prevent it from being changed automatically
|
||||
self?.titleFromTerminal = self?.title
|
||||
self?.title = newTitle
|
||||
}
|
||||
}
|
||||
))
|
||||
|
||||
let popover = NSPopover()
|
||||
popover.contentViewController = hostingController
|
||||
popover.behavior = .transient
|
||||
|
||||
// Show the popover below the current tab title
|
||||
if let window = self.window as? TerminalWindow,
|
||||
let toolbar = window.toolbar as? TerminalToolbar,
|
||||
let titleItem = toolbar.items.first(where: { $0.itemIdentifier == .titleText }),
|
||||
let titleView = titleItem.view {
|
||||
popover.show(
|
||||
relativeTo: titleView.bounds,
|
||||
of: titleView,
|
||||
preferredEdge: .maxY
|
||||
)
|
||||
} else if let window = self.window,
|
||||
let titlebarView = window.contentView?.superview?.firstDescendant(withClassName: "NSTitlebarView"),
|
||||
let titleView = titlebarView.firstDescendant(withClassName: "NSTextField") {
|
||||
popover.show(
|
||||
relativeTo: titleView.bounds,
|
||||
of: titleView,
|
||||
preferredEdge: .maxY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1240,7 +1245,7 @@ extension Ghostty {
|
||||
AppDelegate.logger.warning("action failed action=\(action)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func changeTitle(_ sender: Any) {
|
||||
promptTitle()
|
||||
}
|
||||
@ -1607,3 +1612,47 @@ extension Ghostty.SurfaceView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
struct TabTitleEditPopover: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var newTitle: String
|
||||
let currentTitle: String
|
||||
let onComplete: (String) -> Void
|
||||
|
||||
init(currentTitle: String, onComplete: @escaping (String) -> Void) {
|
||||
self.currentTitle = currentTitle
|
||||
self._newTitle = State(initialValue: currentTitle)
|
||||
self.onComplete = onComplete
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
TextField("Enter title (leave empty for default)", text: $newTitle)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.onSubmit(submit)
|
||||
|
||||
HStack(spacing: 8) {
|
||||
Button(action: { dismiss() }) {
|
||||
Text("Cancel")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.keyboardShortcut(.cancelAction)
|
||||
|
||||
Button(action: { submit() }) {
|
||||
Text("OK")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.keyboardShortcut(.defaultAction)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
private func submit() {
|
||||
onComplete(newTitle)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user