Merge pull request #505 from manaflow-ai/feat-sentry-ga-jg-drop-hint-cache
Cache sidebar drop type + shortcut hint metrics
This commit is contained in:
commit
68a1952dc4
2 changed files with 104 additions and 13 deletions
|
|
@ -5733,7 +5733,7 @@ private struct SidebarExternalDropOverlay: View {
|
|||
.contentShape(Rectangle())
|
||||
.allowsHitTesting(true)
|
||||
.onDrop(
|
||||
of: [SidebarTabDragPayload.typeIdentifier],
|
||||
of: SidebarTabDragPayload.dropContentTypes,
|
||||
delegate: SidebarExternalDropDelegate(draggedTabId: draggedTabId)
|
||||
)
|
||||
} else {
|
||||
|
|
@ -6068,7 +6068,7 @@ private struct SidebarEmptyArea: View {
|
|||
}
|
||||
selection = .tabs
|
||||
}
|
||||
.onDrop(of: [SidebarTabDragPayload.typeIdentifier], delegate: SidebarTabDropDelegate(
|
||||
.onDrop(of: SidebarTabDragPayload.dropContentTypes, delegate: SidebarTabDropDelegate(
|
||||
targetTabId: nil,
|
||||
tabManager: tabManager,
|
||||
draggedTabId: $draggedTabId,
|
||||
|
|
@ -6118,6 +6118,59 @@ enum SidebarPathFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
enum SidebarWorkspaceShortcutHintMetrics {
|
||||
private static let measurementFont = NSFont.systemFont(ofSize: 10, weight: .semibold)
|
||||
private static let minimumSlotWidth: CGFloat = 28
|
||||
private static let horizontalPadding: CGFloat = 12
|
||||
private static let lock = NSLock()
|
||||
private static var cachedHintWidths: [String: CGFloat] = [:]
|
||||
#if DEBUG
|
||||
private static var measurementCount = 0
|
||||
#endif
|
||||
|
||||
static func slotWidth(label: String?, debugXOffset: Double) -> CGFloat {
|
||||
guard let label else { return minimumSlotWidth }
|
||||
let positiveDebugInset = max(0, CGFloat(ShortcutHintDebugSettings.clamped(debugXOffset))) + 2
|
||||
return max(minimumSlotWidth, hintWidth(for: label) + positiveDebugInset)
|
||||
}
|
||||
|
||||
static func hintWidth(for label: String) -> CGFloat {
|
||||
lock.lock()
|
||||
if let cached = cachedHintWidths[label] {
|
||||
lock.unlock()
|
||||
return cached
|
||||
}
|
||||
lock.unlock()
|
||||
|
||||
let textWidth = (label as NSString).size(withAttributes: [.font: measurementFont]).width
|
||||
let measuredWidth = ceil(textWidth) + horizontalPadding
|
||||
|
||||
lock.lock()
|
||||
cachedHintWidths[label] = measuredWidth
|
||||
#if DEBUG
|
||||
measurementCount += 1
|
||||
#endif
|
||||
lock.unlock()
|
||||
return measuredWidth
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
static func resetCacheForTesting() {
|
||||
lock.lock()
|
||||
cachedHintWidths.removeAll()
|
||||
measurementCount = 0
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
static func measurementCountForTesting() -> Int {
|
||||
lock.lock()
|
||||
let count = measurementCount
|
||||
lock.unlock()
|
||||
return count
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private struct TabItemView: View {
|
||||
@EnvironmentObject var tabManager: TabManager
|
||||
@EnvironmentObject var notificationStore: TerminalNotificationStore
|
||||
|
|
@ -6244,15 +6297,10 @@ private struct TabItemView: View {
|
|||
}
|
||||
|
||||
private var workspaceHintSlotWidth: CGFloat {
|
||||
guard let label = workspaceShortcutLabel else { return 28 }
|
||||
let positiveDebugInset = max(0, CGFloat(ShortcutHintDebugSettings.clamped(sidebarShortcutHintXOffset))) + 2
|
||||
return max(28, workspaceHintWidth(for: label) + positiveDebugInset)
|
||||
}
|
||||
|
||||
private func workspaceHintWidth(for label: String) -> CGFloat {
|
||||
let font = NSFont.systemFont(ofSize: 10, weight: .semibold)
|
||||
let textWidth = (label as NSString).size(withAttributes: [.font: font]).width
|
||||
return ceil(textWidth) + 12
|
||||
SidebarWorkspaceShortcutHintMetrics.slotWidth(
|
||||
label: workspaceShortcutLabel,
|
||||
debugXOffset: sidebarShortcutHintXOffset
|
||||
)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
|
@ -6550,7 +6598,7 @@ private struct TabItemView: View {
|
|||
dropIndicator = nil
|
||||
return SidebarTabDragPayload.provider(for: tab.id)
|
||||
}
|
||||
.onDrop(of: [SidebarTabDragPayload.typeIdentifier], delegate: SidebarTabDropDelegate(
|
||||
.onDrop(of: SidebarTabDragPayload.dropContentTypes, delegate: SidebarTabDropDelegate(
|
||||
targetTabId: tab.id,
|
||||
tabManager: tabManager,
|
||||
draggedTabId: $draggedTabId,
|
||||
|
|
@ -6560,7 +6608,7 @@ private struct TabItemView: View {
|
|||
dragAutoScrollController: dragAutoScrollController,
|
||||
dropIndicator: $dropIndicator
|
||||
))
|
||||
.onDrop(of: [BonsplitTabDragPayload.typeIdentifier], delegate: SidebarBonsplitTabDropDelegate(
|
||||
.onDrop(of: BonsplitTabDragPayload.dropContentTypes, delegate: SidebarBonsplitTabDropDelegate(
|
||||
targetWorkspaceId: tab.id,
|
||||
tabManager: tabManager,
|
||||
selectedTabIds: $selectedTabIds,
|
||||
|
|
@ -7789,6 +7837,8 @@ private final class SidebarDragAutoScrollController: ObservableObject {
|
|||
|
||||
private enum SidebarTabDragPayload {
|
||||
static let typeIdentifier = "com.cmux.sidebar-tab-reorder"
|
||||
static let dropContentType = UTType(exportedAs: typeIdentifier)
|
||||
static let dropContentTypes: [UTType] = [dropContentType]
|
||||
private static let prefix = "cmux.sidebar-tab."
|
||||
|
||||
static func provider(for tabId: UUID) -> NSItemProvider {
|
||||
|
|
@ -7804,6 +7854,8 @@ private enum SidebarTabDragPayload {
|
|||
|
||||
private enum BonsplitTabDragPayload {
|
||||
static let typeIdentifier = "com.splittabbar.tabtransfer"
|
||||
static let dropContentType = UTType(exportedAs: typeIdentifier)
|
||||
static let dropContentTypes: [UTType] = [dropContentType]
|
||||
private static let currentProcessId = Int32(ProcessInfo.processInfo.processIdentifier)
|
||||
|
||||
struct Transfer: Decodable {
|
||||
|
|
|
|||
|
|
@ -6081,6 +6081,45 @@ final class WindowDragHandleHitTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@MainActor
|
||||
final class SidebarWorkspaceShortcutHintMetricsTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
SidebarWorkspaceShortcutHintMetrics.resetCacheForTesting()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
SidebarWorkspaceShortcutHintMetrics.resetCacheForTesting()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testHintWidthCachesRepeatedMeasurements() {
|
||||
XCTAssertEqual(SidebarWorkspaceShortcutHintMetrics.measurementCountForTesting(), 0)
|
||||
|
||||
let first = SidebarWorkspaceShortcutHintMetrics.hintWidth(for: "⌘1")
|
||||
XCTAssertGreaterThan(first, 0)
|
||||
XCTAssertEqual(SidebarWorkspaceShortcutHintMetrics.measurementCountForTesting(), 1)
|
||||
|
||||
let second = SidebarWorkspaceShortcutHintMetrics.hintWidth(for: "⌘1")
|
||||
XCTAssertEqual(second, first)
|
||||
XCTAssertEqual(SidebarWorkspaceShortcutHintMetrics.measurementCountForTesting(), 1)
|
||||
|
||||
_ = SidebarWorkspaceShortcutHintMetrics.hintWidth(for: "⌘2")
|
||||
XCTAssertEqual(SidebarWorkspaceShortcutHintMetrics.measurementCountForTesting(), 2)
|
||||
}
|
||||
|
||||
func testSlotWidthAppliesMinimumAndDebugInset() {
|
||||
let nilLabelWidth = SidebarWorkspaceShortcutHintMetrics.slotWidth(label: nil, debugXOffset: 999)
|
||||
XCTAssertEqual(nilLabelWidth, 28)
|
||||
|
||||
let base = SidebarWorkspaceShortcutHintMetrics.slotWidth(label: "⌘1", debugXOffset: 0)
|
||||
let widened = SidebarWorkspaceShortcutHintMetrics.slotWidth(label: "⌘1", debugXOffset: 10)
|
||||
XCTAssertGreaterThan(widened, base)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@MainActor
|
||||
final class DraggableFolderHitTests: XCTestCase {
|
||||
func testFolderHitTestReturnsContainerWhenInsideBounds() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue