From 70a1cc2e63d2c49a135bd89cec3ffb1d427d90ee Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 30 Jun 2026 16:21:47 +0800
Subject: [PATCH] Freeze prep TagLauncher 8.2.9 Pro status UI
---
src/Apptag/AppGridCollectionView.swift | 97 ++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 88 insertions(+), 9 deletions(-)
diff --git a/src/Apptag/AppGridCollectionView.swift b/src/Apptag/AppGridCollectionView.swift
index 13fe190..ba2b864 100644
--- a/src/Apptag/AppGridCollectionView.swift
+++ b/src/Apptag/AppGridCollectionView.swift
@@ -48,6 +48,7 @@
struct AppGridCollectionView: NSViewRepresentable {
let groups: [TagGroup]
let tagColors: [String: Int]
+ let tagCustomColors: [String: TagCustomColor]
let displayMode: String
let iconSize: CGFloat
let showNames: Bool
@@ -89,6 +90,7 @@
context.coordinator.update(
groups: groups,
tagColors: tagColors,
+ tagCustomColors: tagCustomColors,
displayMode: displayMode,
iconSize: iconSize,
showNames: showNames,
@@ -122,6 +124,7 @@
final class Coordinator: NSObject, NSCollectionViewDataSource, NSCollectionViewDelegate {
var groups: [TagGroup] = []
var tagColors: [String: Int] = [:]
+ var tagCustomColors: [String: TagCustomColor] = [:]
var displayMode = AppDefaults.displayMode
var iconSize: CGFloat = AppDefaults.iconSize
var showNames = true
@@ -164,6 +167,7 @@
func update(
groups: [TagGroup],
tagColors: [String: Int],
+ tagCustomColors: [String: TagCustomColor],
displayMode: String,
iconSize: CGFloat,
showNames: Bool,
@@ -193,6 +197,7 @@
) {
self.groups = groups
self.tagColors = tagColors
+ self.tagCustomColors = tagCustomColors
self.displayMode = displayMode
self.iconSize = iconSize
self.showNames = showNames
@@ -225,6 +230,7 @@
let nextSignature = Self.signature(
tagColors: tagColors,
+ tagCustomColors: tagCustomColors,
displayMode: displayMode,
iconSize: iconSize,
showNames: showNames,
@@ -401,6 +407,7 @@
private static func signature(
tagColors: [String: Int],
+ tagCustomColors: [String: TagCustomColor],
displayMode: String,
iconSize: CGFloat,
showNames: Bool,
@@ -413,6 +420,10 @@
.sorted { $0.key < $1.key }
.map { "\($0.key)=\($0.value)" }
.joined(separator: ",")
+ let customColorPart = tagCustomColors
+ .sorted { $0.key < $1.key }
+ .map { "\($0.key)=\($0.value.signature)" }
+ .joined(separator: ",")
return [
displayMode,
"\(Int(iconSize.rounded()))",
@@ -421,8 +432,16 @@
showUncommonAppBubbles ? "uncommon" : "allbubbles",
"bottom=\(Int(bottomContentPadding.rounded()))",
colorPart,
+ customColorPart,
"rev=\(contentRevision)"
].joined(separator: "|")
+ }
+
+ func tagColor(for groupName: String) -> NSColor {
+ TagColor.nsColor(
+ for: tagColors[groupName] ?? 0,
+ customColor: tagCustomColors[groupName]
+ )
}
private static func clampedUsageTipIndex(_ index: Int, tips: [AppGridUsageTip]) -> Int {
@@ -562,6 +581,17 @@
DispatchQueue.main.async {
coordinator.onDropOutsideGroup(path, source, copy)
}
+ }
+
+ func canPreviewEmptyDrop(path: String, source: String, screenPoint: NSPoint, copy: Bool) -> Bool {
+ guard let coordinator,
+ coordinator.displayStyle != .flat
+ else { return false }
+ return !coordinator.shouldCancelEmptyDropForActiveReorder(
+ path: path,
+ screenPoint: screenPoint,
+ copy: copy
+ )
}
private func setup() {
@@ -1134,7 +1164,7 @@
detailScrollView.borderType = .noBorder
detailScrollView.documentView = detailLabel
- closeButton.action = { [weak self] in self?.hideUsageTips() }
+ closeButton.action = { [weak self] in self?.requestHideUsageTips() }
previousButton.action = { [weak self] in self?.selectUsageTip(offset: -1) }
nextButton.action = { [weak self] in self?.selectUsageTip(offset: 1) }
@@ -1194,10 +1224,9 @@
applyCoordinatorState()
}
- private func hideUsageTips() {
+ private func requestHideUsageTips() {
Diagnostics.log("usageTips.hud.close")
clearHoverState()
- isHidden = true
coordinator?.onHideUsageTips()
}
@@ -1206,7 +1235,7 @@
let closeHitOutset: CGFloat = 8
if closeButton.frame.insetBy(dx: -closeHitOutset, dy: -closeHitOutset).contains(point) {
closeButton.performPressFeedback()
- hideUsageTips()
+ requestHideUsageTips()
return true
}
let hitOutset: CGFloat = 6
@@ -1767,7 +1796,7 @@
private struct LayoutItem {
let index: Int
- let frame: NSRect
+ var frame: NSRect
}
private struct LayoutPlan {
@@ -1903,8 +1932,20 @@
let columnWidth = floor((availableWidth - gap * CGFloat(columnCount - 1)) / CGFloat(columnCount))
var columnHeights = Array(repeating: outerPadding, count: columnCount)
var items: [LayoutItem] = []
+ var lastItemIndexForColumn = Array<Int?>(repeating: nil, count: columnCount)
+ let bottomPinnedGroups = groups.enumerated()
+ .filter { bottomPinnedPriority(for: $0.element.containerID) != nil }
+ .sorted {
+ let lhsPriority = bottomPinnedPriority(for: $0.element.containerID) ?? Int.max
+ let rhsPriority = bottomPinnedPriority(for: $1.element.containerID) ?? Int.max
+ if lhsPriority != rhsPriority { return lhsPriority < rhsPriority }
+ return $0.offset < $1.offset
+ }
+ let bottomPinnedIndexes = Set(bottomPinnedGroups.map { $0.offset })
for (index, group) in groups.enumerated() {
+ if bottomPinnedIndexes.contains(index) { continue }
+
let columnIndex = columnHeights.indices.min { columnHeights[$0] < columnHeights[$1] } ?? 0
let x = outerPadding + CGFloat(columnIndex) * (columnWidth + gap)
let y = columnHeights[columnIndex]
@@ -1919,16 +1960,54 @@
frame: NSRect(x: x, y: y, width: columnWidth, height: height)
)
)
+ lastItemIndexForColumn[columnIndex] = items.count - 1
columnHeights[columnIndex] = y + height + gap
}
- let tallest = columnHeights.max() ?? outerPadding
- let contentHeight = (groups.isEmpty ? outerPadding * 2 : tallest - gap + outerPadding)
+ var contentBottom = columnHeights.max() ?? outerPadding
+ if !bottomPinnedGroups.isEmpty {
+ let alignedBottom = max(outerPadding, contentBottom - gap)
+ for itemIndex in lastItemIndexForColumn.compactMap({ $0 }) {
+ let currentFrame = items[itemIndex].frame
+ let alignedHeight = alignedBottom - currentFrame.minY
+ if alignedHeight > currentFrame.height {
+ items[itemIndex].frame.size.height = alignedHeight
+ }
+ }
+ }
+ for (index, group) in bottomPinnedGroups {
+ let y = max(outerPadding, contentBottom)
+ let height = AppGridCollectionMetrics.cardHeight(
+ appCount: group.apps.count,
+ width: availableWidth,
+ iconSize: iconSize
+ )
+ items.append(
+ LayoutItem(
+ index: index,
+ frame: NSRect(x: outerPadding, y: y, width: availableWidth, height: height)
+ )
+ )
+ contentBottom = y + height + gap
+ }
+
+ let contentHeight = (items.isEmpty ? outerPadding * 2 : contentBottom - gap + outerPadding)
+ max(0, bottomContentPadding)
return LayoutPlan(
items: items,
contentSize: NSSize(width: boundedContentWidth, height: max(1, contentHeight))
)
+ }
+
+ private static func bottomPinnedPriority(for containerID: String) -> Int? {
+ switch containerID {
+ case AppContainerID.uncategorized:
+ return 0
+ case AppContainerID.appleBuiltIn:
+ return 1
+ default:
+ return nil
+ }
}
private struct RowItem {
@@ -2265,7 +2344,7 @@
override func draw(_ dirtyRect: NSRect) {
guard let coordinator, let group else { return }
let displayStyle = coordinator.displayStyle
- let tagColor = TagColor.nsColor(for: coordinator.tagColors[group.name] ?? 0)
+ let tagColor = coordinator.tagColor(for: group.name)
let isNavigationHighlighted = coordinator.highlightedGroupName == group.name
let isColorlessActive = coordinator.isColorlessContainerMode
&& (isHovered || isNavigationHighlighted)
@@ -2511,7 +2590,7 @@
let shouldShadow = coordinator.displayStyle.usesCardSurface
&& ((coordinator.isColoredContainerMode && (isHovered || isNavigationHighlighted)) || isColorlessActive)
if coordinator.appGridTheme.usesDarkGlass && coordinator.displayStyle.usesCardSurface {
- let tagColor = TagColor.nsColor(for: coordinator.tagColors[group?.name ?? ""] ?? 0)
+ let tagColor = coordinator.tagColor(for: group?.name ?? "")
let active = isHovered || isNavigationHighlighted
layer?.shadowColor = active
? tagColor.withAlphaComponent(0.82).cgColor
--
Gitblit v1.9.3