diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 84e65b4a5..e24072c5c 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 */, @@ -4669,7 +4693,7 @@ repositoryURL = "https://github.com/CodeEditApp/CodeEditSourceEditor"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.7.1; + minimumVersion = 0.7.2; }; }; 6CDEFC9429E22C2700B7C684 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { 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/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/Models/Editor.swift b/CodeEdit/Features/Editor/Models/Editor.swift index 2a494baa1..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 } @@ -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. @@ -102,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)) } @@ -149,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/Models/EditorLayout.swift b/CodeEdit/Features/Editor/Models/EditorLayout.swift index 86932561a..ee803a76a 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. + 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..4330d2e2b 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 + @Published var updateCachedFlattenedEditors: Bool = true + var cachedFlettenedEditors: [Editor] = [] + var flattenedEditors: [Editor] { + if updateCachedFlattenedEditors { + cachedFlettenedEditors = self.getFlattened() + updateCachedFlattenedEditors = false + } + return cachedFlettenedEditors + } + // MARK: - Init init() { @@ -61,10 +72,21 @@ 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] { + switch editorLayout { + case .horizontal(let data), .vertical(let data): + return data.getFlattened() + default: + return [] } } @@ -99,6 +121,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 994de3759..3b488d4cc 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..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) @@ -136,9 +133,3 @@ struct EditorTabBarLeadingAccessories: View { } } } - -struct TabBarLeadingAccessories_Previews: PreviewProvider { - static var previews: some View { - EditorTabBarLeadingAccessories() - } -} diff --git a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift index 7b8b68507..f023729ca 100644 --- a/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift +++ b/CodeEdit/Features/Editor/TabBar/Views/EditorTabBarTrailingAccessories.swift @@ -69,11 +69,12 @@ 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() } 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 4a9a16781..6ca74ae35 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,24 +23,32 @@ struct EditorView: View { @EnvironmentObject private var editorManager: EditorManager - var editorInsetAmount: Double { - let tabBarHeight = EditorTabBarView.height + 1 - let pathBarHeight = showEditorPathBar ? (EditorPathBarView.height + 1) : 0 - return tabBarHeight + pathBarHeight - } - 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 + } + VStack { if let selected = editor.selectedTab { WorkspaceCodeFileView( 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 +61,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 +86,14 @@ 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 + } + } + .onChange(of: navigationStyle) { newValue in + if newValue == .openInPlace && editor.tabs.count == 1 { + editor.temporaryTab = editor.tabs[0] + } } } } 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 new file mode 100644 index 000000000..fa95f98d3 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/NavigationSettings/Models/NavigationSettings.swift @@ -0,0 +1,42 @@ +// +// NavigationSettings.swift +// CodeEdit +// +// Created by Austin Condiff on 3/4/24. +// + +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 new file mode 100644 index 000000000..552eb4a07 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/NavigationSettings/NavigationSettingsView.swift @@ -0,0 +1,32 @@ +// +// NavigationSettingsView.swift +// CodeEdit +// +// Created by Austin Condiff on 3/4/24. +// + +import SwiftUI + +struct NavigationSettingsView: View { + @AppSettings(\.navigation) + var settings + + var body: some View { + SettingsForm { + Section { + navigationStyle + } + } + } +} + +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: 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 + } }