diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 698df576a5fc..9bf4d595f8f1 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -203,6 +203,22 @@ struct SidebarWorkspaceRowBackgroundStyle { static let clear = Self(color: nil, opacity: 0) } +func sidebarWorkspaceRowExplicitRailNSColor( + activeTabIndicatorStyle: SidebarActiveTabIndicatorStyle, + customColorHex: String?, + colorScheme: ColorScheme +) -> NSColor? { + guard activeTabIndicatorStyle == .leftRail, + let customColorHex else { + return nil + } + return WorkspaceTabColorSettings.displayNSColor( + hex: customColorHex, + colorScheme: colorScheme, + forceBright: true + ) +} + func sidebarWorkspaceRowBackgroundStyle( activeTabIndicatorStyle: SidebarActiveTabIndicatorStyle, isActive: Bool, @@ -223,24 +239,15 @@ func sidebarWorkspaceRowBackgroundStyle( forceBright: activeTabIndicatorStyle == .leftRail ) } - let selectedCustomBackground = customColorHex.flatMap { - sidebarSelectedWorkspaceCustomBackgroundNSColor(hex: $0, colorScheme: colorScheme) - } switch activeTabIndicatorStyle { case .leftRail: if isActive { return SidebarWorkspaceRowBackgroundStyle( - color: selectedCustomBackground ?? selectedBackground, + color: selectedBackground, opacity: 1 ) } - if let customBackground { - return SidebarWorkspaceRowBackgroundStyle( - color: customBackground, - opacity: isMultiSelected ? 0.35 : 0.7 - ) - } if isMultiSelected { return SidebarWorkspaceRowBackgroundStyle(color: accentBackground, opacity: 0.25) } @@ -249,7 +256,7 @@ func sidebarWorkspaceRowBackgroundStyle( case .solidFill: if isActive { return SidebarWorkspaceRowBackgroundStyle( - color: selectedCustomBackground ?? selectedBackground, + color: selectedBackground, opacity: 1 ) } @@ -13073,6 +13080,10 @@ private struct TabItemView: View, Equatable { .semibold } + private var showsLeadingRail: Bool { + explicitRailColor != nil + } + private var activeBorderLineWidth: CGFloat { switch activeTabIndicatorStyle { case .leftRail: @@ -13501,6 +13512,16 @@ private struct TabItemView: View, Equatable { RoundedRectangle(cornerRadius: 6) .strokeBorder(activeBorderColor, lineWidth: activeBorderLineWidth) } + .overlay(alignment: .leading) { + if showsLeadingRail { + Capsule(style: .continuous) + .fill(railColor) + .frame(width: 3) + .padding(.leading, 4) + .padding(.vertical, 5) + .offset(x: -1) + } + } ) .padding(.horizontal, 6) .background { @@ -13943,6 +13964,21 @@ private struct TabItemView: View, Equatable { return Color(nsColor: color).opacity(style.opacity) } + private var railColor: Color { + explicitRailColor ?? .clear + } + + private var explicitRailColor: Color? { + guard let railColor = sidebarWorkspaceRowExplicitRailNSColor( + activeTabIndicatorStyle: activeTabIndicatorStyle, + customColorHex: workspaceSnapshot.customColorHex, + colorScheme: colorScheme + ) else { + return nil + } + return Color(nsColor: railColor).opacity(0.95) + } + private func tabColorSwatchColor(for hex: String) -> NSColor { WorkspaceTabColorSettings.displayNSColor( hex: hex, diff --git a/cmuxTests/WorkspaceUnitTests.swift b/cmuxTests/WorkspaceUnitTests.swift index a4e36a7613a6..f6c3708f4f0c 100644 --- a/cmuxTests/WorkspaceUnitTests.swift +++ b/cmuxTests/WorkspaceUnitTests.swift @@ -61,7 +61,7 @@ final class SidebarSelectedWorkspaceColorTests: XCTestCase { } @MainActor - func testSetTabColorFeedsSelectedSolidFillSidebarBackground() { + func testSolidFillKeepsSelectedBackgroundForActiveCustomColoredWorkspaceRow() { let manager = TabManager() guard let workspace = manager.tabs.first else { XCTFail("Expected TabManager to initialise with a workspace") @@ -87,13 +87,16 @@ final class SidebarSelectedWorkspaceColorTests: XCTestCase { sidebarSelectionColorHex: nil ) - XCTAssertEqual(background.color?.hexString(), "#C0392B") + XCTAssertEqual( + background.color?.hexString(), + sidebarSelectedWorkspaceBackgroundNSColor(for: .light).hexString() + ) XCTAssertEqual(background.opacity, 1.0, accuracy: 0.001) withExtendedLifetime(cancellable) {} } @MainActor - func testSetTabColorFeedsSelectedDefaultSidebarBackground() { + func testLeftRailKeepsSelectedBackgroundForActiveCustomColoredWorkspaceRow() { let manager = TabManager() guard let workspace = manager.tabs.first else { XCTFail("Expected TabManager to initialise with a workspace") @@ -119,10 +122,79 @@ final class SidebarSelectedWorkspaceColorTests: XCTestCase { sidebarSelectionColorHex: nil ) - XCTAssertEqual(background.color?.hexString(), "#C0392B") + XCTAssertEqual( + background.color?.hexString(), + sidebarSelectedWorkspaceBackgroundNSColor(for: .light).hexString() + ) XCTAssertEqual(background.opacity, 1.0, accuracy: 0.001) withExtendedLifetime(cancellable) {} } + + @MainActor + func testLeftRailLeavesInactiveCustomColoredWorkspaceRowTransparent() { + let manager = TabManager() + guard let workspace = manager.tabs.first else { + XCTFail("Expected TabManager to initialise with a workspace") + return + } + + manager.setTabColor(tabId: workspace.id, color: "#C0392B") + + let background = sidebarWorkspaceRowBackgroundStyle( + activeTabIndicatorStyle: .leftRail, + isActive: false, + isMultiSelected: false, + customColorHex: workspace.customColor, + colorScheme: .light, + sidebarSelectionColorHex: nil + ) + + XCTAssertNil(background.color) + XCTAssertEqual(background.opacity, 0, accuracy: 0.001) + } + + @MainActor + func testLeftRailResolvesExplicitRailColorForCustomColoredWorkspaceRow() { + let manager = TabManager() + guard let workspace = manager.tabs.first else { + XCTFail("Expected TabManager to initialise with a workspace") + return + } + + manager.setTabColor(tabId: workspace.id, color: "#C0392B") + + let railColor = sidebarWorkspaceRowExplicitRailNSColor( + activeTabIndicatorStyle: .leftRail, + customColorHex: workspace.customColor, + colorScheme: .light + ) + + XCTAssertNotNil(railColor) + XCTAssertEqual(railColor?.hexString(), "#C0392B") + } + + @MainActor + func testSolidFillUsesInactiveCustomWorkspaceColorAsBackground() { + let manager = TabManager() + guard let workspace = manager.tabs.first else { + XCTFail("Expected TabManager to initialise with a workspace") + return + } + + manager.setTabColor(tabId: workspace.id, color: "#C0392B") + + let background = sidebarWorkspaceRowBackgroundStyle( + activeTabIndicatorStyle: .solidFill, + isActive: false, + isMultiSelected: false, + customColorHex: workspace.customColor, + colorScheme: .light, + sidebarSelectionColorHex: nil + ) + + XCTAssertEqual(background.color?.hexString(), "#C0392B") + XCTAssertEqual(background.opacity, 0.7, accuracy: 0.001) + } }