diff --git a/docs/SMOKE_TEST.md b/docs/SMOKE_TEST.md index fefb357..a3fe144 100644 --- a/docs/SMOKE_TEST.md +++ b/docs/SMOKE_TEST.md @@ -54,7 +54,7 @@ Use this checklist before a release or after changes to panel, pasteboard, setti 21. Right-click a card, use Capture Rules to ignore its source app, copy from that app again, and confirm the new item is skipped. 22. Drag an unassigned card onto the renamed collection chip and confirm the chip count increases and the card appears when that collection is selected. 23. Resize or test on a narrow display and confirm the bottom shelf switches to compact cards that still show two recent clips cleanly. -24. Select a file, rich text, or URL card and confirm the selected-card rail exposes `Paste Plain Text`; on a narrow shelf, confirm secondary actions collapse behind `More` instead of overflowing the card. +24. Select a file, rich text, or URL card and confirm the selected-card rail exposes `Paste Plain Text`, the corner source/kind badge remains visible, and on a narrow shelf secondary actions collapse behind `More` instead of overflowing the card. ## Copy And Paste diff --git a/sources/clipbored/views/ClipboardPanelView.swift b/sources/clipbored/views/ClipboardPanelView.swift index 0c4021c..2e6a909 100644 --- a/sources/clipbored/views/ClipboardPanelView.swift +++ b/sources/clipbored/views/ClipboardPanelView.swift @@ -2320,7 +2320,8 @@ private final class AspectFillImageView: NSView { private final class ClipboardItemCardView: NSView, NSDraggingSource { private enum Metrics { static let dragThreshold: CGFloat = 4 - static let actionRailHorizontalMargin: CGFloat = 12 + static let actionRailBadgeGap: CGFloat = 8 + static let actionRailLeadingMargin: CGFloat = 10 } private enum Palette { static let border = NSColor.separatorColor.withAlphaComponent(0.20).cgColor @@ -2942,7 +2943,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource { } private func fittedActionRailButtonSpecs(from specs: [ActionRailButtonSpec]) -> [ActionRailButtonSpec] { - let maximumWidth = layout.width - (Metrics.actionRailHorizontalMargin * 2) + let maximumWidth = maximumVisibleActionRailWidth guard actionRailWidth(for: specs) > maximumWidth else { return specs } let moreSpec = ActionRailButtonSpec("ellipsis.circle", toolTip: "More", action: #selector(showMoreActionsFromActionRail(_:))) @@ -2980,6 +2981,22 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource { + actionRail.edgeInsets.right } + private var maximumVisibleActionRailWidth: CGFloat { + layout.width + - Metrics.actionRailLeadingMargin + - layout.inset + - Metrics.actionRailBadgeGap + - headerBadgeSize + } + + private var headerBadgeSize: CGFloat { + layout.isCompact ? 36 : 42 + } + + private var actionRailHeaderTopInset: CGFloat { + max(8, (layout.headerHeight - layout.actionRailHeight) / 2) + } + private func cardActionButton( _ systemName: String, toolTip: String, @@ -3018,7 +3035,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource { private func updateActionRailVisibility() { actionRail.isHidden = !isSelected - headerBadgeView?.isHidden = isSelected + headerBadgeView?.isHidden = false headerPinView?.isHidden = isSelected footerDetailLabel.isHidden = false for button in actionRailButtons { @@ -3199,9 +3216,18 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource { footer.widthAnchor.constraint(equalTo: stack.widthAnchor) ]) contentView.addSubview(actionRail) + let actionRailTrailingConstraint: NSLayoutConstraint + if let headerBadgeView { + actionRailTrailingConstraint = actionRail.trailingAnchor.constraint( + equalTo: headerBadgeView.leadingAnchor, + constant: -Metrics.actionRailBadgeGap + ) + } else { + actionRailTrailingConstraint = actionRail.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12) + } NSLayoutConstraint.activate([ - actionRail.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12), - actionRail.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 11) + actionRailTrailingConstraint, + actionRail.topAnchor.constraint(equalTo: contentView.topAnchor, constant: actionRailHeaderTopInset) ]) setSelected(false) } @@ -3264,8 +3290,8 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource { labelStack.trailingAnchor.constraint(lessThanOrEqualTo: badge.leadingAnchor, constant: -12), badge.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -layout.inset), badge.centerYAnchor.constraint(equalTo: header.centerYAnchor), - badge.widthAnchor.constraint(equalToConstant: layout.isCompact ? 36 : 42), - badge.heightAnchor.constraint(equalToConstant: layout.isCompact ? 36 : 42), + badge.widthAnchor.constraint(equalToConstant: headerBadgeSize), + badge.heightAnchor.constraint(equalToConstant: headerBadgeSize), separator.leadingAnchor.constraint(equalTo: header.leadingAnchor, constant: layout.inset), separator.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -layout.inset), separator.bottomAnchor.constraint(equalTo: header.bottomAnchor), diff --git a/tests/clipboredtests/ClipboardPanelViewTests.swift b/tests/clipboredtests/ClipboardPanelViewTests.swift index 10f53a3..3d9d522 100644 --- a/tests/clipboredtests/ClipboardPanelViewTests.swift +++ b/tests/clipboredtests/ClipboardPanelViewTests.swift @@ -366,17 +366,17 @@ final class ClipboardPanelViewTests: XCTestCase { XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Pin", "Collect", "Add to Stack", "Edit", "Preview", "Delete"]) XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 238) XCTAssertFalse(fixture.view.debugFirstCardFooterDetailIsHidden) - XCTAssertTrue(fixture.view.debugFirstCardHeaderBadgeIsHidden) + XCTAssertFalse(fixture.view.debugFirstCardHeaderBadgeIsHidden) fixture.store.upsert(makeItem(kind: .file, text: "/tmp/report.txt", store: fixture.store)) drainMainQueue() fixture.viewModel.selectFirstItem() XCTAssertEqual(fixture.viewModel.visibleItems.first?.kind, .file) - XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Pin", "Collect", "Add to Stack", "Preview", "Open", "Reveal", "More"]) - XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 294) + XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Collect", "Add to Stack", "Preview", "Open", "More"]) + XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 238) XCTAssertFalse(fixture.view.debugFirstCardFooterDetailIsHidden) - XCTAssertTrue(fixture.view.debugFirstCardHeaderBadgeIsHidden) + XCTAssertFalse(fixture.view.debugFirstCardHeaderBadgeIsHidden) } func testCompactFileCardActionsFitInsideShelfWithOverflowMenu() { @@ -387,9 +387,10 @@ final class ClipboardPanelViewTests: XCTestCase { fixture.window.contentView?.layoutSubtreeIfNeeded() XCTAssertEqual(fixture.view.debugCardDensity, "compact") - XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Collect", "Add to Stack", "Preview", "Open", "More"]) - XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 222) - XCTAssertLessThanOrEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 240) + XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Collect", "Add to Stack", "Preview", "More"]) + XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 196) + XCTAssertLessThanOrEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 197) + XCTAssertFalse(fixture.view.debugFirstCardHeaderBadgeIsHidden) XCTAssertEqual( fixture.view.debugFirstCardMenuTitles, ["Paste", "Copy", "Paste Plain Text", "Copy Plain Text", "Rename...", "Add to Stack", "Quick Look", "Pin", "Add to Collection", "Capture Rules", "-", "Open", "Reveal in Finder", "-", "Delete"]