WIP: keep selected clip visible in shelf

This commit is contained in:
Akshay Kolli
2026-06-30 01:29:36 -07:00
parent badc649b25
commit 95430e98cb
2 changed files with 66 additions and 1 deletions

View File

@@ -566,9 +566,30 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
}
private func updateSelection() {
var selectedCard: ClipboardItemCardView?
for (index, card) in cardViews.enumerated() {
card.setSelected(index == viewModel.selectedIndex)
let selected = index == viewModel.selectedIndex
card.setSelected(selected)
if selected {
selectedCard = card
}
}
if let selectedCard {
scrollCardIntoView(selectedCard)
}
}
private func scrollCardIntoView(_ card: NSView) {
guard scrollView.documentView === itemsStack else { return }
guard card.window != nil else { return }
scrollView.layoutSubtreeIfNeeded()
itemsStack.layoutSubtreeIfNeeded()
let frame = card.convert(card.bounds, to: itemsStack)
let paddedFrame = frame.insetBy(dx: -Metrics.cardSpacing, dy: 0)
itemsStack.scrollToVisible(paddedFrame)
scrollView.reflectScrolledClipView(scrollView.contentView)
}
private func updateStatus(_ message: String) {
@@ -846,6 +867,18 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
cardViews.map(\.debugHeaderBadgeSymbol)
}
var debugSelectedCardFrameInDocument: NSRect {
guard viewModel.selectedIndex >= 0, viewModel.selectedIndex < cardViews.count else {
return .zero
}
let card = cardViews[viewModel.selectedIndex]
return card.convert(card.bounds, to: itemsStack)
}
var debugCardRailVisibleRect: NSRect {
scrollView.contentView.bounds
}
var debugFirstCardMenuTitles: [String] {
cardViews.first?.debugMenuTitles ?? []
}

View File

@@ -243,6 +243,38 @@ final class ClipboardPanelViewTests: XCTestCase {
XCTAssertTrue(fixture.view.debugCustomCollectionTitles.contains("Product References"))
}
func testSelectionScrollsCardRailToKeepSelectedCardVisible() {
let fixture = makePanelFixture()
fixture.window.setFrame(NSRect(x: 0, y: 0, width: 620, height: 520), display: true)
for index in 0..<8 {
fixture.store.upsert(makeTextItem("Scrollable clipboard item \(index)", store: fixture.store))
drainMainQueue()
}
fixture.window.contentView?.layoutSubtreeIfNeeded()
fixture.viewModel.selectFirstItem()
drainMainQueue()
fixture.window.contentView?.layoutSubtreeIfNeeded()
XCTAssertLessThanOrEqual(fixture.view.debugCardRailVisibleRect.minX, 1)
fixture.viewModel.selectItem(at: fixture.viewModel.visibleItems.count - 1)
drainMainQueue()
fixture.window.contentView?.layoutSubtreeIfNeeded()
let visibleRect = fixture.view.debugCardRailVisibleRect
let selectedFrame = fixture.view.debugSelectedCardFrameInDocument
XCTAssertGreaterThan(visibleRect.minX, 0)
XCTAssertLessThanOrEqual(selectedFrame.minX, visibleRect.maxX)
XCTAssertGreaterThanOrEqual(visibleRect.maxX + 1, selectedFrame.maxX)
fixture.viewModel.selectItem(at: 0)
drainMainQueue()
fixture.window.contentView?.layoutSubtreeIfNeeded()
XCTAssertLessThanOrEqual(fixture.view.debugCardRailVisibleRect.minX, 1)
}
func testFilteredEmptyStateNamesCurrentCollection() {
let fixture = makePanelFixture()
fixture.store.upsert(makeTextItem("Only text exists", store: fixture.store))