Skip to content
Open
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
6 changes: 5 additions & 1 deletion Sources/SwiftTerm/Apple/AppleTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ extension TerminalView {
resetCaches()
self.cellDimension = computeFontDimensions ()
if (frame.width > 0) && (frame.height > 0) {
let newCols = Int(frame.width / cellDimension.width)
// Use getEffectiveWidth so the scroller's reserved width is taken
// into account, matching processSizeChange(). Computing columns from
// the raw frame width here would over-count by the scroller width,
// so zooming the font in and back out would drift the column count.
let newCols = Int(getEffectiveWidth(size: frame.size) / cellDimension.width)
let newRows = Int(frame.height / cellDimension.height)
resize(cols: newCols, rows: newRows)
}
Expand Down
51 changes: 51 additions & 0 deletions Tests/SwiftTermTests/FontResizeColumnsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// FontResizeColumnsTests.swift
// SwiftTerm
//
// Regression tests ensuring that after a font change the terminal content
// stays clear of the scroller, i.e. the recomputed geometry reserves the
// scroller width just like the live window-resize path does.
//

#if os(macOS)
import Foundation
import Testing
import AppKit
@testable import SwiftTerm

final class FontResizeColumnsTests {
/// The core invariant: after a font change the rendered columns must fit
/// within the area that is not covered by the scroller. If `resetFont`
/// sized the terminal from the raw frame width, the right-most columns
/// would be drawn underneath the scroller and become hidden.
@Test func testFontChangeKeepsContentClearOfScroller() {
let frame = CGRect(x: 0, y: 0, width: 400, height: 200)
let view = TerminalView(frame: frame)
view.font = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular)

let renderedWidth = CGFloat(view.getTerminal().cols) * view.cellDimension.width
let usableWidth = view.getEffectiveWidth(size: frame.size)

// Content must not extend into the reserved scroller strip.
#expect(renderedWidth <= usableWidth)
// Sanity: the scroller actually reserves space in this configuration,
// so the test is exercising a non-trivial case.
#expect(usableWidth < frame.width)
}

/// The font-change path (`resetFont`) and the live-resize path
/// (`processSizeChange`) must agree on the column count for a given frame,
/// so zooming the font in and back out never drifts the column count.
@Test func testFontChangeColumnsMatchResizePath() {
let frame = CGRect(x: 0, y: 0, width: 400, height: 200)
let view = TerminalView(frame: frame)
view.font = NSFont.monospacedSystemFont(ofSize: 12, weight: .regular)

let colsAfterFontChange = view.getTerminal().cols
view.processSizeChange(newSize: frame.size)
let colsAfterResize = view.getTerminal().cols

#expect(colsAfterFontChange == colsAfterResize)
}
}
#endif
Loading