Merge pull request #506 from manaflow-ai/feat-sentry-f1-titlebar-size-throttle

Throttle titlebar controls size-update churn
This commit is contained in:
Lawrence Chen 2026-02-25 16:50:08 -08:00 committed by GitHub
commit 0a807a94e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 112 additions and 8 deletions

View file

@ -657,12 +657,45 @@ private final class TitlebarCommandKeyMonitor: ObservableObject {
}
}
struct TitlebarControlsLayoutSnapshot: Equatable {
let contentSize: NSSize
let containerHeight: CGFloat
let yOffset: CGFloat
}
func titlebarControlsShouldScheduleForViewSizeChange(
previous: NSSize,
current: NSSize,
tolerance: CGFloat = 0.5
) -> Bool {
guard current.width > 0, current.height > 0 else { return false }
guard previous.width > 0, previous.height > 0 else { return true }
return abs(previous.width - current.width) > tolerance
|| abs(previous.height - current.height) > tolerance
}
func titlebarControlsShouldApplyLayout(
previous: TitlebarControlsLayoutSnapshot?,
next: TitlebarControlsLayoutSnapshot,
tolerance: CGFloat = 0.5
) -> Bool {
guard let previous else { return true }
return abs(previous.contentSize.width - next.contentSize.width) > tolerance
|| abs(previous.contentSize.height - next.contentSize.height) > tolerance
|| abs(previous.containerHeight - next.containerHeight) > tolerance
|| abs(previous.yOffset - next.yOffset) > tolerance
}
final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewController, NSPopoverDelegate {
private let hostingView: NonDraggableHostingView<TitlebarControlsView>
private let containerView = NSView()
private let notificationStore: TerminalNotificationStore
private lazy var notificationsPopover: NSPopover = makeNotificationsPopover()
private var pendingSizeUpdate = false
private var fittingSizeNeedsRefresh = true
private var cachedFittingSize: NSSize?
private var lastObservedViewSize: NSSize = .zero
private var lastAppliedLayoutSnapshot: TitlebarControlsLayoutSnapshot?
private let viewModel = TitlebarControlsViewModel()
private var userDefaultsObserver: NSObjectProtocol?
var popoverIsShownForTesting: Bool { notificationsPopover.isShown }
@ -696,10 +729,10 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
object: nil,
queue: .main
) { [weak self] _ in
self?.scheduleSizeUpdate()
self?.scheduleSizeUpdate(invalidateFittingSize: true)
}
scheduleSizeUpdate()
scheduleSizeUpdate(invalidateFittingSize: true)
}
required init?(coder: NSCoder) {
@ -714,15 +747,26 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
override func viewDidAppear() {
super.viewDidAppear()
scheduleSizeUpdate()
scheduleSizeUpdate(invalidateFittingSize: true)
}
override func viewDidLayout() {
super.viewDidLayout()
scheduleSizeUpdate()
let currentViewSize = view.bounds.size
guard titlebarControlsShouldScheduleForViewSizeChange(
previous: lastObservedViewSize,
current: currentViewSize
) else {
return
}
lastObservedViewSize = currentViewSize
scheduleSizeUpdate(invalidateFittingSize: true)
}
private func scheduleSizeUpdate() {
private func scheduleSizeUpdate(invalidateFittingSize: Bool = false) {
if invalidateFittingSize {
fittingSizeNeedsRefresh = true
}
guard !pendingSizeUpdate else { return }
pendingSizeUpdate = true
DispatchQueue.main.async { [weak self] in
@ -732,14 +776,33 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
}
private func updateSize() {
hostingView.invalidateIntrinsicContentSize()
hostingView.layoutSubtreeIfNeeded()
let contentSize = hostingView.fittingSize
let contentSize: NSSize
if fittingSizeNeedsRefresh || cachedFittingSize == nil {
hostingView.invalidateIntrinsicContentSize()
hostingView.layoutSubtreeIfNeeded()
cachedFittingSize = hostingView.fittingSize
fittingSizeNeedsRefresh = false
}
contentSize = cachedFittingSize ?? .zero
guard contentSize.width > 0, contentSize.height > 0 else { return }
let titlebarHeight = view.window.map { window in
window.frame.height - window.contentLayoutRect.height
} ?? contentSize.height
let containerHeight = max(contentSize.height, titlebarHeight)
let yOffset = max(0, (containerHeight - contentSize.height) / 2.0)
let nextLayoutSnapshot = TitlebarControlsLayoutSnapshot(
contentSize: contentSize,
containerHeight: containerHeight,
yOffset: yOffset
)
guard titlebarControlsShouldApplyLayout(
previous: lastAppliedLayoutSnapshot,
next: nextLayoutSnapshot
) else {
return
}
lastAppliedLayoutSnapshot = nextLayoutSnapshot
preferredContentSize = NSSize(width: contentSize.width, height: containerHeight)
containerView.frame = NSRect(x: 0, y: 0, width: contentSize.width, height: containerHeight)
hostingView.frame = NSRect(x: 0, y: yOffset, width: contentSize.width, height: contentSize.height)

View file

@ -222,6 +222,47 @@ final class BrowserInsecureHTTPSettingsTests: XCTestCase {
}
}
final class TitlebarControlsSizingPolicyTests: XCTestCase {
func testSchedulePolicyRequiresMeaningfulViewSizeChange() {
XCTAssertFalse(titlebarControlsShouldScheduleForViewSizeChange(previous: .zero, current: .zero))
XCTAssertTrue(
titlebarControlsShouldScheduleForViewSizeChange(
previous: .zero,
current: NSSize(width: 240, height: 38)
)
)
XCTAssertFalse(
titlebarControlsShouldScheduleForViewSizeChange(
previous: NSSize(width: 240, height: 38),
current: NSSize(width: 240.2, height: 38.1)
)
)
XCTAssertTrue(
titlebarControlsShouldScheduleForViewSizeChange(
previous: NSSize(width: 240, height: 38),
current: NSSize(width: 247, height: 38)
)
)
}
func testLayoutApplyPolicySkipsEquivalentSnapshots() {
let baseline = TitlebarControlsLayoutSnapshot(
contentSize: NSSize(width: 128, height: 22),
containerHeight: 28,
yOffset: 3
)
XCTAssertTrue(titlebarControlsShouldApplyLayout(previous: nil, next: baseline))
XCTAssertFalse(titlebarControlsShouldApplyLayout(previous: baseline, next: baseline))
let changed = TitlebarControlsLayoutSnapshot(
contentSize: NSSize(width: 132, height: 22),
containerHeight: 28,
yOffset: 3
)
XCTAssertTrue(titlebarControlsShouldApplyLayout(previous: baseline, next: changed))
}
}
/// Regression test: ensure new terminal windows are born in full-size content mode so
/// titlebar/content offsets are correct before the first resize.
final class MainWindowLayoutStyleTests: XCTestCase {