From 8e40f5dd6c9437e75890ed0773859259c543fbcb Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Tue, 5 Mar 2024 02:04:18 -0600 Subject: [PATCH 01/12] Added navigation style setting --- .../Models/NavigationSettings.swift | 8 ++++++++ .../NavigationSettingsView.swift | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift create mode 100644 CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift diff --git a/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift b/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift new file mode 100644 index 000000000..3b19111cd --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift @@ -0,0 +1,8 @@ +// +// NavigationSettings.swift +// CodeEdit +// +// Created by Austin Condiff on 3/4/24. +// + +import Foundation diff --git a/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift b/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift new file mode 100644 index 000000000..0d8ee57ed --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift @@ -0,0 +1,18 @@ +// +// NavigationSettingsView.swift +// CodeEdit +// +// Created by Austin Condiff on 3/4/24. +// + +import SwiftUI + +struct NavigationSettingsView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + NavigationSettingsView() +} From 5061ebe5b55272bc7d47146abe4ef0b2def6a42b Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Tue, 5 Mar 2024 02:04:49 -0600 Subject: [PATCH 02/12] Added navigation style setting --- CodeEdit.xcodeproj/project.pbxproj | 34 ++++++++++++--- .../CodeEditUI/Views/SegmentedControl.swift | 2 +- .../PathBar/Views/EditorPathBarView.swift | 17 +++++++- .../EditorTabBarLeadingAccessories.swift | 6 --- .../Features/Editor/Views/EditorView.swift | 43 +++++++++++++------ .../ProjectNavigatorViewController.swift | 3 +- .../Views/SourceControlNavigatorView.swift | 2 +- .../Settings/Models/SettingsData.swift | 7 ++- .../Models/NavigationSettings.swift | 34 +++++++++++++++ .../NavigationSettingsView.swift | 20 +++++++-- CodeEdit/Features/Settings/SettingsView.swift | 9 ++++ 11 files changed, 145 insertions(+), 32 deletions(-) diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 84e65b4a5..2e6257c76 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -422,6 +422,8 @@ B65B10FE2B08B07D002852CF /* SourceControlNavigatorChangesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B10FD2B08B07D002852CF /* SourceControlNavigatorChangesList.swift */; }; B65B11012B09D5D4002852CF /* GitClient+Pull.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B11002B09D5D4002852CF /* GitClient+Pull.swift */; }; B65B11042B09DB1C002852CF /* GitClient+Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65B11032B09DB1C002852CF /* GitClient+Fetch.swift */; }; + B664C3B02B965F6C00816B4E /* NavigationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B664C3AF2B965F6C00816B4E /* NavigationSettings.swift */; }; + B664C3B32B96634F00816B4E /* NavigationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B664C3B22B96634F00816B4E /* NavigationSettingsView.swift */; }; B66A4E4529C8E86D004573B4 /* CommandsFixes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66A4E4429C8E86D004573B4 /* CommandsFixes.swift */; }; B66A4E4C29C9179B004573B4 /* CodeEditApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66A4E4B29C9179B004573B4 /* CodeEditApp.swift */; }; B66A4E4F29C917B8004573B4 /* WelcomeWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66A4E4E29C917B8004573B4 /* WelcomeWindow.swift */; }; @@ -970,6 +972,8 @@ B65B10FD2B08B07D002852CF /* SourceControlNavigatorChangesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlNavigatorChangesList.swift; sourceTree = ""; }; B65B11002B09D5D4002852CF /* GitClient+Pull.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Pull.swift"; sourceTree = ""; }; B65B11032B09DB1C002852CF /* GitClient+Fetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Fetch.swift"; sourceTree = ""; }; + B664C3AF2B965F6C00816B4E /* NavigationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSettings.swift; sourceTree = ""; }; + B664C3B22B96634F00816B4E /* NavigationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSettingsView.swift; sourceTree = ""; }; B66A4E4429C8E86D004573B4 /* CommandsFixes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandsFixes.swift; sourceTree = ""; }; B66A4E4B29C9179B004573B4 /* CodeEditApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeEditApp.swift; sourceTree = ""; }; B66A4E4E29C917B8004573B4 /* WelcomeWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeWindow.swift; sourceTree = ""; }; @@ -1277,13 +1281,13 @@ 287776EA27E350A100D46668 /* NavigatorArea */ = { isa = PBXGroup; children = ( - 307AC4CB2ABABD9800163376 /* ViewModels */, D7012EE627E757660001E1EF /* FindNavigator */, 581550CB29FBD30400684881 /* OutlineView */, 286471AC27ED52950039369D /* ProjectNavigator */, 201169D52837B29600F92B46 /* SourceControlNavigator */, B67660682AA972D400CD56B0 /* Models */, B67660692AA972DC00CD56B0 /* Views */, + 307AC4CB2ABABD9800163376 /* ViewModels */, ); path = NavigatorArea; sourceTree = ""; @@ -2553,16 +2557,17 @@ B61DA9DD29D929BF00BF4A43 /* Pages */ = { isa = PBXGroup; children = ( + B664C3AD2B965F4500816B4E /* NavigationSettings */, + B61DA9E129D929F900BF4A43 /* GeneralSettings */, + B6E41C6E29DD15540088F9F4 /* AccountsSettings */, + 58F2EAAE292FB2B0004A9BDE /* ThemeSettings */, + B6EA1FF329DA37D3001BF195 /* TextEditingSettings */, 5B698A082B262F8400DE9392 /* SearchSettings */, 6C5BE51A2A3D5419002DA0FC /* FeatureFlags */, B6CF632629E5417C0085880A /* Keybindings */, - B6E41C6E29DD15540088F9F4 /* AccountsSettings */, - B6EA1FF329DA37D3001BF195 /* TextEditingSettings */, B6F0516E29D9E35300D72287 /* LocationsSettings */, B6F0516D29D9E34200D72287 /* SourceControlSettings */, B6F0516C29D9E32700D72287 /* TerminalSettings */, - 58F2EAAE292FB2B0004A9BDE /* ThemeSettings */, - B61DA9E129D929F900BF4A43 /* GeneralSettings */, ); path = Pages; sourceTree = ""; @@ -2663,6 +2668,23 @@ path = "Preview Content"; sourceTree = ""; }; + B664C3AD2B965F4500816B4E /* NavigationSettings */ = { + isa = PBXGroup; + children = ( + B664C3AE2B965F5500816B4E /* Models */, + B664C3B22B96634F00816B4E /* NavigationSettingsView.swift */, + ); + path = NavigationSettings; + sourceTree = ""; + }; + B664C3AE2B965F5500816B4E /* Models */ = { + isa = PBXGroup; + children = ( + B664C3AF2B965F6C00816B4E /* NavigationSettings.swift */, + ); + path = Models; + sourceTree = ""; + }; B66DD19E2B3C0E0C0004FFEC /* History */ = { isa = PBXGroup; children = ( @@ -3291,6 +3313,7 @@ 587B9E9229301D8F00AC7927 /* BitBucketAccount.swift in Sources */, DE513F52281B672D002260B9 /* EditorTabBarAccessory.swift in Sources */, 2813F93927ECC4C300E305E4 /* NavigatorAreaView.swift in Sources */, + B664C3B02B965F6C00816B4E /* NavigationSettings.swift in Sources */, 5B698A0F2B2636A700DE9392 /* SearchSettingsIgnoreGlobPatternItemView.swift in Sources */, 587B9E8A29301D8F00AC7927 /* GitHubIssue.swift in Sources */, EC0870F72A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift in Sources */, @@ -3339,6 +3362,7 @@ 611192042B08CCED00D4459B /* SearchIndexer+ProgressiveSearch.swift in Sources */, 611192022B08CCDC00D4459B /* SearchIndexer+Search.swift in Sources */, 04BA7C272AE2E9F100584E1C /* GitClient+Push.swift in Sources */, + B664C3B32B96634F00816B4E /* NavigationSettingsView.swift in Sources */, 2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */, D7012EE827E757850001E1EF /* FindNavigatorView.swift in Sources */, 58A5DF8029325B5A00D1BD5D /* GitClient.swift in Sources */, diff --git a/CodeEdit/Features/CodeEditUI/Views/SegmentedControl.swift b/CodeEdit/Features/CodeEditUI/Views/SegmentedControl.swift index 05b97e270..0b142e54e 100644 --- a/CodeEdit/Features/CodeEditUI/Views/SegmentedControl.swift +++ b/CodeEdit/Features/CodeEditUI/Views/SegmentedControl.swift @@ -31,7 +31,7 @@ struct SegmentedControl: View { } var body: some View { - HStack(spacing: 8) { + HStack(spacing: 4) { ForEach(options.indices, id: \.self) { index in SegmentedControlItem( label: options[index], diff --git a/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift b/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift index 994de3759..32a5ed72d 100644 --- a/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift +++ b/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift @@ -9,6 +9,7 @@ import SwiftUI struct EditorPathBarView: View { private let file: CEWorkspaceFile? + private let shouldShowTabBar: Bool private let tappedOpenFile: (CEWorkspaceFile) -> Void @Environment(\.colorScheme) @@ -20,13 +21,15 @@ struct EditorPathBarView: View { @Environment(\.controlActiveState) private var activeState - static let height = 27.0 + static let height = 28.0 init( file: CEWorkspaceFile?, + shouldShowTabBar: Bool, tappedOpenFile: @escaping (CEWorkspaceFile) -> Void ) { self.file = file ?? nil + self.shouldShowTabBar = shouldShowTabBar self.tappedOpenFile = tappedOpenFile } @@ -63,7 +66,17 @@ struct EditorPathBarView: View { } } } - .padding(.horizontal, 10) + } + .padding(.horizontal, shouldShowTabBar ? file == nil ? 10 : 4 : 0) + .safeAreaInset(edge: .leading, spacing: 0) { + if !shouldShowTabBar { + EditorTabBarLeadingAccessories() + } + } + .safeAreaInset(edge: .trailing, spacing: 0) { + if !shouldShowTabBar { + EditorTabBarTrailingAccessories() + } } .frame(height: Self.height, alignment: .center) .opacity(activeState == .inactive ? 0.8 : 1.0) diff --git a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift index 6879af8e8..40ddf958c 100644 --- a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift +++ b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift @@ -136,9 +136,3 @@ struct EditorTabBarLeadingAccessories: View { } } } - -struct TabBarLeadingAccessories_Previews: PreviewProvider { - static var previews: some View { - EditorTabBarLeadingAccessories() - } -} diff --git a/CodeEdit/Features/Editor/Views/EditorView.swift b/CodeEdit/Features/Editor/Views/EditorView.swift index 4a9a16781..4b36365c8 100644 --- a/CodeEdit/Features/Editor/Views/EditorView.swift +++ b/CodeEdit/Features/Editor/Views/EditorView.swift @@ -11,6 +11,9 @@ struct EditorView: View { @AppSettings(\.general.showEditorPathBar) var showEditorPathBar + @AppSettings(\.navigation.navigationStyle) + var navigationStyle + @AppSettings(\.general.dimEditorsWithoutFocus) var dimEditorsWithoutFocus @@ -20,8 +23,15 @@ struct EditorView: View { @EnvironmentObject private var editorManager: EditorManager + // TODO: If ANY editor has permanent tabs, show tab bar, not just this editor (is this the most efficient place to do this check?) + var shouldShowTabBar: Bool { + return navigationStyle == .openInTabs + || (editor.temporaryTab == nil && !editor.tabs.isEmpty) + || (editor.temporaryTab != nil && editor.tabs.count > 1) + } + var editorInsetAmount: Double { - let tabBarHeight = EditorTabBarView.height + 1 + let tabBarHeight = shouldShowTabBar ? (EditorTabBarView.height + 1) : 0 let pathBarHeight = showEditorPathBar ? (EditorPathBarView.height + 1) : 0 return tabBarHeight + pathBarHeight } @@ -33,11 +43,11 @@ struct EditorView: View { file: selected.file, textViewCoordinators: [selected.rangeTranslator].compactMap({ $0 }) ) - .focusedObject(editor) - .transformEnvironment(\.edgeInsets) { insets in - insets.top += editorInsetAmount - } - .opacity(dimEditorsWithoutFocus && editor != editorManager.activeEditor ? 0.5 : 1) + .focusedObject(editor) + .transformEnvironment(\.edgeInsets) { insets in + insets.top += editorInsetAmount + } + .opacity(dimEditorsWithoutFocus && editor != editorManager.activeEditor ? 0.5 : 1) } else { CEContentUnavailableView("No Editor") .padding(.top, editorInsetAmount) @@ -50,16 +60,23 @@ struct EditorView: View { .ignoresSafeArea(.all) .safeAreaInset(edge: .top, spacing: 0) { VStack(spacing: 0) { - EditorTabBarView() - .id("TabBarView" + editor.id.uuidString) - .environmentObject(editor) - Divider() + if shouldShowTabBar { + EditorTabBarView() + .id("TabBarView" + editor.id.uuidString) + .environmentObject(editor) + Divider() + } if showEditorPathBar { - EditorPathBarView(file: editor.selectedTab?.file) { [weak editor] newFile in + EditorPathBarView( + file: editor.selectedTab?.file, + shouldShowTabBar: shouldShowTabBar + ) { [weak editor] newFile in if let file = editor?.selectedTab, let index = editor?.tabs.firstIndex(of: file) { editor?.openTab(file: newFile, at: index) } } + .environmentObject(editor) + .padding(.top, shouldShowTabBar ? -1 : 0) Divider() } } @@ -68,7 +85,9 @@ struct EditorView: View { } .focused($focus, equals: editor) .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("CodeEditor.didBeginEditing"))) { _ in - editor.temporaryTab = nil + if navigationStyle == .openInTabs { + editor.temporaryTab = nil + } } } } diff --git a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift index 12ddae603..ef12c300c 100644 --- a/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift +++ b/CodeEdit/Features/NavigatorArea/ProjectNavigator/OutlineView/ProjectNavigatorViewController.swift @@ -29,6 +29,7 @@ final class ProjectNavigatorViewController: NSViewController { var workspace: WorkspaceDocument? var iconColor: SettingsData.FileIconStyle = .color + var fileExtensionsVisibility: SettingsData.FileExtensionsVisibility = .showAll var shownFileExtensions: SettingsData.FileExtensions = .default var hiddenFileExtensions: SettingsData.FileExtensions = .default @@ -119,7 +120,7 @@ final class ProjectNavigatorViewController: NSViewController { } else { outlineView.expandItem(item) } - } else { + } else if Settings[\.navigation].navigationStyle == .openInTabs { workspace?.editorManager.activeEditor.openTab(file: item, asTemporary: false) } } diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift index ccdf86216..c3f534d9b 100644 --- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift +++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift @@ -46,7 +46,7 @@ struct SourceControlNavigatorTabs: View { prominent: true ) .frame(maxWidth: .infinity) - .frame(height: 26) + .frame(height: 27) .padding(.horizontal, 8) .task { do { diff --git a/CodeEdit/Features/Settings/Models/SettingsData.swift b/CodeEdit/Features/Settings/Models/SettingsData.swift index 05c48c719..2eebf5af0 100644 --- a/CodeEdit/Features/Settings/Models/SettingsData.swift +++ b/CodeEdit/Features/Settings/Models/SettingsData.swift @@ -29,6 +29,9 @@ struct SettingsData: Codable, Hashable { /// The global settings for accounts var accounts: AccountsSettings = .init() + /// The global settings for themes + var navigation: NavigationSettings = .init() + /// The global settings for themes var theme: ThemeSettings = .init() @@ -58,6 +61,7 @@ struct SettingsData: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) self.general = try container.decodeIfPresent(GeneralSettings.self, forKey: .general) ?? .init() self.accounts = try container.decodeIfPresent(AccountsSettings.self, forKey: .accounts) ?? .init() + self.navigation = try container.decodeIfPresent(NavigationSettings.self, forKey: .navigation) ?? .init() self.theme = try container.decodeIfPresent(ThemeSettings.self, forKey: .theme) ?? .init() self.terminal = try container.decodeIfPresent(TerminalSettings.self, forKey: .terminal) ?? .init() self.textEditing = try container.decodeIfPresent(TextEditingSettings.self, forKey: .textEditing) ?? .init() @@ -82,6 +86,8 @@ struct SettingsData: Codable, Hashable { general.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) } case .accounts: accounts.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) } + case .navigation: + navigation.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) } case .theme: theme.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) } case .textEditing: @@ -97,7 +103,6 @@ struct SettingsData: Codable, Hashable { case .featureFlags: featureFlags.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) } case .behavior: return [.init(name, settingName: "Error")] - case .navigation: return [.init(name, settingName: "Error")] case .components: return [.init(name, settingName: "Error")] case .keybindings: return [.init(name, settingName: "Error")] case .advanced: return [.init(name, settingName: "Error")] diff --git a/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift b/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift index 3b19111cd..fa95f98d3 100644 --- a/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift +++ b/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift @@ -6,3 +6,37 @@ // import Foundation + +extension SettingsData { + + /// The global settings for the terminal emulator + struct NavigationSettings: Codable, Hashable, SearchableSettingsPage { + + /// The search keys + var searchKeys: [String] { + [ + "Navigation Style", + ] + .map { NSLocalizedString($0, comment: "") } + } + + /// Navigation style used + var navigationStyle: NavigationStyle = .openInTabs + + /// Default initializer + init() {} + + /// Explicit decoder init for setting default values when key is not present in `JSON` + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.navigationStyle = try container.decodeIfPresent( + NavigationStyle.self, forKey: .navigationStyle + ) ?? .openInTabs + } + } + + enum NavigationStyle: String, Codable, Hashable { + case openInTabs + case openInPlace + } +} diff --git a/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift b/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift index 0d8ee57ed..552eb4a07 100644 --- a/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift +++ b/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift @@ -8,11 +8,25 @@ import SwiftUI struct NavigationSettingsView: View { + @AppSettings(\.navigation) + var settings + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + SettingsForm { + Section { + navigationStyle + } + } } } -#Preview { - NavigationSettingsView() +private extension NavigationSettingsView { + private var navigationStyle: some View { + Picker("Navigation Style", selection: $settings.navigationStyle) { + Text("Open in Tabs") + .tag(SettingsData.NavigationStyle.openInTabs) + Text("Open in Place") + .tag(SettingsData.NavigationStyle.openInPlace) + } + } } diff --git a/CodeEdit/Features/Settings/SettingsView.swift b/CodeEdit/Features/Settings/SettingsView.swift index faddda296..a40d76dba 100644 --- a/CodeEdit/Features/Settings/SettingsView.swift +++ b/CodeEdit/Features/Settings/SettingsView.swift @@ -36,6 +36,13 @@ struct SettingsView: View { icon: .system("at") ) ), + .init( + SettingsPage( + .navigation, + baseColor: .green, + icon: .system("arrow.triangle.turn.up.right.diamond.fill") + ) + ), .init( SettingsPage( .theme, @@ -152,6 +159,8 @@ struct SettingsView: View { GeneralSettingsView().environmentObject(updater) case .accounts: AccountsSettingsView() + case .navigation: + NavigationSettingsView() case .theme: ThemeSettingsView() case .textEditing: From 81879106a433a39d32fdc7bd832bf3ef573ac048 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Wed, 6 Mar 2024 01:51:33 -0600 Subject: [PATCH 03/12] If one editors tab bar should be shown, all tab bars should be shown across other editors --- .../Features/Editor/Models/EditorLayout.swift | 18 ++++++++++++++++ .../Editor/Models/EditorManager.swift | 21 +++++++++++++++++++ .../Features/Editor/Views/EditorView.swift | 9 ++++++-- .../SplitView/Model/SplitViewData.swift | 9 ++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/CodeEdit/Features/Editor/Models/EditorLayout.swift b/CodeEdit/Features/Editor/Models/EditorLayout.swift index 86932561a..3f06163c8 100644 --- a/CodeEdit/Features/Editor/Models/EditorLayout.swift +++ b/CodeEdit/Features/Editor/Models/EditorLayout.swift @@ -89,6 +89,24 @@ enum EditorLayout: Equatable { } } + /// Gets flattened splitviews. + mutating func getFlattened(parent: SplitViewData) -> [Editor] { + switch self { + case .one(let editor): + return [editor] + case .horizontal(let data), .vertical(let data): + if data.editorLayouts.count == 1 { + let one = data.editorLayouts[0] + if case .one(let editor) = one { + return [editor] + } + return [] + } else { + return data.getFlattened() + } + } + } + var isEmpty: Bool { switch self { case .one: diff --git a/CodeEdit/Features/Editor/Models/EditorManager.swift b/CodeEdit/Features/Editor/Models/EditorManager.swift index 3ce1866e2..e0b501a0b 100644 --- a/CodeEdit/Features/Editor/Models/EditorManager.swift +++ b/CodeEdit/Features/Editor/Models/EditorManager.swift @@ -36,6 +36,17 @@ class EditorManager: ObservableObject { var tabBarTabIdSubject = PassthroughSubject() var cancellable: AnyCancellable? + // This caching mechanism is a temporary solution and is not optimized + var updateCachedFlattenedEditors: Bool = true + var cachedFlettenedEditors: [Editor] = [] + var flattenedEditors: [Editor] { + if updateCachedFlattenedEditors { + cachedFlettenedEditors = self.getFlattened() + updateCachedFlattenedEditors = false + } + return cachedFlettenedEditors + } + // MARK: - Init init() { @@ -68,6 +79,16 @@ class EditorManager: ObservableObject { } } + /// Returns and array of flattened splitviews. + func getFlattened() -> [Editor] { + if case .horizontal(let data) = editorLayout { + return data.getFlattened() + } else if case .vertical(let data) = editorLayout { + return data.getFlattened() + } + return [] + } + /// Opens a new tab in a editor. /// - Parameters: /// - item: The tab to open. diff --git a/CodeEdit/Features/Editor/Views/EditorView.swift b/CodeEdit/Features/Editor/Views/EditorView.swift index 4b36365c8..db6a27f49 100644 --- a/CodeEdit/Features/Editor/Views/EditorView.swift +++ b/CodeEdit/Features/Editor/Views/EditorView.swift @@ -26,8 +26,10 @@ struct EditorView: View { // TODO: If ANY editor has permanent tabs, show tab bar, not just this editor (is this the most efficient place to do this check?) var shouldShowTabBar: Bool { return navigationStyle == .openInTabs - || (editor.temporaryTab == nil && !editor.tabs.isEmpty) - || (editor.temporaryTab != nil && editor.tabs.count > 1) + || editorManager.flattenedEditors.contains { editor in + (editor.temporaryTab == nil && !editor.tabs.isEmpty) + || (editor.temporaryTab != nil && editor.tabs.count > 1) + } } var editorInsetAmount: Double { @@ -89,5 +91,8 @@ struct EditorView: View { editor.temporaryTab = nil } } + .onChange(of: editorManager.flattenedEditors) { newValue in + print(newValue) + } } } diff --git a/CodeEdit/Features/SplitView/Model/SplitViewData.swift b/CodeEdit/Features/SplitView/Model/SplitViewData.swift index 5eb8275ed..a87408576 100644 --- a/CodeEdit/Features/SplitView/Model/SplitViewData.swift +++ b/CodeEdit/Features/SplitView/Model/SplitViewData.swift @@ -85,4 +85,13 @@ final class SplitViewData: ObservableObject { editorLayouts[index].flatten(parent: self) } } + + /// Gets flattened splitviews. + func getFlattened() -> [Editor] { + var arr: [Editor] = [] + for index in editorLayouts.indices { + arr += editorLayouts[index].getFlattened(parent: self) + } + return arr + } } From 1d9f98e376ebb2f0f04f798dc8fe15598f958e0f Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Wed, 6 Mar 2024 02:03:51 -0600 Subject: [PATCH 04/12] Removed complete TODO comment --- CodeEdit/Features/Editor/Views/EditorView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/CodeEdit/Features/Editor/Views/EditorView.swift b/CodeEdit/Features/Editor/Views/EditorView.swift index db6a27f49..55fb9146f 100644 --- a/CodeEdit/Features/Editor/Views/EditorView.swift +++ b/CodeEdit/Features/Editor/Views/EditorView.swift @@ -23,7 +23,6 @@ struct EditorView: View { @EnvironmentObject private var editorManager: EditorManager - // TODO: If ANY editor has permanent tabs, show tab bar, not just this editor (is this the most efficient place to do this check?) var shouldShowTabBar: Bool { return navigationStyle == .openInTabs || editorManager.flattenedEditors.contains { editor in From f4f257a12c4fb26ac6c2cccd0b50edc4cbb4b9b2 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Wed, 6 Mar 2024 02:12:56 -0600 Subject: [PATCH 05/12] When there is only one tab in an editor and the navigation style changes to open in place, we change the tab to be termporary so that tab bar will hide --- CodeEdit/Features/Editor/Views/EditorView.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CodeEdit/Features/Editor/Views/EditorView.swift b/CodeEdit/Features/Editor/Views/EditorView.swift index 55fb9146f..3942cf73f 100644 --- a/CodeEdit/Features/Editor/Views/EditorView.swift +++ b/CodeEdit/Features/Editor/Views/EditorView.swift @@ -90,8 +90,10 @@ struct EditorView: View { editor.temporaryTab = nil } } - .onChange(of: editorManager.flattenedEditors) { newValue in - print(newValue) + .onChange(of: navigationStyle) { newValue in + if newValue == .openInPlace && editor.tabs.count == 1 { + editor.temporaryTab = editor.tabs[0] + } } } } From d571c734cc3c9b972e0e3bcc1fbc77cb640355cd Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Thu, 7 Mar 2024 12:50:44 -0600 Subject: [PATCH 06/12] Invalidating flattenedEditors cache when editors change. --- .../Editor/Models/EditorManager.swift | 3 ++- .../PathBar/Views/EditorPathBarView.swift | 2 +- .../EditorTabBarTrailingAccessories.swift | 1 + .../Features/Editor/Views/EditorView.swift | 24 +++++++++---------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/CodeEdit/Features/Editor/Models/EditorManager.swift b/CodeEdit/Features/Editor/Models/EditorManager.swift index e0b501a0b..063e30a70 100644 --- a/CodeEdit/Features/Editor/Models/EditorManager.swift +++ b/CodeEdit/Features/Editor/Models/EditorManager.swift @@ -37,7 +37,7 @@ class EditorManager: ObservableObject { var cancellable: AnyCancellable? // This caching mechanism is a temporary solution and is not optimized - var updateCachedFlattenedEditors: Bool = true + @Published var updateCachedFlattenedEditors: Bool = true var cachedFlettenedEditors: [Editor] = [] var flattenedEditors: [Editor] { if updateCachedFlattenedEditors { @@ -120,6 +120,7 @@ class EditorManager: ObservableObject { flatten() objectWillChange.send() + updateCachedFlattenedEditors = true } /// Set a new active editor. diff --git a/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift b/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift index 32a5ed72d..3b488d4cc 100644 --- a/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift +++ b/CodeEdit/Features/Editor/PathBar/Views/EditorPathBarView.swift @@ -67,7 +67,7 @@ struct EditorPathBarView: View { } } } - .padding(.horizontal, shouldShowTabBar ? file == nil ? 10 : 4 : 0) + .padding(.horizontal, shouldShowTabBar ? (file == nil ? 10 : 4) : 0) .safeAreaInset(edge: .leading, spacing: 0) { if !shouldShowTabBar { EditorTabBarLeadingAccessories() diff --git a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift index 7b8b68507..eb93488a1 100644 --- a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift +++ b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift @@ -74,6 +74,7 @@ struct EditorTabBarTrailingAccessories: View { newEditor = .init() } splitEditor(edge, newEditor) + editorManager.updateCachedFlattenedEditors = true editorManager.activeEditor = newEditor } } diff --git a/CodeEdit/Features/Editor/Views/EditorView.swift b/CodeEdit/Features/Editor/Views/EditorView.swift index 3942cf73f..6ca74ae35 100644 --- a/CodeEdit/Features/Editor/Views/EditorView.swift +++ b/CodeEdit/Features/Editor/Views/EditorView.swift @@ -23,21 +23,21 @@ struct EditorView: View { @EnvironmentObject private var editorManager: EditorManager - var shouldShowTabBar: Bool { - return navigationStyle == .openInTabs - || editorManager.flattenedEditors.contains { editor in - (editor.temporaryTab == nil && !editor.tabs.isEmpty) - || (editor.temporaryTab != nil && editor.tabs.count > 1) + var body: some View { + var shouldShowTabBar: Bool { + return navigationStyle == .openInTabs + || editorManager.flattenedEditors.contains { editor in + (editor.temporaryTab == nil && !editor.tabs.isEmpty) + || (editor.temporaryTab != nil && editor.tabs.count > 1) + } } - } - var editorInsetAmount: Double { - let tabBarHeight = shouldShowTabBar ? (EditorTabBarView.height + 1) : 0 - let pathBarHeight = showEditorPathBar ? (EditorPathBarView.height + 1) : 0 - return tabBarHeight + pathBarHeight - } + var editorInsetAmount: Double { + let tabBarHeight = shouldShowTabBar ? (EditorTabBarView.height + 1) : 0 + let pathBarHeight = showEditorPathBar ? (EditorPathBarView.height + 1) : 0 + return tabBarHeight + pathBarHeight + } - var body: some View { VStack { if let selected = editor.selectedTab { WorkspaceCodeFileView( From a02bf0b8a113a658382249397a4e21a0fd6a30ad Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Mon, 11 Mar 2024 09:42:51 -0500 Subject: [PATCH 07/12] New editors open first tab as a temporary tab. --- CodeEdit/Features/Editor/Models/Editor.swift | 5 +++++ .../Features/Editor/Models/EditorManager.swift | 15 ++++++++------- .../Views/EditorTabBarTrailingAccessories.swift | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CodeEdit/Features/Editor/Models/Editor.swift b/CodeEdit/Features/Editor/Models/Editor.swift index 2a494baa1..f04d33d42 100644 --- a/CodeEdit/Features/Editor/Models/Editor.swift +++ b/CodeEdit/Features/Editor/Models/Editor.swift @@ -64,29 +64,34 @@ final class Editor: ObservableObject, Identifiable { init() { self.tabs = [] + self.temporaryTab = nil self.parent = nil } init( files: OrderedSet = [], selectedTab: Tab? = nil, + temporaryTab: Tab? = nil, parent: SplitViewData? = nil ) { self.tabs = [] self.parent = parent files.forEach { openTab(file: $0) } self.selectedTab = selectedTab ?? (files.isEmpty ? nil : Tab(file: files.first!)) + self.temporaryTab = temporaryTab } init( files: OrderedSet = [], selectedTab: Tab? = nil, + temporaryTab: Tab? = nil, parent: SplitViewData? = nil ) { self.tabs = [] self.parent = parent files.forEach { openTab(file: $0.file) } self.selectedTab = selectedTab ?? tabs.first + self.temporaryTab = temporaryTab } /// Closes the editor. diff --git a/CodeEdit/Features/Editor/Models/EditorManager.swift b/CodeEdit/Features/Editor/Models/EditorManager.swift index 063e30a70..4330d2e2b 100644 --- a/CodeEdit/Features/Editor/Models/EditorManager.swift +++ b/CodeEdit/Features/Editor/Models/EditorManager.swift @@ -72,21 +72,22 @@ class EditorManager: ObservableObject { /// Flattens the splitviews. func flatten() { - if case .horizontal(let data) = editorLayout { - data.flatten() - } else if case .vertical(let data) = editorLayout { + switch editorLayout { + case .horizontal(let data), .vertical(let data): data.flatten() + default: + break } } /// Returns and array of flattened splitviews. func getFlattened() -> [Editor] { - if case .horizontal(let data) = editorLayout { - return data.getFlattened() - } else if case .vertical(let data) = editorLayout { + switch editorLayout { + case .horizontal(let data), .vertical(let data): return data.getFlattened() + default: + return [] } - return [] } /// Opens a new tab in a editor. diff --git a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift index eb93488a1..f023729ca 100644 --- a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift +++ b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift @@ -69,7 +69,7 @@ struct EditorTabBarTrailingAccessories: View { func split(edge: Edge) { let newEditor: Editor if let tab = editor.selectedTab { - newEditor = .init(files: [tab]) + newEditor = .init(files: [tab], temporaryTab: tab) } else { newEditor = .init() } From 86ddbb8c016243e5680f275b25cc5d66015eed95 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Mon, 11 Mar 2024 14:48:55 -0500 Subject: [PATCH 08/12] History improvements - if going back or forward in history to a file for a teb that was previously closed, we now open it as a temporary tab --- CodeEdit/Features/Editor/Models/Editor.swift | 17 ++++++++++------- .../Views/EditorTabBarLeadingAccessories.swift | 17 +++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CodeEdit/Features/Editor/Models/Editor.swift b/CodeEdit/Features/Editor/Models/Editor.swift index f04d33d42..ffc71f152 100644 --- a/CodeEdit/Features/Editor/Models/Editor.swift +++ b/CodeEdit/Features/Editor/Models/Editor.swift @@ -40,11 +40,11 @@ final class Editor: ObservableObject, Identifiable { let tab = history[historyOffset] if !tabs.contains(tab) { - if let selectedTab { - openTab(file: tab.file, at: tabs.firstIndex(of: selectedTab), fromHistory: true) - } else { - openTab(file: tab.file, fromHistory: true) + if let temporaryTab, tabs.contains(temporaryTab) { + closeTab(file: temporaryTab.file, fromHistory: true) } + temporaryTab = tab + openTab(file: tab.file, fromHistory: true) } selectedTab = tab } @@ -107,14 +107,15 @@ final class Editor: ObservableObject, Identifiable { /// Closes a tab in the editor. /// This will also write any changes to the file on disk and will add the tab to the tab history. /// - Parameter item: the tab to close. - func closeTab(file: CEWorkspaceFile) { + func closeTab(file: CEWorkspaceFile, fromHistory: Bool = false) { guard canCloseTab(file: file) else { return } if temporaryTab?.file == file { temporaryTab = nil } - - historyOffset = 0 + if !fromHistory { + historyOffset = 0 + } if file != selectedTab?.file { history.prepend(EditorInstance(file: file)) } @@ -154,7 +155,9 @@ final class Editor: ObservableObject, Identifiable { switch (temporaryTab, asTemporary) { case (.some(let tab), true): if let index = tabs.firstIndex(of: tab) { + history.removeFirst(historyOffset) history.prepend(item) + historyOffset = 0 tabs.remove(tab) tabs.insert(item, at: index) self.selectedTab = item diff --git a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift index 40ddf958c..d330da3e7 100644 --- a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift +++ b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarLeadingAccessories.swift @@ -74,11 +74,9 @@ struct EditorTabBarLeadingAccessories: View { } } label: { Image(systemName: "chevron.left") - .controlSize(.regular) - .opacity( - editor.historyOffset == editor.history.count-1 || editor.history.isEmpty - ? 0.5 : 1.0 - ) + .opacity(editor.historyOffset == editor.history.count-1 || editor.history.isEmpty ? 0.5 : 1) + .frame(height: EditorTabBarView.height - 2) + .padding(.horizontal, 4) } primaryAction: { editorManager.activeEditor = editor editor.goBackInHistory() @@ -103,8 +101,9 @@ struct EditorTabBarLeadingAccessories: View { } } label: { Image(systemName: "chevron.right") - .controlSize(.regular) - .opacity(editor.historyOffset == 0 ? 0.5 : 1.0) + .opacity(editor.historyOffset == 0 ? 0.5 : 1) + .frame(height: EditorTabBarView.height - 2) + .padding(.horizontal, 4) } primaryAction: { editorManager.activeEditor = editor editor.goForwardInHistory() @@ -112,11 +111,9 @@ struct EditorTabBarLeadingAccessories: View { .disabled(editor.historyOffset == 0) .help("Navigate forward") } + .buttonStyle(.icon) .controlSize(.small) .font(EditorTabBarAccessoryIcon.iconFont) - .frame(height: EditorTabBarView.height - 2) - .padding(.horizontal, 4) - .contentShape(Rectangle()) } .foregroundColor(.secondary) .buttonStyle(.plain) From 5ef954a261d45e38d19565d4ccd6deebef985460 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Mon, 11 Mar 2024 15:03:37 -0500 Subject: [PATCH 09/12] Update CodeEditSourceEditor dependency version --- CodeEdit.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 2e6257c76..e24072c5c 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -4693,7 +4693,7 @@ repositoryURL = "https://github.com/CodeEditApp/CodeEditSourceEditor"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.7.1; + minimumVersion = 0.7.2; }; }; 6CDEFC9429E22C2700B7C684 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { From afa32e34db1c9b560451cbb5941ed962300c1334 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Mon, 11 Mar 2024 15:13:26 -0500 Subject: [PATCH 10/12] Invalidating flattenedEditors cache when closing an editor --- CodeEdit/Features/Editor/Models/Editor.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/CodeEdit/Features/Editor/Models/Editor.swift b/CodeEdit/Features/Editor/Models/Editor.swift index ffc71f152..52b9d34e1 100644 --- a/CodeEdit/Features/Editor/Models/Editor.swift +++ b/CodeEdit/Features/Editor/Models/Editor.swift @@ -97,6 +97,7 @@ final class Editor: ObservableObject, Identifiable { /// Closes the editor. func close() { parent?.closeEditor(with: id) + editorManager.updateCachedFlattenedEditors = true } /// Gets the editor layout. From 29a0c764b40cb7e8c137b018e01593250a22d873 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Mon, 11 Mar 2024 15:18:39 -0500 Subject: [PATCH 11/12] Removing last change --- CodeEdit/Features/Editor/Models/Editor.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/CodeEdit/Features/Editor/Models/Editor.swift b/CodeEdit/Features/Editor/Models/Editor.swift index 52b9d34e1..ffc71f152 100644 --- a/CodeEdit/Features/Editor/Models/Editor.swift +++ b/CodeEdit/Features/Editor/Models/Editor.swift @@ -97,7 +97,6 @@ final class Editor: ObservableObject, Identifiable { /// Closes the editor. func close() { parent?.closeEditor(with: id) - editorManager.updateCachedFlattenedEditors = true } /// Gets the editor layout. From 2cd2022c70be18914bb0a7218bbbff70d1969163 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Tue, 12 Mar 2024 00:26:03 -0500 Subject: [PATCH 12/12] Fixed PR issue. --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- CodeEdit/Features/Editor/Models/EditorLayout.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 847168345..040dc189f 100644 --- a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/CodeEditApp/CodeEditSourceEditor", "state" : { - "revision" : "a72e6c9b5f38b00ea0b03da5e706e3c4d05c706e", - "version" : "0.7.1" + "revision" : "7360f00bf7ec8e93b4833357bd254bef7e5c943d", + "version" : "0.7.2" } }, { diff --git a/CodeEdit/Features/Editor/Models/EditorLayout.swift b/CodeEdit/Features/Editor/Models/EditorLayout.swift index 3f06163c8..ee803a76a 100644 --- a/CodeEdit/Features/Editor/Models/EditorLayout.swift +++ b/CodeEdit/Features/Editor/Models/EditorLayout.swift @@ -90,7 +90,7 @@ enum EditorLayout: Equatable { } /// Gets flattened splitviews. - mutating func getFlattened(parent: SplitViewData) -> [Editor] { + func getFlattened(parent: SplitViewData) -> [Editor] { switch self { case .one(let editor): return [editor]