diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 9bbe7eadb1..67ab88a43a 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -82,6 +82,7 @@ D7E201B227E8D50000CB86D0 /* FindNavigatorModeSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B127E8D50000CB86D0 /* FindNavigatorModeSelector.swift */; }; D7E201BD27EA00E200CB86D0 /* FindNavigatorResultFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201BC27EA00E200CB86D0 /* FindNavigatorResultFileItem.swift */; }; D7F72DEB27EA3574000C3064 /* Search in Frameworks */ = {isa = PBXBuildFile; productRef = D7F72DEA27EA3574000C3064 /* Search */; }; + DE6F77872813625500D00A76 /* TabBarDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6F77862813625500D00A76 /* TabBarDivider.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -175,6 +176,7 @@ D7E201B127E8D50000CB86D0 /* FindNavigatorModeSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindNavigatorModeSelector.swift; sourceTree = ""; }; D7E201B327E9989900CB86D0 /* FindNavigatorResultList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindNavigatorResultList.swift; sourceTree = ""; }; D7E201BC27EA00E200CB86D0 /* FindNavigatorResultFileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindNavigatorResultFileItem.swift; sourceTree = ""; }; + DE6F77862813625500D00A76 /* TabBarDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarDivider.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -347,6 +349,7 @@ children = ( 287776E827E34BC700D46668 /* TabBar.swift */, 287776EE27E3515300D46668 /* TabBarItem.swift */, + DE6F77862813625500D00A76 /* TabBarDivider.swift */, ); path = TabBar; sourceTree = ""; @@ -703,6 +706,7 @@ 20EBB507280C32D300F3A5DA /* QuickHelpInspector.swift in Sources */, 04C3255C2801F86900C8DA2D /* OutlineMenu.swift in Sources */, 04540D5E27DD08C300E91B77 /* WorkspaceView.swift in Sources */, + DE6F77872813625500D00A76 /* TabBarDivider.swift in Sources */, D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */, 20D839AE280E0CA700B27357 /* PopoverView.swift in Sources */, 2072FA1E280D891500C7F8D4 /* Location.swift in Sources */, diff --git a/CodeEdit/Documents/WorkspaceCodeFileView.swift b/CodeEdit/Documents/WorkspaceCodeFileView.swift index 64644b4790..daef72f07b 100644 --- a/CodeEdit/Documents/WorkspaceCodeFileView.swift +++ b/CodeEdit/Documents/WorkspaceCodeFileView.swift @@ -30,6 +30,7 @@ struct WorkspaceCodeFileView: View { TabBar(windowController: windowController, workspace: workspace) TabBarBottomDivider() BreadcrumbsView(file: item, tappedOpenFile: workspace.openFile(item:)) + Divider() } } } else { diff --git a/CodeEdit/TabBar/TabBar.swift b/CodeEdit/TabBar/TabBar.swift index 78c5d05a2f..a2704b7f5f 100644 --- a/CodeEdit/TabBar/TabBar.swift +++ b/CodeEdit/TabBar/TabBar.swift @@ -14,6 +14,9 @@ struct TabBar: View { @Environment(\.colorScheme) var colorScheme + @Environment(\.controlActiveState) + private var activeState + var windowController: NSWindowController @ObservedObject @@ -47,6 +50,7 @@ struct TabBar: View { .buttonStyle(.plain) } .padding(.horizontal, 11) + .opacity(activeState != .inactive ? 1.0 : 0.5) // Tab bar items. ScrollView(.horizontal, showsIndicators: false) { ScrollViewReader { value in @@ -67,25 +71,21 @@ struct TabBar: View { // TODO: Tab bar tools (e.g. split view). } .frame(height: tabBarHeight) - .overlay { + .overlay(alignment: .top) { // When tab bar style is `xcode`, we put the top divider as an overlay. if prefs.preferences.general.tabBarStyle == .xcode { TabBarTopDivider() - .frame(height: tabBarHeight, alignment: .top) } } .background { if prefs.preferences.general.tabBarStyle == .xcode { Color(nsColor: .controlBackgroundColor) } else { - ZStack { + ZStack(alignment: .top) { Color(nsColor: .black) - .opacity(colorScheme == .dark ? 0.50 : 0.05) - // Set padding top to 1 to avoid color-overlapping. - .padding(.top, 1) + .opacity(colorScheme == .dark ? 0.45 : 0.05) // When tab bar style is `native`, we put the top divider beneath tabs. TabBarTopDivider() - .frame(height: tabBarHeight, alignment: .top) } } } diff --git a/CodeEdit/TabBar/TabBarDivider.swift b/CodeEdit/TabBar/TabBarDivider.swift new file mode 100644 index 0000000000..eb97ce79a4 --- /dev/null +++ b/CodeEdit/TabBar/TabBarDivider.swift @@ -0,0 +1,104 @@ +// +// TabBarDivider.swift +// CodeEdit +// +// Created by Lingxi Li on 4/22/22. +// + +import SwiftUI +import AppPreferences +import CodeEditUI + +/// The vertical divider between tab bar items. +struct TabDivider: View { + @Environment(\.colorScheme) + var colorScheme + + @StateObject + private var prefs: AppPreferencesModel = .shared + + let width: CGFloat = 1 + + var body: some View { + Rectangle() + .frame(width: width) + .padding(.vertical, prefs.preferences.general.tabBarStyle == .xcode ? 8 : 0) + .foregroundColor( + prefs.preferences.general.tabBarStyle == .xcode + ? Color(nsColor: colorScheme == .dark ? .white : .black) + .opacity(0.12) + : Color(nsColor: colorScheme == .dark ? .white : .black) + .opacity(colorScheme == .dark ? 0.08 : 0.12) + ) + } +} + +/// The top border for tab bar (between tab bar and titlebar). +struct TabBarTopDivider: View { + @Environment(\.colorScheme) + var colorScheme + + @StateObject + private var prefs: AppPreferencesModel = .shared + + var body: some View { + ZStack(alignment: .top) { + Color(nsColor: .black) + .opacity( + prefs.preferences.general.tabBarStyle == .xcode + ? (colorScheme == .dark ? 0.29 : 0.11) + : (colorScheme == .dark ? 0.80 : 0.02) + ) + .frame(height: prefs.preferences.general.tabBarStyle == .xcode ? 1.0 : 0.8) + // Shadow of top divider in native style. + if prefs.preferences.general.tabBarStyle == .native { + TabBarNativeShadow() + } + } + } +} + +/// The bottom border for tab bar (between tab bar and breadcrumbs). +struct TabBarBottomDivider: View { + @Environment(\.colorScheme) + var colorScheme + + @StateObject + private var prefs: AppPreferencesModel = .shared + + var body: some View { + Rectangle() + .foregroundColor( + prefs.preferences.general.tabBarStyle == .xcode + ? Color(nsColor: .separatorColor) + .opacity(colorScheme == .dark ? 0.40 : 0.45) + : Color(nsColor: .black) + .opacity(colorScheme == .dark ? 0.65 : 0.09) + + ) + .frame(height: prefs.preferences.general.tabBarStyle == .xcode ? 1.0 : 0.8) + } +} + +/// The divider shadow for native tab bar style. +/// +/// This is generally used in the top divider of tab bar when tab bar style is set to `native`. +struct TabBarNativeShadow: View { + let shadowColor = Color(nsColor: .shadowColor) + + var body: some View { + LinearGradient( + colors: [ + shadowColor.opacity(0.18), + shadowColor.opacity(0.06), + shadowColor.opacity(0.03), + shadowColor.opacity(0.01), + shadowColor.opacity(0) + ], + startPoint: .top, + endPoint: .bottom + ) + .frame(height: 3.8) + .opacity(0.70) + } +} diff --git a/CodeEdit/TabBar/TabBarItem.swift b/CodeEdit/TabBar/TabBarItem.swift index b605e1c262..3b7ead065d 100644 --- a/CodeEdit/TabBar/TabBarItem.swift +++ b/CodeEdit/TabBar/TabBarItem.swift @@ -10,78 +10,13 @@ import WorkspaceClient import AppPreferences import CodeEditUI -/// The vertical divider between tab bar items. -struct TabDivider: View { - @Environment(\.colorScheme) - var colorScheme - - @StateObject - private var prefs: AppPreferencesModel = .shared - - let width: CGFloat = 1 - - var body: some View { - Rectangle() - .frame(width: width) - .padding(.vertical, prefs.preferences.general.tabBarStyle == .xcode ? 8 : 0) - .foregroundColor( - prefs.preferences.general.tabBarStyle == .xcode - ? Color(nsColor: colorScheme == .dark ? .white : .black) - .opacity(0.12) - : Color(nsColor: colorScheme == .dark ? .white : .black) - .opacity(colorScheme == .dark ? 0.08 : 0.12) - ) - } -} - -/// The top border for tab bar (between tab bar and titlebar). -struct TabBarTopDivider: View { - @Environment(\.colorScheme) - var colorScheme - - @StateObject - private var prefs: AppPreferencesModel = .shared - - var body: some View { - Rectangle() - .foregroundColor( - Color(nsColor: .black) - .opacity( - prefs.preferences.general.tabBarStyle == .xcode - ? (colorScheme == .dark ? 0.29 : 0.11) - : (colorScheme == .dark ? 0.80 : 0.15) - ) - ) - .frame(height: prefs.preferences.general.tabBarStyle == .xcode ? 1.0 : 0.8) - } -} - -/// The bottom border for tab bar (between tab bar and breadcrumbs). -struct TabBarBottomDivider: View { - @Environment(\.colorScheme) - var colorScheme - - @StateObject - private var prefs: AppPreferencesModel = .shared - - var body: some View { - Rectangle() - .foregroundColor( - prefs.preferences.general.tabBarStyle == .xcode - ? Color(nsColor: .separatorColor) - .opacity(colorScheme == .dark ? 0.40 : 0.45) - : Color(nsColor: .black) - .opacity(colorScheme == .dark ? 0.65 : 0.09) - - ) - .frame(height: prefs.preferences.general.tabBarStyle == .xcode ? 1.0 : 0.8) - } -} - struct TabBarItem: View { @Environment(\.colorScheme) var colorScheme + @Environment(\.controlActiveState) + private var activeState + @StateObject private var prefs: AppPreferencesModel = .shared @@ -132,7 +67,9 @@ struct TabBarItem: View { .resizable() .aspectRatio(contentMode: .fit) .foregroundColor( - prefs.preferences.general.fileIconStyle == .color ? item.iconColor : .secondary + prefs.preferences.general.fileIconStyle == .color && activeState != .inactive + ? item.iconColor + : .secondary ) .frame(width: 12, height: 12) Text(item.url.lastPathComponent) @@ -231,18 +168,27 @@ struct TabBarItem: View { } .frame(maxWidth: .infinity, alignment: .leading) } - .overlay { - // Only show NativeTabShadow when `tabBarStyle` is native and this tab is not active. - if prefs.preferences.general.tabBarStyle == .native && !isActive { - TabBarTopDivider() - .frame(maxHeight: .infinity, alignment: .top) - } - } + .opacity( + // Inactive states for tab bar item content. + activeState != .inactive + ? 1.0 + : ( + isActive + ? (prefs.preferences.general.tabBarStyle == .xcode ? 0.6 : 0.35) + : (prefs.preferences.general.tabBarStyle == .xcode ? 0.4 : 0.55) + ) + ) TabDivider() .opacity( isActive && prefs.preferences.general.tabBarStyle == .xcode ? 0.0 : 1.0 ) } + .overlay(alignment: .top) { + // Only show NativeTabShadow when `tabBarStyle` is native and this tab is not active. + if prefs.preferences.general.tabBarStyle == .native && !isActive { + TabBarTopDivider() + } + } .foregroundColor( isActive ? ( @@ -279,7 +225,11 @@ struct TabBarItem: View { .background { if prefs.preferences.general.tabBarStyle == .xcode { Color(nsColor: isActive ? .selectedControlColor : .clear) - .opacity(colorScheme == .dark ? 0.70 : 0.50) + .opacity( + colorScheme == .dark + ? (activeState != .inactive ? 0.70 : 0.50) + : (activeState != .inactive ? 0.50 : 0.35) + ) .background( // This layer of background is to hide dividers of other tab bar items // because the original background above is translucent (by opacity). @@ -301,7 +251,7 @@ struct TabBarItem: View { ZStack { // Native inactive tab background dim. Color(nsColor: .black) - .opacity(colorScheme == .dark ? 0.50 : 0.05) + .opacity(colorScheme == .dark ? 0.45 : 0.05) // Native inactive tab hover state. Color(nsColor: colorScheme == .dark ? .white : .black) @@ -312,7 +262,6 @@ struct TabBarItem: View { ) .animation(.easeInOut(duration: 0.10), value: isHovering) } - .padding(.top, colorScheme == .dark ? 0 : 1) .padding(.horizontal, 1) } } diff --git a/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsComponent.swift b/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsComponent.swift index 8a1a833603..8478e7ea47 100644 --- a/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsComponent.swift +++ b/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsComponent.swift @@ -10,6 +10,12 @@ import SwiftUI import WorkspaceClient public struct BreadcrumbsComponent: View { + @Environment(\.colorScheme) + var colorScheme + + @Environment(\.controlActiveState) + private var activeState + @StateObject private var prefs: AppPreferencesModel = .shared @State var position: NSPoint? private let fileItem: WorkspaceClient.FileItem @@ -33,23 +39,29 @@ public struct BreadcrumbsComponent: View { private var color: Color { fileItem.parent == nil ? .accentColor - : fileItem.children?.isEmpty ?? true - ? fileItem.iconColor - : .secondary + : ( + fileItem.isFolder + ? Color(hex: colorScheme == .dark ? "#61b6df" :"#27b9ff") + : fileItem.iconColor + ) } public var body: some View { - HStack(alignment: .center) { - HStack { - Image(systemName: image) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 12) - .foregroundStyle(prefs.preferences.general.fileIconStyle == .color ? color : .secondary) - } + HStack(alignment: .center, spacing: 5) { + Image(systemName: image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 12) + .foregroundStyle( + prefs.preferences.general.fileIconStyle == .color + ? color + : .secondary + ) + .opacity(activeState != .inactive ? 1.0 : 0.4) Text(fileItem.fileName) .foregroundStyle(.primary) .font(.system(size: 11)) + .opacity(activeState != .inactive ? 1.0 : 0.2) } /// Get location in window .background(GeometryReader { (proxy: GeometryProxy) -> Color in diff --git a/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsView.swift b/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsView.swift index ec3a5ff876..0ae61e9f0d 100644 --- a/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsView.swift +++ b/CodeEditModules/Modules/Breadcrumbs/src/BreadcrumbsView.swift @@ -9,7 +9,12 @@ import SwiftUI import WorkspaceClient public struct BreadcrumbsView: View { - @Environment(\.colorScheme) private var colorScheme + @Environment(\.colorScheme) + private var colorScheme + + @Environment(\.controlActiveState) + private var activeState + @State private var fileItems: [WorkspaceClient.FileItem] = [] private let file: WorkspaceClient.FileItem @@ -24,25 +29,20 @@ public struct BreadcrumbsView: View { } public var body: some View { - ZStack(alignment: .leading) { - Rectangle() - .foregroundStyle(Color(nsColor: .controlBackgroundColor)) - ScrollView(.horizontal, showsIndicators: false) { - HStack { - ForEach(fileItems, id: \.self) { fileItem in - if fileItem.parent != nil { - chevron - } - BreadcrumbsComponent(fileItem: fileItem, tappedOpenFile: tappedOpenFile) + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 1.5) { + ForEach(fileItems, id: \.self) { fileItem in + if fileItem.parent != nil { + chevron } + BreadcrumbsComponent(fileItem: fileItem, tappedOpenFile: tappedOpenFile) + .padding(.leading, 2.5) } - .padding(.horizontal, 12) } + .padding(.horizontal, 10) } - .frame(height: 29) - .overlay(alignment: .bottom) { - Divider() - } + .frame(height: 28, alignment: .center) + .background(Color(nsColor: .controlBackgroundColor)) .onAppear { fileInfo(self.file) } @@ -53,8 +53,11 @@ public struct BreadcrumbsView: View { private var chevron: some View { Image(systemName: "chevron.compact.right") - .foregroundStyle(.secondary) + .font(.system(size: 14, weight: .thin, design: .default)) + .foregroundStyle(.primary) + .scaleEffect(x: 1.30, y: 1.0, anchor: .center) .imageScale(.large) + .opacity(activeState != .inactive ? 0.8 : 0.5) } private func fileInfo(_ file: WorkspaceClient.FileItem) {