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.
|
/// Set the title by prompting the user.
|
||||||
func promptTitle() {
|
func promptTitle() {
|
||||||
// Create an alert dialog
|
// Create a popover
|
||||||
let alert = NSAlert()
|
let hostingController = NSHostingController(rootView: TabTitleEditPopover(
|
||||||
alert.messageText = "Change Terminal Title"
|
currentTitle: title,
|
||||||
alert.informativeText = "Leave blank to restore the default."
|
onComplete: { [weak self] newTitle in
|
||||||
alert.alertStyle = .informational
|
if newTitle.isEmpty {
|
||||||
|
// Empty means that user wants the title to be set automatically
|
||||||
// Add a text field to the alert
|
let prevTitle = self?.titleFromTerminal ?? "👻"
|
||||||
let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24))
|
self?.titleFromTerminal = nil
|
||||||
textField.stringValue = title
|
self?.setTitle(prevTitle)
|
||||||
alert.accessoryView = textField
|
} else {
|
||||||
|
// Set the title and prevent it from being changed automatically
|
||||||
// Add buttons
|
self?.titleFromTerminal = self?.title
|
||||||
alert.addButton(withTitle: "OK")
|
self?.title = newTitle
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
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)")
|
AppDelegate.logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func changeTitle(_ sender: Any) {
|
@IBAction func changeTitle(_ sender: Any) {
|
||||||
promptTitle()
|
promptTitle()
|
||||||
}
|
}
|
||||||
@ -1607,3 +1612,47 @@ extension Ghostty.SurfaceView {
|
|||||||
return false
|
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