Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Sources/Ghostty/AccessoryKeyboardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,37 @@ final class AccessoryKeyboardView: UIInputView {

// MARK: Keys row

/// The scrollable key bar plus a fixed collapse button at the trailing
/// edge, so "hide keyboard" is always reachable regardless of scroll
/// position.
private func buildKeysRow() -> UIView {
let row = UIStackView()
row.axis = .horizontal
row.spacing = 2
row.alignment = .center
row.translatesAutoresizingMaskIntoConstraints = false
row.isLayoutMarginsRelativeArrangement = true
row.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 6)
row.addArrangedSubview(buildKeysScroll())
row.addArrangedSubview(buildHideButton())
return row
}

private func buildHideButton() -> UIButton {
let button = UIButton(type: .system)
var config = UIButton.Configuration.plain()
config.image = UIImage(systemName: "keyboard.chevron.compact.down")
config.baseForegroundColor = .label
config.contentInsets = NSDirectionalEdgeInsets(top: 6, leading: 8, bottom: 6, trailing: 8)
button.configuration = config
button.accessibilityLabel = "Hide keyboard"
button.addAction(UIAction { [weak self] _ in
self?.target?.collapseKeyboard()
}, for: .touchUpInside)
return button
}

private func buildKeysScroll() -> UIView {
let scroll = UIScrollView()
scroll.translatesAutoresizingMaskIntoConstraints = false
scroll.showsHorizontalScrollIndicator = false
Expand Down
20 changes: 18 additions & 2 deletions Sources/Ghostty/TerminalSurfaceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ final class TerminalSurfaceView: UIView {
@objc private func keyboardWillHide() { keyboardVisible = false }

/// Bring the software keyboard back if the user dismissed it (e.g. with the
/// iPadOS keyboard hide key). Tapping the terminal is the only affordance
/// for getting it back, so the single-tap gesture calls this.
/// iPadOS keyboard hide key, or the accessory bar's collapse button). The
/// single-tap gesture calls this so tapping the terminal expands it again.
func showKeyboardIfNeeded() {
if !isFirstResponder {
_ = becomeFirstResponder()
Expand All @@ -216,6 +216,22 @@ final class TerminalSurfaceView: UIView {
}
}

/// Collapse the on-screen keyboard (and the accessory bar with it), giving
/// the whole screen to the terminal. Tapping the terminal or the status-bar
/// keyboard button expands it again.
func collapseKeyboard() {
_ = resignFirstResponder()
}

/// Toggle the on-screen keyboard: collapse when visible, expand otherwise.
func toggleKeyboard() {
if isFirstResponder && keyboardVisible {
collapseKeyboard()
} else {
showKeyboardIfNeeded()
}
}

// MARK: Font size (pinch-to-zoom)

/// Perform a libghostty keybind action by name (e.g. "increase_font_size:1").
Expand Down
9 changes: 9 additions & 0 deletions Sources/UI/TerminalScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ struct TerminalScreen: View {
}
.background(Color.black.ignoresSafeArea())
.preferredColorScheme(.dark)
.onChange(of: session.state) { _, newState in
// A clean remote close (e.g. `exit` in the shell) dismisses the
// terminal; failures keep the screen up so the error stays readable.
if newState == .closed { onClose() }
}
.alert(
session.hostKeyRequest?.prompt.kind == .changed ? "Host Key Changed" : "Unknown Host",
isPresented: Binding(
Expand Down Expand Up @@ -133,6 +138,10 @@ struct TerminalScreen: View {
.font(.subheadline.weight(.medium))
.lineLimit(1)
Spacer()
Button { session.surface.toggleKeyboard() } label: {
Image(systemName: "keyboard").font(.body.weight(.semibold))
}
.accessibilityLabel("Toggle keyboard")
Button { showingAICommands = true } label: {
Image(systemName: "sparkles").font(.body.weight(.semibold))
}
Expand Down
Loading