config: add `scrollbar` config to control when scrollbars appear

pull/9232/head
Mitchell Hashimoto 2025-10-16 09:49:03 -07:00
parent 7207ff08d5
commit 4b34b2389a
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
4 changed files with 58 additions and 1 deletions

View File

@ -603,6 +603,17 @@ extension Ghostty {
let str = String(cString: ptr) let str = String(cString: ptr)
return MacShortcuts(rawValue: str) ?? defaultValue return MacShortcuts(rawValue: str) ?? defaultValue
} }
var scrollbar: Scrollbar {
let defaultValue = Scrollbar.system
guard let config = self.config else { return defaultValue }
var v: UnsafePointer<Int8>? = nil
let key = "scrollbar"
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
guard let ptr = v else { return defaultValue }
let str = String(cString: ptr)
return Scrollbar(rawValue: str) ?? defaultValue
}
} }
} }
@ -641,6 +652,11 @@ extension Ghostty.Config {
case ask case ask
} }
enum Scrollbar: String {
case system
case never
}
enum ResizeOverlay : String { enum ResizeOverlay : String {
case always case always
case never case never

View File

@ -1,4 +1,5 @@
import SwiftUI import SwiftUI
import Combine
/// Wraps a Ghostty surface view in an NSScrollView to provide native macOS scrollbar support. /// Wraps a Ghostty surface view in an NSScrollView to provide native macOS scrollbar support.
/// ///
@ -16,6 +17,7 @@ class SurfaceScrollView: NSView {
private let documentView: NSView private let documentView: NSView
private let surfaceView: Ghostty.SurfaceView private let surfaceView: Ghostty.SurfaceView
private var observers: [NSObjectProtocol] = [] private var observers: [NSObjectProtocol] = []
private var cancellables: Set<AnyCancellable> = []
private var isLiveScrolling = false private var isLiveScrolling = false
/// The last row position sent via scroll_to_row action. Used to avoid /// The last row position sent via scroll_to_row action. Used to avoid
@ -28,7 +30,7 @@ class SurfaceScrollView: NSView {
// The scroll view is our outermost view that controls all our scrollbar // The scroll view is our outermost view that controls all our scrollbar
// rendering and behavior. // rendering and behavior.
scrollView = NSScrollView() scrollView = NSScrollView()
scrollView.hasVerticalScroller = true scrollView.hasVerticalScroller = false
scrollView.hasHorizontalScroller = false scrollView.hasHorizontalScroller = false
scrollView.autohidesScrollers = true scrollView.autohidesScrollers = true
scrollView.usesPredominantAxisScrolling = true scrollView.usesPredominantAxisScrolling = true
@ -49,6 +51,9 @@ class SurfaceScrollView: NSView {
// Our scroll view is our only view // Our scroll view is our only view
addSubview(scrollView) addSubview(scrollView)
// Apply initial scrollbar settings
synchronizeAppearance()
// We listen for scroll events through bounds notifications on our NSClipView. // We listen for scroll events through bounds notifications on our NSClipView.
// This is based on: https://christiantietze.de/posts/2018/07/synchronize-nsscrollview/ // This is based on: https://christiantietze.de/posts/2018/07/synchronize-nsscrollview/
scrollView.contentView.postsBoundsChangedNotifications = true scrollView.contentView.postsBoundsChangedNotifications = true
@ -93,6 +98,15 @@ class SurfaceScrollView: NSView {
) { [weak self] _ in ) { [weak self] _ in
self?.handleLiveScroll() self?.handleLiveScroll()
}) })
// Listen for derived config changes to update scrollbar settings live
surfaceView.$derivedConfig
.sink { [weak self] _ in
DispatchQueue.main.async { [weak self] in
self?.synchronizeAppearance()
}
}
.store(in: &cancellables)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {

View File

@ -1532,6 +1532,7 @@ extension Ghostty {
let macosWindowShadow: Bool let macosWindowShadow: Bool
let windowTitleFontFamily: String? let windowTitleFontFamily: String?
let windowAppearance: NSAppearance? let windowAppearance: NSAppearance?
let scrollbar: Ghostty.Config.Scrollbar
init() { init() {
self.backgroundColor = Color(NSColor.windowBackgroundColor) self.backgroundColor = Color(NSColor.windowBackgroundColor)
@ -1539,6 +1540,7 @@ extension Ghostty {
self.macosWindowShadow = true self.macosWindowShadow = true
self.windowTitleFontFamily = nil self.windowTitleFontFamily = nil
self.windowAppearance = nil self.windowAppearance = nil
self.scrollbar = .system
} }
init(_ config: Ghostty.Config) { init(_ config: Ghostty.Config) {
@ -1547,6 +1549,7 @@ extension Ghostty {
self.macosWindowShadow = config.macosWindowShadow self.macosWindowShadow = config.macosWindowShadow
self.windowTitleFontFamily = config.windowTitleFontFamily self.windowTitleFontFamily = config.windowTitleFontFamily
self.windowAppearance = .init(ghosttyConfig: config) self.windowAppearance = .init(ghosttyConfig: config)
self.scrollbar = config.scrollbar
} }
} }

View File

@ -1197,6 +1197,24 @@ input: RepeatableReadableIO = .{},
/// This can be changed at runtime but will only affect new terminal surfaces. /// This can be changed at runtime but will only affect new terminal surfaces.
@"scrollback-limit": usize = 10_000_000, // 10MB @"scrollback-limit": usize = 10_000_000, // 10MB
/// Control when the scrollbar is shown to scroll the scrollback buffer.
///
/// The default value is `system`.
///
/// Valid values:
///
/// * `system` - Respect the system settings for when to show scrollbars.
/// For example, on macOS, this will respect the "Scrollbar behavior"
/// system setting which by default usually only shows scrollbars while
/// actively scrolling or hovering the gutter.
///
/// * `never` - Never show a scrollbar. You can still scroll using the mouse,
/// keybind actions, etc. but you will not have a visual UI widget showing
/// a scrollbar.
///
/// This only applies to macOS currently. GTK doesn't yet support scrollbars.
scrollbar: Scrollbar = .system,
/// Match a regular expression against the terminal text and associate clicking /// Match a regular expression against the terminal text and associate clicking
/// it with an action. This can be used to match URLs, file paths, etc. Actions /// it with an action. This can be used to match URLs, file paths, etc. Actions
/// can be opening using the system opener (e.g. `open` or `xdg-open`) or /// can be opening using the system opener (e.g. `open` or `xdg-open`) or
@ -8379,6 +8397,12 @@ pub const WindowPadding = struct {
} }
}; };
/// See scrollbar
pub const Scrollbar = enum {
system,
never,
};
/// See scroll-to-bottom /// See scroll-to-bottom
pub const ScrollToBottom = packed struct { pub const ScrollToBottom = packed struct {
keystroke: bool = true, keystroke: bool = true,