Add Ghostty-style scrollbars and focus handling
This commit is contained in:
parent
fd78696474
commit
5d11e612e4
2 changed files with 392 additions and 80 deletions
|
|
@ -47,8 +47,7 @@ class GhosttyApp {
|
|||
}
|
||||
}
|
||||
runtimeConfig.action_cb = { app, target, action in
|
||||
// Handle actions
|
||||
return false
|
||||
return GhosttyApp.shared.handleAction(target: target, action: action)
|
||||
}
|
||||
runtimeConfig.read_clipboard_cb = { userdata, location, state in
|
||||
// Read clipboard
|
||||
|
|
@ -80,6 +79,38 @@ class GhosttyApp {
|
|||
guard let app = app else { return }
|
||||
ghostty_app_tick(app)
|
||||
}
|
||||
|
||||
private func handleAction(target: ghostty_target_s, action: ghostty_action_s) -> Bool {
|
||||
guard target.tag == GHOSTTY_TARGET_SURFACE else { return false }
|
||||
guard let userdata = ghostty_surface_userdata(target.target.surface) else { return false }
|
||||
let surfaceView = Unmanaged<GhosttyNSView>.fromOpaque(userdata).takeUnretainedValue()
|
||||
|
||||
switch action.tag {
|
||||
case GHOSTTY_ACTION_SCROLLBAR:
|
||||
let scrollbar = GhosttyScrollbar(c: action.action.scrollbar)
|
||||
surfaceView.scrollbar = scrollbar
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyDidUpdateScrollbar,
|
||||
object: surfaceView,
|
||||
userInfo: [GhosttyNotificationKey.scrollbar: scrollbar]
|
||||
)
|
||||
return true
|
||||
case GHOSTTY_ACTION_CELL_SIZE:
|
||||
let cellSize = CGSize(
|
||||
width: CGFloat(action.action.cell_size.width),
|
||||
height: CGFloat(action.action.cell_size.height)
|
||||
)
|
||||
surfaceView.cellSize = cellSize
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyDidUpdateCellSize,
|
||||
object: surfaceView,
|
||||
userInfo: [GhosttyNotificationKey.cellSize: cellSize]
|
||||
)
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Terminal Surface (owns the ghostty_surface_t lifecycle)
|
||||
|
|
@ -124,6 +155,7 @@ class TerminalSurface {
|
|||
var surfaceConfig = ghostty_surface_config_new()
|
||||
surfaceConfig.platform_tag = GHOSTTY_PLATFORM_MACOS
|
||||
surfaceConfig.platform.macos.nsview = Unmanaged.passUnretained(view).toOpaque()
|
||||
surfaceConfig.userdata = Unmanaged.passUnretained(view).toOpaque()
|
||||
surfaceConfig.scale_factor = scale
|
||||
surfaceConfig.context = GHOSTTY_SURFACE_CONTEXT_TAB
|
||||
|
||||
|
|
@ -149,7 +181,6 @@ class TerminalSurface {
|
|||
let scale = view.window?.screen?.backingScaleFactor ?? NSScreen.main?.backingScaleFactor ?? 2.0
|
||||
|
||||
updateMetalLayer(for: view)
|
||||
|
||||
// Update the nsview pointer in the surface
|
||||
ghostty_surface_set_content_scale(surface, scale, scale)
|
||||
ghostty_surface_set_size(
|
||||
|
|
@ -222,6 +253,8 @@ class TerminalSurface {
|
|||
class GhosttyNSView: NSView {
|
||||
var terminalSurface: TerminalSurface?
|
||||
private var surfaceAttached = false
|
||||
var scrollbar: GhosttyScrollbar?
|
||||
var cellSize: CGSize = .zero
|
||||
|
||||
override func makeBackingLayer() -> CALayer {
|
||||
let metalLayer = CAMetalLayer()
|
||||
|
|
@ -294,6 +327,13 @@ class GhosttyNSView: NSView {
|
|||
terminalSurface?.surface
|
||||
}
|
||||
|
||||
func performBindingAction(_ action: String) -> Bool {
|
||||
guard let surface = surface else { return false }
|
||||
return action.withCString { cString in
|
||||
ghostty_surface_binding_action(surface, cString, UInt(strlen(cString)))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Input Handling
|
||||
|
||||
override var acceptsFirstResponder: Bool { true }
|
||||
|
|
@ -514,6 +554,198 @@ class GhosttyNSView: NSView {
|
|||
}
|
||||
}
|
||||
|
||||
struct GhosttyScrollbar {
|
||||
let total: UInt64
|
||||
let offset: UInt64
|
||||
let len: UInt64
|
||||
|
||||
init(c: ghostty_action_scrollbar_s) {
|
||||
total = c.total
|
||||
offset = c.offset
|
||||
len = c.len
|
||||
}
|
||||
}
|
||||
|
||||
enum GhosttyNotificationKey {
|
||||
static let scrollbar = "ghostty.scrollbar"
|
||||
static let cellSize = "ghostty.cellSize"
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
static let ghosttyDidUpdateScrollbar = Notification.Name("ghosttyDidUpdateScrollbar")
|
||||
static let ghosttyDidUpdateCellSize = Notification.Name("ghosttyDidUpdateCellSize")
|
||||
}
|
||||
|
||||
// MARK: - Scroll View Wrapper (Ghostty-style scrollbar)
|
||||
|
||||
final class GhosttySurfaceScrollView: NSView {
|
||||
private let scrollView: NSScrollView
|
||||
private let documentView: NSView
|
||||
private let surfaceView: GhosttyNSView
|
||||
private var observers: [NSObjectProtocol] = []
|
||||
private var isLiveScrolling = false
|
||||
private var lastSentRow: Int?
|
||||
|
||||
init(surfaceView: GhosttyNSView) {
|
||||
self.surfaceView = surfaceView
|
||||
scrollView = NSScrollView()
|
||||
scrollView.hasVerticalScroller = true
|
||||
scrollView.hasHorizontalScroller = false
|
||||
scrollView.autohidesScrollers = false
|
||||
scrollView.usesPredominantAxisScrolling = true
|
||||
scrollView.scrollerStyle = .overlay
|
||||
scrollView.drawsBackground = false
|
||||
scrollView.contentView.clipsToBounds = false
|
||||
|
||||
documentView = NSView(frame: .zero)
|
||||
scrollView.documentView = documentView
|
||||
documentView.addSubview(surfaceView)
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
addSubview(scrollView)
|
||||
|
||||
scrollView.contentView.postsBoundsChangedNotifications = true
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: NSView.boundsDidChangeNotification,
|
||||
object: scrollView.contentView,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.handleScrollChange()
|
||||
})
|
||||
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: NSScrollView.willStartLiveScrollNotification,
|
||||
object: scrollView,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.isLiveScrolling = true
|
||||
})
|
||||
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: NSScrollView.didEndLiveScrollNotification,
|
||||
object: scrollView,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.isLiveScrolling = false
|
||||
})
|
||||
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .ghosttyDidUpdateScrollbar,
|
||||
object: surfaceView,
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
self?.handleScrollbarUpdate(notification)
|
||||
})
|
||||
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: .ghosttyDidUpdateCellSize,
|
||||
object: surfaceView,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.synchronizeScrollView()
|
||||
})
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) not implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
observers.forEach { NotificationCenter.default.removeObserver($0) }
|
||||
}
|
||||
|
||||
override var safeAreaInsets: NSEdgeInsets { NSEdgeInsetsZero }
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
scrollView.frame = bounds
|
||||
surfaceView.frame.size = scrollView.bounds.size
|
||||
documentView.frame.size.width = scrollView.bounds.width
|
||||
synchronizeScrollView()
|
||||
synchronizeSurfaceView()
|
||||
}
|
||||
|
||||
override func scrollWheel(with event: NSEvent) {
|
||||
// Route scroll wheel events to the surface so the terminal core
|
||||
// can decide whether to scroll scrollback or send mouse events.
|
||||
if window?.firstResponder !== surfaceView {
|
||||
window?.makeFirstResponder(surfaceView)
|
||||
}
|
||||
surfaceView.scrollWheel(with: event)
|
||||
}
|
||||
|
||||
func attachSurface(_ terminalSurface: TerminalSurface) {
|
||||
surfaceView.attachSurface(terminalSurface)
|
||||
}
|
||||
|
||||
func setActive(_ active: Bool) {
|
||||
if active {
|
||||
DispatchQueue.main.async {
|
||||
self.window?.makeFirstResponder(self.surfaceView)
|
||||
}
|
||||
} else {
|
||||
surfaceView.terminalSurface?.setFocus(false)
|
||||
}
|
||||
}
|
||||
|
||||
private func synchronizeSurfaceView() {
|
||||
let visibleRect = scrollView.contentView.documentVisibleRect
|
||||
surfaceView.frame.origin = visibleRect.origin
|
||||
}
|
||||
|
||||
private func synchronizeScrollView() {
|
||||
documentView.frame.size.height = documentHeight()
|
||||
|
||||
if !isLiveScrolling {
|
||||
let cellHeight = surfaceView.cellSize.height
|
||||
if cellHeight > 0, let scrollbar = surfaceView.scrollbar {
|
||||
let offsetY =
|
||||
CGFloat(scrollbar.total - scrollbar.offset - scrollbar.len) * cellHeight
|
||||
scrollView.contentView.scroll(to: CGPoint(x: 0, y: offsetY))
|
||||
lastSentRow = Int(scrollbar.offset)
|
||||
}
|
||||
}
|
||||
|
||||
scrollView.reflectScrolledClipView(scrollView.contentView)
|
||||
}
|
||||
|
||||
private func handleScrollChange() {
|
||||
synchronizeSurfaceView()
|
||||
guard isLiveScrolling else { return }
|
||||
let cellHeight = surfaceView.cellSize.height
|
||||
guard cellHeight > 0 else { return }
|
||||
|
||||
let visibleRect = scrollView.contentView.documentVisibleRect
|
||||
let documentHeight = documentView.frame.height
|
||||
let scrollOffset = documentHeight - visibleRect.origin.y - visibleRect.height
|
||||
let row = Int(scrollOffset / cellHeight)
|
||||
|
||||
guard row != lastSentRow else { return }
|
||||
lastSentRow = row
|
||||
_ = surfaceView.performBindingAction("scroll_to_row:\(row)")
|
||||
}
|
||||
|
||||
private func handleScrollbarUpdate(_ notification: Notification) {
|
||||
guard let scrollbar = notification.userInfo?[GhosttyNotificationKey.scrollbar] as? GhosttyScrollbar else {
|
||||
return
|
||||
}
|
||||
surfaceView.scrollbar = scrollbar
|
||||
synchronizeScrollView()
|
||||
}
|
||||
|
||||
private func documentHeight() -> CGFloat {
|
||||
let contentHeight = scrollView.contentSize.height
|
||||
let cellHeight = surfaceView.cellSize.height
|
||||
if cellHeight > 0, let scrollbar = surfaceView.scrollbar {
|
||||
let documentGridHeight = CGFloat(scrollbar.total) * cellHeight
|
||||
let padding = contentHeight - (CGFloat(scrollbar.len) * cellHeight)
|
||||
return documentGridHeight + padding
|
||||
}
|
||||
return contentHeight
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSTextInputClient
|
||||
|
||||
extension GhosttyNSView: NSTextInputClient {
|
||||
|
|
@ -609,30 +841,16 @@ struct GhosttyTerminalView: NSViewRepresentable {
|
|||
let terminalSurface: TerminalSurface
|
||||
var isActive: Bool = true
|
||||
|
||||
func makeNSView(context: Context) -> GhosttyNSView {
|
||||
let view = GhosttyNSView(frame: .zero)
|
||||
func makeNSView(context: Context) -> GhosttySurfaceScrollView {
|
||||
let surfaceView = GhosttyNSView(frame: .zero)
|
||||
let view = GhosttySurfaceScrollView(surfaceView: surfaceView)
|
||||
view.attachSurface(terminalSurface)
|
||||
// Focus after view is in window
|
||||
if isActive {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
view.window?.makeFirstResponder(view)
|
||||
}
|
||||
}
|
||||
view.setActive(isActive)
|
||||
return view
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: GhosttyNSView, context: Context) {
|
||||
// Ensure the surface is attached
|
||||
func updateNSView(_ nsView: GhosttySurfaceScrollView, context: Context) {
|
||||
nsView.attachSurface(terminalSurface)
|
||||
|
||||
if isActive {
|
||||
// Focus on tab switch and notify surface
|
||||
DispatchQueue.main.async {
|
||||
nsView.window?.makeFirstResponder(nsView)
|
||||
}
|
||||
} else {
|
||||
// Unfocus when tab becomes inactive
|
||||
terminalSurface.setFocus(false)
|
||||
}
|
||||
nsView.setActive(isActive)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,16 +35,54 @@ struct TerminalContainerView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// Custom wrapper to handle first responder and layout
|
||||
final class ScrollReportingTerminalView: LocalProcessTerminalView {
|
||||
var onScroll: (() -> Void)?
|
||||
|
||||
override func scrolled(source: TerminalView, position: Double) {
|
||||
super.scrolled(source: source, position: position)
|
||||
onScroll?()
|
||||
}
|
||||
|
||||
override func scrollWheel(with event: NSEvent) {
|
||||
if let scrollView = enclosingScrollView {
|
||||
scrollView.scrollWheel(with: event)
|
||||
return
|
||||
}
|
||||
super.scrollWheel(with: event)
|
||||
}
|
||||
}
|
||||
|
||||
// Custom wrapper to handle first responder and native scrollbars
|
||||
class FocusableTerminalView: NSView {
|
||||
var terminalView: LocalProcessTerminalView?
|
||||
private var scroller: NSScroller?
|
||||
private var fadeTimer: Timer?
|
||||
private var scrollMonitor: Any?
|
||||
private var lastScrollerValue: Double = 0
|
||||
private let scrollView = NSScrollView()
|
||||
private let documentView = NSView()
|
||||
private let debugScrollerOverlay = NSView()
|
||||
var terminalView: ScrollReportingTerminalView? {
|
||||
didSet {
|
||||
configureTerminalView()
|
||||
}
|
||||
}
|
||||
private var observers: [NSObjectProtocol] = []
|
||||
private var isLiveScrolling = false
|
||||
private var isProgrammaticScroll = false
|
||||
private var lastSentPosition: Double?
|
||||
|
||||
override var acceptsFirstResponder: Bool { true }
|
||||
|
||||
override init(frame frameRect: NSRect) {
|
||||
super.init(frame: frameRect)
|
||||
setupScrollView()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setupScrollView()
|
||||
}
|
||||
|
||||
deinit {
|
||||
observers.forEach { NotificationCenter.default.removeObserver($0) }
|
||||
}
|
||||
|
||||
override func becomeFirstResponder() -> Bool {
|
||||
if let tv = terminalView {
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -61,73 +99,130 @@ class FocusableTerminalView: NSView {
|
|||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
scrollView.frame = bounds
|
||||
let scrollerWidth = NSScroller.scrollerWidth(for: .regular, scrollerStyle: scrollView.scrollerStyle)
|
||||
debugScrollerOverlay.frame = NSRect(x: bounds.maxX - scrollerWidth, y: 0, width: scrollerWidth, height: bounds.height)
|
||||
if let tv = terminalView, bounds.size.width > 0, bounds.size.height > 0 {
|
||||
tv.setFrameSize(bounds.size)
|
||||
setupScrollerTracking(in: tv)
|
||||
tv.setFrameSize(scrollView.contentSize)
|
||||
documentView.frame.size.width = scrollView.bounds.width
|
||||
synchronizeScrollView()
|
||||
synchronizeTerminalView()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidMoveToWindow() {
|
||||
super.viewDidMoveToWindow()
|
||||
if window != nil, let tv = terminalView, bounds.size.width > 0 {
|
||||
tv.setFrameSize(bounds.size)
|
||||
setupScrollerTracking(in: tv)
|
||||
setupScrollMonitor()
|
||||
tv.setFrameSize(scrollView.contentSize)
|
||||
documentView.frame.size.width = scrollView.bounds.width
|
||||
synchronizeScrollView()
|
||||
synchronizeTerminalView()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillMove(toWindow newWindow: NSWindow?) {
|
||||
super.viewWillMove(toWindow: newWindow)
|
||||
if newWindow == nil, let monitor = scrollMonitor {
|
||||
NSEvent.removeMonitor(monitor)
|
||||
scrollMonitor = nil
|
||||
private func setupScrollView() {
|
||||
scrollView.hasVerticalScroller = true
|
||||
scrollView.hasHorizontalScroller = false
|
||||
scrollView.autohidesScrollers = false
|
||||
scrollView.usesPredominantAxisScrolling = true
|
||||
scrollView.scrollerStyle = .overlay
|
||||
scrollView.drawsBackground = false
|
||||
scrollView.contentView.clipsToBounds = false
|
||||
scrollView.documentView = documentView
|
||||
addSubview(scrollView)
|
||||
|
||||
debugScrollerOverlay.wantsLayer = true
|
||||
debugScrollerOverlay.layer?.backgroundColor = NSColor.systemRed.withAlphaComponent(0.25).cgColor
|
||||
addSubview(debugScrollerOverlay)
|
||||
|
||||
scrollView.contentView.postsBoundsChangedNotifications = true
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: NSView.boundsDidChangeNotification,
|
||||
object: scrollView.contentView,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.handleScrollChange()
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: NSScrollView.willStartLiveScrollNotification,
|
||||
object: scrollView,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.isLiveScrolling = true
|
||||
})
|
||||
observers.append(NotificationCenter.default.addObserver(
|
||||
forName: NSScrollView.didEndLiveScrollNotification,
|
||||
object: scrollView,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.isLiveScrolling = false
|
||||
})
|
||||
}
|
||||
|
||||
private func configureTerminalView() {
|
||||
guard let tv = terminalView else { return }
|
||||
tv.onScroll = { [weak self] in
|
||||
self?.synchronizeScrollView()
|
||||
}
|
||||
documentView.addSubview(tv)
|
||||
hideInternalScroller(in: tv)
|
||||
synchronizeScrollView()
|
||||
synchronizeTerminalView()
|
||||
}
|
||||
|
||||
private func documentHeight() -> CGFloat {
|
||||
let contentHeight = scrollView.contentSize.height
|
||||
guard let tv = terminalView, tv.canScroll else { return contentHeight }
|
||||
let thumb = max(tv.scrollThumbsize, 0.01)
|
||||
return max(contentHeight / thumb, contentHeight)
|
||||
}
|
||||
|
||||
private func synchronizeScrollView() {
|
||||
documentView.frame.size.height = documentHeight()
|
||||
guard let tv = terminalView else { return }
|
||||
|
||||
if !isLiveScrolling {
|
||||
let contentHeight = scrollView.contentSize.height
|
||||
let maxOffset = max(documentView.frame.height - contentHeight, 0)
|
||||
let offsetY = (1 - tv.scrollPosition) * maxOffset
|
||||
isProgrammaticScroll = true
|
||||
scrollView.contentView.scroll(to: CGPoint(x: 0, y: offsetY))
|
||||
scrollView.reflectScrolledClipView(scrollView.contentView)
|
||||
isProgrammaticScroll = false
|
||||
} else {
|
||||
scrollView.reflectScrolledClipView(scrollView.contentView)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupScrollerTracking(in view: NSView) {
|
||||
if scroller == nil {
|
||||
for subview in view.subviews {
|
||||
if let s = subview as? NSScroller {
|
||||
scroller = s
|
||||
s.alphaValue = 0 // Start hidden
|
||||
lastScrollerValue = s.doubleValue
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
private func synchronizeTerminalView() {
|
||||
guard let tv = terminalView else { return }
|
||||
let visibleRect = scrollView.contentView.documentVisibleRect
|
||||
tv.frame.origin = visibleRect.origin
|
||||
tv.frame.size = scrollView.contentSize
|
||||
}
|
||||
|
||||
private func setupScrollMonitor() {
|
||||
guard scrollMonitor == nil else { return }
|
||||
|
||||
// Monitor scroll wheel events
|
||||
scrollMonitor = NSEvent.addLocalMonitorForEvents(matching: .scrollWheel) { [weak self] event in
|
||||
if let self = self,
|
||||
let window = self.window,
|
||||
event.window == window {
|
||||
self.showScrollerTemporarily()
|
||||
}
|
||||
return event
|
||||
private func handleScrollChange() {
|
||||
synchronizeTerminalView()
|
||||
guard !isProgrammaticScroll, let tv = terminalView else { return }
|
||||
let contentHeight = scrollView.contentSize.height
|
||||
let maxOffset = max(documentView.frame.height - contentHeight, 0)
|
||||
guard maxOffset > 0 else { return }
|
||||
let offsetY = scrollView.contentView.documentVisibleRect.origin.y
|
||||
let position = 1 - Double(offsetY / maxOffset)
|
||||
if let last = lastSentPosition, abs(last - position) < 0.0001 {
|
||||
return
|
||||
}
|
||||
lastSentPosition = position
|
||||
tv.scroll(toPosition: position)
|
||||
}
|
||||
|
||||
func showScrollerTemporarily() {
|
||||
guard let scroller = scroller else { return }
|
||||
|
||||
// Show scroller
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
context.duration = 0.15
|
||||
scroller.animator().alphaValue = 1
|
||||
}
|
||||
|
||||
// Cancel existing timer
|
||||
fadeTimer?.invalidate()
|
||||
|
||||
// Fade out after 1.5 seconds of no scrolling
|
||||
fadeTimer = Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false) { [weak self] _ in
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
context.duration = 0.3
|
||||
self?.scroller?.animator().alphaValue = 0
|
||||
private func hideInternalScroller(in view: NSView) {
|
||||
for subview in view.subviews {
|
||||
if let scroller = subview as? NSScroller {
|
||||
scroller.isHidden = true
|
||||
scroller.alphaValue = 0
|
||||
} else if !subview.subviews.isEmpty {
|
||||
hideInternalScroller(in: subview)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -141,7 +236,7 @@ struct SwiftTermView: NSViewRepresentable {
|
|||
let containerView = FocusableTerminalView()
|
||||
containerView.wantsLayer = true
|
||||
|
||||
let terminalView = LocalProcessTerminalView(frame: CGRect(x: 0, y: 0, width: 800, height: 600))
|
||||
let terminalView = ScrollReportingTerminalView(frame: CGRect(x: 0, y: 0, width: 800, height: 600))
|
||||
|
||||
// Use autoresizingMask instead of Auto Layout for SwiftTerm compatibility
|
||||
terminalView.autoresizingMask = [.width, .height]
|
||||
|
|
@ -172,7 +267,6 @@ struct SwiftTermView: NSViewRepresentable {
|
|||
context.coordinator.terminalView = terminalView
|
||||
context.coordinator.containerView = containerView
|
||||
|
||||
containerView.addSubview(terminalView)
|
||||
containerView.terminalView = terminalView
|
||||
|
||||
// Get shell path
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue