WIP: add glyphs to collection rail
This commit is contained in:
@@ -62,6 +62,7 @@ Use this checklist before a release or after changes to panel, pasteboard, setti
|
|||||||
29. Confirm single-line text cards do not repeat the same text in both title and body, while multi-line text cards show the remaining lines below the first line.
|
29. Confirm single-line text cards do not repeat the same text in both title and body, while multi-line text cards show the remaining lines below the first line.
|
||||||
30. Confirm the Pinned empty state points to the Pin action instead of a plain-key shortcut.
|
30. Confirm the Pinned empty state points to the Pin action instead of a plain-key shortcut.
|
||||||
31. Confirm each card's source or type badge reads as an attached header-corner tile instead of a small floating icon.
|
31. Confirm each card's source or type badge reads as an attached header-corner tile instead of a small floating icon.
|
||||||
|
32. Confirm built-in collection chips use recognizable glyphs, while custom collection chips keep color-dot swatches.
|
||||||
|
|
||||||
## Copy And Paste
|
## Copy And Paste
|
||||||
|
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
private let collectionScrollView = HorizontalRailScrollView()
|
private let collectionScrollView = HorizontalRailScrollView()
|
||||||
private let collectionStack = NSStackView()
|
private let collectionStack = NSStackView()
|
||||||
private let addCollectionButton = NSButton()
|
private let addCollectionButton = NSButton()
|
||||||
private let stackChip = CollectionChipView(title: "Stack", color: .systemGreen)
|
private let stackChip = CollectionChipView(title: "Stack", color: .systemGreen, symbolName: "square.stack.3d.up.fill")
|
||||||
private let itemsStack = NSStackView()
|
private let itemsStack = NSStackView()
|
||||||
private let scrollView = HorizontalRailScrollView()
|
private let scrollView = HorizontalRailScrollView()
|
||||||
private let statusLabel = NSTextField(labelWithString: "")
|
private let statusLabel = NSTextField(labelWithString: "")
|
||||||
@@ -542,7 +542,7 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for mode in ClipboardSortMode.allCases {
|
for mode in ClipboardSortMode.allCases {
|
||||||
let chip = CollectionChipView(title: collectionTitle(for: mode), color: collectionColor(for: mode))
|
let chip = CollectionChipView(title: collectionTitle(for: mode), color: collectionColor(for: mode), symbolName: collectionSymbol(for: mode))
|
||||||
chip.toolTip = mode.title
|
chip.toolTip = mode.title
|
||||||
chip.onPress = { [weak self] in
|
chip.onPress = { [weak self] in
|
||||||
self?.viewModel.sortMode = mode
|
self?.viewModel.sortMode = mode
|
||||||
@@ -642,6 +642,19 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func collectionSymbol(for mode: ClipboardSortMode) -> String {
|
||||||
|
switch mode {
|
||||||
|
case .mostRecent: return "doc.on.clipboard"
|
||||||
|
case .mostUsed: return "chart.bar.fill"
|
||||||
|
case .text: return "text.alignleft"
|
||||||
|
case .links: return "link"
|
||||||
|
case .images: return "photo"
|
||||||
|
case .audio: return "music.note"
|
||||||
|
case .files: return "doc.fill"
|
||||||
|
case .pinned: return "pin.fill"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func collectionColor(for mode: ClipboardSortMode) -> NSColor {
|
private func collectionColor(for mode: ClipboardSortMode) -> NSColor {
|
||||||
ClipboardCollectionVisuals.color(for: mode)
|
ClipboardCollectionVisuals.color(for: mode)
|
||||||
}
|
}
|
||||||
@@ -1486,6 +1499,10 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
ClipboardSortMode.allCases.compactMap { collectionButtons[$0]?.titleText }
|
ClipboardSortMode.allCases.compactMap { collectionButtons[$0]?.titleText }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var debugCollectionLeadingSymbols: [String] {
|
||||||
|
ClipboardSortMode.allCases.compactMap { collectionButtons[$0]?.debugLeadingSymbolName }
|
||||||
|
}
|
||||||
|
|
||||||
var debugSelectedCollectionTitle: String? {
|
var debugSelectedCollectionTitle: String? {
|
||||||
if stackChip.isSelected {
|
if stackChip.isSelected {
|
||||||
return stackChip.titleText
|
return stackChip.titleText
|
||||||
@@ -2036,7 +2053,9 @@ private final class RailEdgeFadeView: NSView {
|
|||||||
private final class CollectionChipView: NSView {
|
private final class CollectionChipView: NSView {
|
||||||
let titleText: String
|
let titleText: String
|
||||||
private let color: NSColor
|
private let color: NSColor
|
||||||
|
private let symbolName: String?
|
||||||
private let dot = NSView()
|
private let dot = NSView()
|
||||||
|
private let symbolView = NSImageView()
|
||||||
private let label: NSTextField
|
private let label: NSTextField
|
||||||
private let countLabel = NSTextField(labelWithString: "0")
|
private let countLabel = NSTextField(labelWithString: "0")
|
||||||
private(set) var isSelected = false
|
private(set) var isSelected = false
|
||||||
@@ -2052,9 +2071,10 @@ private final class CollectionChipView: NSView {
|
|||||||
var onEdit: (() -> Void)?
|
var onEdit: (() -> Void)?
|
||||||
var onDelete: (() -> Void)?
|
var onDelete: (() -> Void)?
|
||||||
|
|
||||||
init(title: String, color: NSColor) {
|
init(title: String, color: NSColor, symbolName: String? = nil) {
|
||||||
self.titleText = title
|
self.titleText = title
|
||||||
self.color = color
|
self.color = color
|
||||||
|
self.symbolName = symbolName
|
||||||
self.label = NSTextField(labelWithString: title)
|
self.label = NSTextField(labelWithString: title)
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
configure()
|
configure()
|
||||||
@@ -2082,6 +2102,20 @@ private final class CollectionChipView: NSView {
|
|||||||
dot.widthAnchor.constraint(equalToConstant: 8).isActive = true
|
dot.widthAnchor.constraint(equalToConstant: 8).isActive = true
|
||||||
dot.heightAnchor.constraint(equalToConstant: 8).isActive = true
|
dot.heightAnchor.constraint(equalToConstant: 8).isActive = true
|
||||||
|
|
||||||
|
let leadingIndicator: NSView
|
||||||
|
if let symbolName {
|
||||||
|
let image = NSImage(systemSymbolName: symbolName, accessibilityDescription: titleText)
|
||||||
|
image?.isTemplate = true
|
||||||
|
symbolView.image = image
|
||||||
|
symbolView.imageScaling = .scaleProportionallyUpOrDown
|
||||||
|
symbolView.contentTintColor = color.withAlphaComponent(0.86)
|
||||||
|
symbolView.widthAnchor.constraint(equalToConstant: 13).isActive = true
|
||||||
|
symbolView.heightAnchor.constraint(equalToConstant: 13).isActive = true
|
||||||
|
leadingIndicator = symbolView
|
||||||
|
} else {
|
||||||
|
leadingIndicator = dot
|
||||||
|
}
|
||||||
|
|
||||||
label.font = .systemFont(ofSize: NSFont.smallSystemFontSize, weight: .medium)
|
label.font = .systemFont(ofSize: NSFont.smallSystemFontSize, weight: .medium)
|
||||||
label.textColor = .secondaryLabelColor
|
label.textColor = .secondaryLabelColor
|
||||||
label.lineBreakMode = .byTruncatingTail
|
label.lineBreakMode = .byTruncatingTail
|
||||||
@@ -2101,7 +2135,7 @@ private final class CollectionChipView: NSView {
|
|||||||
countLabel.heightAnchor.constraint(equalToConstant: 16).isActive = true
|
countLabel.heightAnchor.constraint(equalToConstant: 16).isActive = true
|
||||||
countLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
countLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
|
||||||
let stack = NSStackView(views: [dot, label, countLabel])
|
let stack = NSStackView(views: [leadingIndicator, label, countLabel])
|
||||||
stack.orientation = .horizontal
|
stack.orientation = .horizontal
|
||||||
stack.alignment = .centerY
|
stack.alignment = .centerY
|
||||||
stack.spacing = 6
|
stack.spacing = 6
|
||||||
@@ -2124,9 +2158,10 @@ private final class CollectionChipView: NSView {
|
|||||||
countLabel.textColor = selected ? .labelColor : .tertiaryLabelColor
|
countLabel.textColor = selected ? .labelColor : .tertiaryLabelColor
|
||||||
countLabel.layer?.backgroundColor = (
|
countLabel.layer?.backgroundColor = (
|
||||||
selected
|
selected
|
||||||
? NSColor.controlAccentColor.withAlphaComponent(0.16)
|
? color.withAlphaComponent(0.16)
|
||||||
: NSColor.labelColor.withAlphaComponent(0.07)
|
: NSColor.labelColor.withAlphaComponent(0.07)
|
||||||
).cgColor
|
).cgColor
|
||||||
|
symbolView.contentTintColor = color.withAlphaComponent(selected ? 0.98 : 0.78)
|
||||||
updateCountLabelVisibility()
|
updateCountLabelVisibility()
|
||||||
updateAccessibility()
|
updateAccessibility()
|
||||||
updateChrome()
|
updateChrome()
|
||||||
@@ -2143,11 +2178,11 @@ private final class CollectionChipView: NSView {
|
|||||||
layer?.backgroundColor = color.withAlphaComponent(0.18).cgColor
|
layer?.backgroundColor = color.withAlphaComponent(0.18).cgColor
|
||||||
layer?.borderColor = color.withAlphaComponent(0.68).cgColor
|
layer?.borderColor = color.withAlphaComponent(0.68).cgColor
|
||||||
} else if isSelected {
|
} else if isSelected {
|
||||||
layer?.backgroundColor = NSColor.windowBackgroundColor.withAlphaComponent(0.58).cgColor
|
layer?.backgroundColor = color.withAlphaComponent(0.13).cgColor
|
||||||
layer?.borderColor = NSColor.controlAccentColor.withAlphaComponent(isKeyboardFocused ? 0.74 : 0.34).cgColor
|
layer?.borderColor = color.withAlphaComponent(isKeyboardFocused ? 0.72 : 0.38).cgColor
|
||||||
} else if isKeyboardFocused {
|
} else if isKeyboardFocused {
|
||||||
layer?.backgroundColor = NSColor.windowBackgroundColor.withAlphaComponent(0.34).cgColor
|
layer?.backgroundColor = color.withAlphaComponent(0.09).cgColor
|
||||||
layer?.borderColor = NSColor.controlAccentColor.withAlphaComponent(0.52).cgColor
|
layer?.borderColor = color.withAlphaComponent(0.44).cgColor
|
||||||
} else {
|
} else {
|
||||||
layer?.backgroundColor = NSColor.clear.cgColor
|
layer?.backgroundColor = NSColor.clear.cgColor
|
||||||
layer?.borderColor = NSColor.clear.cgColor
|
layer?.borderColor = NSColor.clear.cgColor
|
||||||
@@ -2319,6 +2354,10 @@ private final class CollectionChipView: NSView {
|
|||||||
countLabel.isHidden
|
countLabel.isHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var debugLeadingSymbolName: String {
|
||||||
|
symbolName ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
func debugDropItem(_ itemID: UUID) {
|
func debugDropItem(_ itemID: UUID) {
|
||||||
onDropItem?(itemID)
|
onDropItem?(itemID)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,6 +229,10 @@ final class ClipboardPanelViewTests: XCTestCase {
|
|||||||
fixture.view.debugCollectionTitles,
|
fixture.view.debugCollectionTitles,
|
||||||
["Clipboard", "Frequent", "Text", "Links", "Images", "Audio", "Files", "Pinned"]
|
["Clipboard", "Frequent", "Text", "Links", "Images", "Audio", "Files", "Pinned"]
|
||||||
)
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
fixture.view.debugCollectionLeadingSymbols,
|
||||||
|
["doc.on.clipboard", "chart.bar.fill", "text.alignleft", "link", "photo", "music.note", "doc.fill", "pin.fill"]
|
||||||
|
)
|
||||||
XCTAssertEqual(fixture.view.debugSelectedCollectionTitle, "Clipboard")
|
XCTAssertEqual(fixture.view.debugSelectedCollectionTitle, "Clipboard")
|
||||||
|
|
||||||
fixture.viewModel.sortMode = .links
|
fixture.viewModel.sortMode = .links
|
||||||
|
|||||||
Reference in New Issue
Block a user