From f2d2e298203e67345ae24d56d6ece98fd46e3fb7 Mon Sep 17 00:00:00 2001 From: Abe M Date: Sun, 16 Jul 2023 19:34:17 -0700 Subject: [PATCH 01/20] Fixed close tab and close window shortcut --- CodeEdit/Features/Tabs/Views/TabBarItemView.swift | 2 +- CodeEdit/Features/WindowCommands/FileCommands.swift | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift index f5a10fc3d..65a584344 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift @@ -167,7 +167,7 @@ struct TabBarItemView: View { // Close Tab Shortcut: // Using an invisible button to contain the keyboard shortcut is simply // because the keyboard shortcut has an unexpected bug when working with - // custom buttonStyle. This is an workaround and it works as expected. + // custom buttonStyle. This is a workaround and it works as expected. Button( action: closeAction, label: { EmptyView() } diff --git a/CodeEdit/Features/WindowCommands/FileCommands.swift b/CodeEdit/Features/WindowCommands/FileCommands.swift index 412c3145c..530af79bb 100644 --- a/CodeEdit/Features/WindowCommands/FileCommands.swift +++ b/CodeEdit/Features/WindowCommands/FileCommands.swift @@ -36,13 +36,8 @@ struct FileCommands: Commands { } CommandGroup(replacing: .saveItem) { - Button("Close Tab") { - NSApp.sendAction(#selector(NSWindow.close), to: nil, from: nil) - } - .keyboardShortcut("w") - Button("Close Editor") { - + NSApp.sendAction(#selector(NSWindow.close), to: nil, from: nil) } .keyboardShortcut("w", modifiers: [.control, .shift, .command]) From 210884b0e84f876c22d16432eb3415a6b5429d24 Mon Sep 17 00:00:00 2001 From: Abe M Date: Sun, 16 Jul 2023 21:54:54 -0700 Subject: [PATCH 02/20] Replace .hidden modifier with .opacity modifier --- CodeEdit/Features/Tabs/Views/TabBarItemView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift index 65a584344..746ef6f47 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift @@ -174,7 +174,7 @@ struct TabBarItemView: View { ) .frame(width: 0, height: 0) .keyboardShortcut("w", modifiers: [.command]) - .hidden() + .opacity(0) } // Switch Tab Shortcut: // Using an invisible button to contain the keyboard shortcut is simply From 518c4ab4d64303196a2c6d6c668a502e8c62a8a8 Mon Sep 17 00:00:00 2001 From: Abe M Date: Sun, 16 Jul 2023 22:07:36 -0700 Subject: [PATCH 03/20] Fixed tab switching shortcuts not working --- CodeEdit/Features/Tabs/Views/TabBarItemView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift index 746ef6f47..6c43bf0bf 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift @@ -190,7 +190,7 @@ struct TabBarItemView: View { KeyEquivalent(Character(String(index))), modifiers: [.command] ) - .hidden() + .opacity(0) } // Close Button TabBarItemCloseButton( From 3e074281d9521f97651dcebe98ae261ec4578e0f Mon Sep 17 00:00:00 2001 From: Abe M Date: Sun, 16 Jul 2023 22:13:18 -0700 Subject: [PATCH 04/20] Undo changes --- CodeEdit/Features/Tabs/Views/TabBarItemView.swift | 4 ++-- CodeEdit/Features/WindowCommands/FileCommands.swift | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift index 6c43bf0bf..665337b96 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift @@ -167,14 +167,14 @@ struct TabBarItemView: View { // Close Tab Shortcut: // Using an invisible button to contain the keyboard shortcut is simply // because the keyboard shortcut has an unexpected bug when working with - // custom buttonStyle. This is a workaround and it works as expected. + // custom buttonStyle. This is an workaround and it works as expected. Button( action: closeAction, label: { EmptyView() } ) .frame(width: 0, height: 0) .keyboardShortcut("w", modifiers: [.command]) - .opacity(0) + .hidden() } // Switch Tab Shortcut: // Using an invisible button to contain the keyboard shortcut is simply diff --git a/CodeEdit/Features/WindowCommands/FileCommands.swift b/CodeEdit/Features/WindowCommands/FileCommands.swift index 530af79bb..412c3145c 100644 --- a/CodeEdit/Features/WindowCommands/FileCommands.swift +++ b/CodeEdit/Features/WindowCommands/FileCommands.swift @@ -36,8 +36,13 @@ struct FileCommands: Commands { } CommandGroup(replacing: .saveItem) { - Button("Close Editor") { + Button("Close Tab") { NSApp.sendAction(#selector(NSWindow.close), to: nil, from: nil) + } + .keyboardShortcut("w") + + Button("Close Editor") { + } .keyboardShortcut("w", modifiers: [.control, .shift, .command]) From 3ac4741a456e6453cb4dd2c96b0d4abff7ae9cad Mon Sep 17 00:00:00 2001 From: Abe M Date: Sun, 16 Jul 2023 22:19:32 -0700 Subject: [PATCH 05/20] Fixed the close button not being able to be pressed --- CodeEdit/Features/Tabs/Views/TabBarItemView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift index 665337b96..66a38a486 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift @@ -191,6 +191,7 @@ struct TabBarItemView: View { modifiers: [.command] ) .opacity(0) + .allowsHitTesting(false) } // Close Button TabBarItemCloseButton( From 989ecb257a40a216ef35d790ef54dd77a9db36ca Mon Sep 17 00:00:00 2001 From: Abe M Date: Mon, 24 Jul 2023 00:06:38 -0700 Subject: [PATCH 06/20] Update keyboard shortcuts to switch between sidebar navigator items --- CodeEdit.xcodeproj/project.pbxproj | 4 ++++ .../CodeEditWindowController.swift | 11 ++++++--- .../NavigatorSidebarView.swift | 13 +++++------ .../NavigatorSidebarViewModel.swift | 16 +++++++++++++ .../WindowCommands/ViewCommands.swift | 23 ++++++++++++++++++- CodeEdit/WindowSplitView.swift | 3 ++- 6 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 2b92f14b9..8bf617e3b 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -54,6 +54,7 @@ 2B7AC06B282452FB0082A5B8 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2B7AC06A282452FB0082A5B8 /* Media.xcassets */; }; 2BE487EF28245162003F3F64 /* FinderSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BE487EE28245162003F3F64 /* FinderSync.swift */; }; 2BE487F428245162003F3F64 /* OpenWithCodeEdit.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 2BE487EC28245162003F3F64 /* OpenWithCodeEdit.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 30E6D0012A6E505200A58B20 /* NavigatorSidebarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E6D0002A6E505200A58B20 /* NavigatorSidebarViewModel.swift */; }; 3E0196732A3921AC002648D8 /* codeedit_shell_integration.zsh in Resources */ = {isa = PBXBuildFile; fileRef = 3E0196722A3921AC002648D8 /* codeedit_shell_integration.zsh */; }; 3E01967A2A392B45002648D8 /* codeedit_shell_integration.bash in Resources */ = {isa = PBXBuildFile; fileRef = 3E0196792A392B45002648D8 /* codeedit_shell_integration.bash */; }; 474397C52893AC4B00518C8C /* codeedit-midnight.json in Resources */ = {isa = PBXBuildFile; fileRef = 474397C42893AC4B00518C8C /* codeedit-midnight.json */; }; @@ -514,6 +515,7 @@ 2BE487EC28245162003F3F64 /* OpenWithCodeEdit.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenWithCodeEdit.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 2BE487EE28245162003F3F64 /* FinderSync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinderSync.swift; sourceTree = ""; }; 2BE487F028245162003F3F64 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 30E6D0002A6E505200A58B20 /* NavigatorSidebarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigatorSidebarViewModel.swift; sourceTree = ""; }; 3E0196722A3921AC002648D8 /* codeedit_shell_integration.zsh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = codeedit_shell_integration.zsh; sourceTree = ""; }; 3E0196792A392B45002648D8 /* codeedit_shell_integration.bash */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = codeedit_shell_integration.bash; sourceTree = ""; }; 474397C42893AC4B00518C8C /* codeedit-midnight.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "codeedit-midnight.json"; sourceTree = ""; }; @@ -1070,6 +1072,7 @@ 201169D52837B29600F92B46 /* SourceControlNavigator */, 287776E627E3413200D46668 /* NavigatorSidebarView.swift */, 6CE6226D2A2A1CDE0013085C /* NavigatorTab.swift */, + 30E6D0002A6E505200A58B20 /* NavigatorSidebarViewModel.swift */, ); path = NavigatorSidebar; sourceTree = ""; @@ -2972,6 +2975,7 @@ 6C6BD6F629CD145F00235D17 /* ExtensionInfo.swift in Sources */, 58F2EB05292FB2B0004A9BDE /* Settings.swift in Sources */, 6CBD1BC62978DE53006639D5 /* Font+Caption3.swift in Sources */, + 30E6D0012A6E505200A58B20 /* NavigatorSidebarViewModel.swift in Sources */, B6E41C9429DEAE260088F9F4 /* SourceControlAccount.swift in Sources */, 2806E9022979588B000040F4 /* Contributor.swift in Sources */, 58D01C98293167DC00C5B6B4 /* String+RemoveOccurrences.swift in Sources */, diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift index 6a5c95eed..59c9852cd 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift @@ -19,6 +19,7 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs var workspace: WorkspaceDocument? var quickOpenPanel: OverlayPanel? var commandPalettePanel: OverlayPanel? + var navigatorSidebarViewModel: NavigatorSidebarViewModel? var splitViewController: NSSplitViewController! @@ -85,14 +86,18 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs let feedbackPerformer = NSHapticFeedbackManager.defaultPerformer let splitVC = CodeEditSplitViewController(workspace: workspace, feedbackPerformer: feedbackPerformer) - let navigatorView = SettingsInjector { - NavigatorSidebarView(workspace: workspace) + let navigatorViewModel = NavigatorSidebarViewModel() + navigatorSidebarViewModel = navigatorViewModel + + let settingsView = SettingsInjector { + NavigatorSidebarView(workspace: workspace, viewModel: navigatorViewModel) .environmentObject(workspace) .environmentObject(workspace.tabManager) + .environmentObject(navigatorViewModel) } let navigator = NSSplitViewItem( - sidebarWithViewController: NSHostingController(rootView: navigatorView) + sidebarWithViewController: NSHostingController(rootView: settingsView) ) navigator.titlebarSeparatorStyle = .none navigator.minimumThickness = Self.minSidebarWidth diff --git a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift index 9d81d244f..a8e918383 100644 --- a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift +++ b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift @@ -9,16 +9,15 @@ import SwiftUI struct NavigatorSidebarView: View { @ObservedObject private var workspace: WorkspaceDocument - @ObservedObject private var extensionManager = ExtensionManager.shared + @ObservedObject public var viewModel: NavigatorSidebarViewModel @AppSettings(\.general.navigatorTabBarPosition) var sidebarPosition: SettingsData.SidebarTabBarPosition - @State private var selection: NavigatorTab? = .project - - init(workspace: WorkspaceDocument) { + init(workspace: WorkspaceDocument, viewModel: NavigatorSidebarViewModel) { self.workspace = workspace + self.viewModel = viewModel } private var items: [NavigatorTab] { @@ -38,7 +37,7 @@ struct NavigatorSidebarView: View { var body: some View { VStack { - if let selection { + if let selection = viewModel.selectedTab { selection } else { NoSelectionInspectorView() @@ -47,7 +46,7 @@ struct NavigatorSidebarView: View { .safeAreaInset(edge: .leading, spacing: 0) { if sidebarPosition == .side { HStack(spacing: 0) { - AreaTabBar(items: items, selection: $selection, position: sidebarPosition) + AreaTabBar(items: items, selection: $viewModel.selectedTab, position: sidebarPosition) Divider() } } @@ -56,7 +55,7 @@ struct NavigatorSidebarView: View { if sidebarPosition == .top { VStack(spacing: 0) { Divider() - AreaTabBar(items: items, selection: $selection, position: sidebarPosition) + AreaTabBar(items: items, selection: $viewModel.selectedTab, position: sidebarPosition) Divider() } } else { diff --git a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift new file mode 100644 index 000000000..24ec92751 --- /dev/null +++ b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift @@ -0,0 +1,16 @@ +// +// NavigatorSidebarViewModel.swift +// CodeEdit +// +// Created by Abe Malla on 7/23/23. +// + +import Foundation + +class NavigatorSidebarViewModel: ObservableObject { + @Published var selectedTab: NavigatorTab? = .project + + func setNavigatorTab(tab newTab: NavigatorTab) { + selectedTab = newTab + } +} diff --git a/CodeEdit/Features/WindowCommands/ViewCommands.swift b/CodeEdit/Features/WindowCommands/ViewCommands.swift index 6bfe0a60e..8376901f2 100644 --- a/CodeEdit/Features/WindowCommands/ViewCommands.swift +++ b/CodeEdit/Features/WindowCommands/ViewCommands.swift @@ -88,7 +88,6 @@ struct ViewCommands: Commands { } .disabled(inspectorVisibility == nil) .keyboardShortcut("i", modifiers: [.control, .command]) - } else { Button("\(navigatorCollapsed ? "Show" : "Hide") Navigator") { windowController?.toggleFirstPanel() @@ -105,6 +104,28 @@ struct ViewCommands: Commands { .disabled(windowController == nil) .keyboardShortcut("i", modifiers: [.control, .command]) } + + Divider() + + Menu("Navigators") { + Button("Project") { +// windowController?.navigatorSidebarView?.setNavigatorTab(tab:.project) +// print(windowController?.navigatorSidebarView as Any) + + windowController?.navigatorSidebarViewModel?.setNavigatorTab(tab: .project) + print(windowController) + print(windowController?.navigatorSidebarViewModel) + } + .keyboardShortcut("1") + Button("Source Control") { + windowController?.navigatorSidebarViewModel?.setNavigatorTab(tab: .sourceControl) + } + .keyboardShortcut("2") + Button("Find") { + windowController?.navigatorSidebarViewModel?.setNavigatorTab(tab: .search) + } + .keyboardShortcut("4") + } } } } diff --git a/CodeEdit/WindowSplitView.swift b/CodeEdit/WindowSplitView.swift index ea6d3993e..30abceb26 100644 --- a/CodeEdit/WindowSplitView.swift +++ b/CodeEdit/WindowSplitView.swift @@ -9,6 +9,7 @@ import SwiftUI struct WindowSplitView: View { @StateObject var workspace: WorkspaceDocument + @StateObject var navigatorViewModel = NavigatorSidebarViewModel() @State var visibility: NavigationSplitViewVisibility = .all @State var showInspector = true @State var window: NSWindow = .init() @@ -16,7 +17,7 @@ struct WindowSplitView: View { var body: some View { WindowObserver(window: window) { NavigationSplitView(columnVisibility: $visibility) { - NavigatorSidebarView(workspace: workspace) + NavigatorSidebarView(workspace: workspace, viewModel: navigatorViewModel) .toolbar { ToolbarItem { Button { From 85e31a7689a1ea8333ed4d6bb2a74d05a327cf30 Mon Sep 17 00:00:00 2001 From: Abe M Date: Mon, 24 Jul 2023 00:08:19 -0700 Subject: [PATCH 07/20] Remove old shortcut --- .../Features/Tabs/Views/TabBarItemView.swift | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift index bb51f3e48..fb5dacaa5 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift @@ -159,23 +159,6 @@ struct TabBarItemView: View { .padding(.horizontal, tabBarStyle == .native ? 28 : 23) .overlay { ZStack { - // Switch Tab Shortcut: - // Using an invisible button to contain the keyboard shortcut is simply - // because the keyboard shortcut has an unexpected bug when working with - // custom buttonStyle. This is an workaround and it works as expected. - if index < 10 { - Button( - action: switchAction, - label: { EmptyView() } - ) - .frame(width: 0, height: 0) - .keyboardShortcut( - KeyEquivalent(Character(String(index))), - modifiers: [.command] - ) - .opacity(0) - .allowsHitTesting(false) - } // Close Button TabBarItemCloseButton( isActive: isActive, From 395e92215c2248d599e2143e1678e70503d3cc8b Mon Sep 17 00:00:00 2001 From: Abe M Date: Mon, 24 Jul 2023 00:10:24 -0700 Subject: [PATCH 08/20] Remove old code --- CodeEdit/Features/WindowCommands/ViewCommands.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CodeEdit/Features/WindowCommands/ViewCommands.swift b/CodeEdit/Features/WindowCommands/ViewCommands.swift index 8376901f2..7dfa5ba36 100644 --- a/CodeEdit/Features/WindowCommands/ViewCommands.swift +++ b/CodeEdit/Features/WindowCommands/ViewCommands.swift @@ -109,12 +109,7 @@ struct ViewCommands: Commands { Menu("Navigators") { Button("Project") { -// windowController?.navigatorSidebarView?.setNavigatorTab(tab:.project) -// print(windowController?.navigatorSidebarView as Any) - windowController?.navigatorSidebarViewModel?.setNavigatorTab(tab: .project) - print(windowController) - print(windowController?.navigatorSidebarViewModel) } .keyboardShortcut("1") Button("Source Control") { From 3a042b8edf1f026b62531080f5b94268f5ec6032 Mon Sep 17 00:00:00 2001 From: Abe M Date: Mon, 24 Jul 2023 03:25:09 -0700 Subject: [PATCH 09/20] Small refactor --- .../Documents/Controllers/CodeEditWindowController.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift index 59c9852cd..dde46f79c 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift @@ -88,12 +88,11 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs let navigatorViewModel = NavigatorSidebarViewModel() navigatorSidebarViewModel = navigatorViewModel - + let settingsView = SettingsInjector { NavigatorSidebarView(workspace: workspace, viewModel: navigatorViewModel) .environmentObject(workspace) .environmentObject(workspace.tabManager) - .environmentObject(navigatorViewModel) } let navigator = NSSplitViewItem( From d9f514e94f704aea58cd7e0643594a946fe100d7 Mon Sep 17 00:00:00 2001 From: Abe M Date: Sat, 26 Aug 2023 19:50:52 -0700 Subject: [PATCH 10/20] New drag gestures, refactors --- .../CodeEditUI/Views/AreaTabBar.swift | 241 ++++++++++++------ .../NavigatorSidebarView.swift | 8 +- .../NavigatorSidebarViewModel.swift | 3 +- .../Tabs/Views/TabBarItemCloseButton.swift | 2 + .../WindowCommands/ViewCommands.swift | 29 +-- 5 files changed, 186 insertions(+), 97 deletions(-) diff --git a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift index 79d0b84e4..a8c49959e 100644 --- a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift +++ b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift @@ -16,15 +16,31 @@ struct AreaTabBar: View { @Environment(\.controlActiveState) private var activeState - var items: [Tab] + @State var items: [Tab] @Binding var selection: Tab? var position: SettingsData.SidebarTabBarPosition - @State private var hasChangedLocation: Bool = false - @State private var draggingItem: SidebarDockIcon? - @State private var drugItemLocation: CGPoint? + @State private var tabLocations: [Tab: CGRect] = [:] + @State private var tabWidth: [Tab: CGFloat] = [:] + @State private var tabOffsets: [Tab: CGFloat] = [:] + + /// The tab currently being dragged. + /// + /// It will be `nil` when there is no tab dragged currently. + @State private var draggingTab: Tab? + + /// The start location of dragging. + /// + /// When there is no tab being dragged, it will be `nil`. + @State private var draggingStartLocation: CGFloat? + + /// The last location of dragging. + /// + /// This is used to determine the dragging direction. + /// - TODO: Check if I can use `value.translation` instead. + @State private var draggingLastLocation: CGFloat? var body: some View { if position == .top { @@ -63,19 +79,12 @@ struct AreaTabBar: View { layout { ForEach(items) { icon in makeIcon(tab: icon, size: size) - .opacity(draggingItem?.imageName == icon.systemImage && - hasChangedLocation && - drugItemLocation != nil ? 0.0 : 1.0) - // .onDrop( - // of: [.utf8PlainText], - // delegate: InspectorSidebarDockIconDelegate( - // item: icon, - // current: $draggingItem, - // icons: $icons, - // hasChangedLocation: $hasChangedLocation, - // drugItemLocation: $drugItemLocation - // ) - // ) + .offset( + x: (position == .top) ? (tabOffsets[icon] ?? 0) : 0, + y: (position == .side) ? (tabOffsets[icon] ?? 0) : 0 + ) + .background(makeTabItemGeometryReader(tab: icon)) + .simultaneousGesture(makeAreaTabDragGesture(tab: icon)) } if position == .side { Spacer() @@ -100,85 +109,165 @@ struct AreaTabBar: View { alignment: .center ) .help(tab.title) - // .onDrag { - // if let index = icons.firstIndex(where: { $0.imageName == named }) { - // draggingItem = icons[index] - // } - // return .init(object: NSString(string: named)) - // } preview: { - // RoundedRectangle(cornerRadius: .zero) - // .frame(width: .zero) - // } } .buttonStyle(.icon(isActive: tab == selection, size: nil)) } - private func getSafeImage(named: String, accessibilityDescription: String?) -> Image { - // We still use the NSImage init to check if a symbol with the name exists. - if NSImage(systemSymbolName: named, accessibilityDescription: nil) != nil { - return Image(systemName: named) - } else { - return Image(symbol: named) - } - } + private func makeAreaTabDragGesture(tab: Tab) -> some Gesture { + DragGesture(minimumDistance: 2, coordinateSpace: .global) + .onChanged({ value in + if draggingTab != tab { + initializeDragGesture(value: value, for: tab) + } - struct NavigatorToolbarButtonStyle: ButtonStyle { - var id: Int - var selection: Int - var activeState: ControlActiveState - var sidebarWidth: CGFloat + // Get the current cursor location + let currentLocation = (position == .top) ? value.location.x : value.location.y + guard let startLocation = draggingStartLocation, + let currentIndex = items.firstIndex(of: tab), + let currentTabWidth = tabWidth[tab], + let lastLocation = draggingLastLocation + else { return } - func makeBody(configuration: Configuration) -> some View { - configuration.label - .foregroundColor(id == selection ? .accentColor : configuration.isPressed ? .primary : .secondary) - } + let dragDifference = currentLocation - lastLocation + let previousIndex = currentIndex > 0 ? currentIndex - 1 : nil + let nextIndex = currentIndex < items.count - 1 ? currentIndex + 1 : nil + + tabOffsets[tab] = currentLocation - startLocation + + swapWithPreviousTab( + tab: tab, + currentIndex: currentIndex, + currentLocation: currentLocation, + dragDifference: dragDifference, + currentTabWidth: currentTabWidth + ) + swapWithNextTab( + tab: tab, + currentIndex: currentIndex, + currentLocation: currentLocation, + dragDifference: dragDifference, + currentTabWidth: currentTabWidth + ) + + // Update the last dragging location if there's enough offset + let currentLocationOnAxis = ((position == .top) ? value.location.x : value.location.y) + if draggingLastLocation == nil || abs(currentLocationOnAxis - draggingLastLocation!) >= 10 { + draggingLastLocation = (position == .top) ? value.location.x : value.location.y + } + }) + .onEnded({ _ in + draggingStartLocation = nil + draggingLastLocation = nil + withAnimation(.easeInOut(duration: 0.25)) { + tabOffsets = [:] + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { + draggingTab = nil + } + }) } - private struct SidebarDockIcon: Identifiable, Equatable { - let imageName: String - let title: String - var id: Int - var disabled: Bool = false + private func initializeDragGesture(value: DragGesture.Value, for tab: Tab) { + draggingTab = tab + let initialLocation = position == .top ? value.startLocation.x : value.startLocation.y + draggingStartLocation = initialLocation + draggingLastLocation = initialLocation } - private struct NavigatorSidebarDockIconDelegate: DropDelegate { - let item: SidebarDockIcon - @Binding var current: SidebarDockIcon? - @Binding var icons: [SidebarDockIcon] - @Binding var hasChangedLocation: Bool - @Binding var drugItemLocation: CGPoint? - - func dropEntered(info: DropInfo) { - if current == nil { - current = item - drugItemLocation = info.location - } + private func swapWithPreviousTab( + tab: Tab, currentIndex: Int, currentLocation: CGFloat, dragDifference: CGFloat, currentTabWidth: CGFloat + ) { + guard let previousIndex = currentIndex > 0 ? currentIndex - 1 : nil, + dragDifference < 0 else { return } - guard item != current, let current = current, - let from = icons.firstIndex(of: current), - let toIndex = icons.firstIndex(of: item) else { return } + let previousTab = items[previousIndex] + guard let previousTabLocation = tabLocations[previousTab], + let previousTabWidth = tabWidth[previousTab] + else { return } - hasChangedLocation = true - drugItemLocation = info.location + var isWithinBounds = false - if icons[toIndex] != current { - icons.move(fromOffsets: IndexSet(integer: from), toOffset: toIndex > from ? toIndex + 1 : toIndex) + if position == .top { + isWithinBounds = currentLocation < max( + previousTabLocation.maxX - previousTabWidth * 0.1, + previousTabLocation.minX + previousTabWidth * 0.9 + ) + } else { + isWithinBounds = currentLocation < max( + previousTabLocation.maxY - previousTabWidth * 0.1, + previousTabLocation.minY + previousTabWidth * 0.9 + ) + } + + if isWithinBounds { + let changing = previousTabWidth - 1 + draggingStartLocation! -= changing + withAnimation { + tabOffsets[tab]! += changing + items.swapAt(currentIndex, previousIndex) } + return } + } + + private func swapWithNextTab( + tab: Tab, currentIndex: Int, currentLocation: CGFloat, dragDifference: CGFloat, currentTabWidth: CGFloat + ) { + guard let nextIndex = currentIndex < items.count - 1 ? currentIndex + 1 : nil, + dragDifference > 0 else { return } + + let nextTab = items[nextIndex] + guard let nextTabLocation = tabLocations[nextTab], + let nextTabWidth = tabWidth[nextTab] + else { return } - func dropExited(info: DropInfo) { - drugItemLocation = nil + var isWithinBounds = false + + if position == .top { + isWithinBounds = currentLocation > min( + nextTabLocation.minX + nextTabWidth * 0.1, + nextTabLocation.maxX - currentTabWidth * 0.9 + ) + } else { + isWithinBounds = currentLocation > min( + nextTabLocation.minY + nextTabWidth * 0.1, + nextTabLocation.maxY - currentTabWidth * 0.9 + ) } - func dropUpdated(info: DropInfo) -> DropProposal? { - DropProposal(operation: .move) + if isWithinBounds { + let changing = nextTabWidth - 1 + draggingStartLocation! += changing + withAnimation { + tabOffsets[tab]! -= changing + items.swapAt(currentIndex, nextIndex) + } } + } - func performDrop(info: DropInfo) -> Bool { - hasChangedLocation = false - drugItemLocation = nil - current = nil - return true + private func makeTabItemGeometryReader(tab: Tab) -> some View { + GeometryReader { geometry in + Rectangle() + .foregroundColor(.clear) + .onAppear { + self.tabWidth[tab] = geometry.size.width + self.tabLocations[tab] = geometry.frame(in: .global) + } + .onChange(of: geometry.frame(in: .global)) { newFrame in + self.tabLocations[tab] = newFrame + } + .onChange(of: geometry.size.width) { newWidth in + self.tabWidth[tab] = newWidth + } + } + } + + private func getSafeImage(named: String, accessibilityDescription: String?) -> Image { + // We still use the NSImage init to check if a symbol with the name exists. + if NSImage(systemSymbolName: named, accessibilityDescription: nil) != nil { + return Image(systemName: named) + } else { + return Image(symbol: named) } } } diff --git a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift index a8e918383..74d4265d2 100644 --- a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift +++ b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift @@ -18,10 +18,8 @@ struct NavigatorSidebarView: View { init(workspace: WorkspaceDocument, viewModel: NavigatorSidebarViewModel) { self.workspace = workspace self.viewModel = viewModel - } - private var items: [NavigatorTab] { - [.project, .sourceControl, .search] + + viewModel.items = [.project, .sourceControl, .search] + extensionManager .extensions .map { ext in @@ -46,7 +44,7 @@ struct NavigatorSidebarView: View { .safeAreaInset(edge: .leading, spacing: 0) { if sidebarPosition == .side { HStack(spacing: 0) { - AreaTabBar(items: items, selection: $viewModel.selectedTab, position: sidebarPosition) + AreaTabBar(items: viewModel.items, selection: $viewModel.selectedTab, position: sidebarPosition) Divider() } } @@ -55,7 +53,7 @@ struct NavigatorSidebarView: View { if sidebarPosition == .top { VStack(spacing: 0) { Divider() - AreaTabBar(items: items, selection: $viewModel.selectedTab, position: sidebarPosition) + AreaTabBar(items: viewModel.items, selection: $viewModel.selectedTab, position: sidebarPosition) Divider() } } else { diff --git a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift index 24ec92751..5d9c89f9e 100644 --- a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift +++ b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarViewModel.swift @@ -9,7 +9,8 @@ import Foundation class NavigatorSidebarViewModel: ObservableObject { @Published var selectedTab: NavigatorTab? = .project - + var items: [NavigatorTab] = [] + func setNavigatorTab(tab newTab: NavigatorTab) { selectedTab = newTab } diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift b/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift index ff3ccbf71..0c364bae3 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift @@ -70,6 +70,8 @@ struct TabBarItemCloseButton: View { closeButtonGestureActive = true }) .onEnded({ value in + // If the final position of the mouse is within the bounds of the + // close button then close the tab if value.location.x > 0 && value.location.x < buttonSize && value.location.y > 0 diff --git a/CodeEdit/Features/WindowCommands/ViewCommands.swift b/CodeEdit/Features/WindowCommands/ViewCommands.swift index 7dfa5ba36..35dbdae5a 100644 --- a/CodeEdit/Features/WindowCommands/ViewCommands.swift +++ b/CodeEdit/Features/WindowCommands/ViewCommands.swift @@ -105,21 +105,20 @@ struct ViewCommands: Commands { .keyboardShortcut("i", modifiers: [.control, .command]) } - Divider() - - Menu("Navigators") { - Button("Project") { - windowController?.navigatorSidebarViewModel?.setNavigatorTab(tab: .project) - } - .keyboardShortcut("1") - Button("Source Control") { - windowController?.navigatorSidebarViewModel?.setNavigatorTab(tab: .sourceControl) - } - .keyboardShortcut("2") - Button("Find") { - windowController?.navigatorSidebarViewModel?.setNavigatorTab(tab: .search) - } - .keyboardShortcut("4") + if let windowController = windowController { + Divider() + + Menu("Navigators", content: { + ForEach( + Array((windowController.navigatorSidebarViewModel?.items ?? []).enumerated()), + id: \.element + ) { index, tab in + Button(tab.title, action: { + windowController.navigatorSidebarViewModel?.setNavigatorTab(tab: tab) + }) + .keyboardShortcut(KeyEquivalent(Character(String(index + 1)))) + } + }) } } } From b2f5634b1f397c7d4c3f7c87f748ff8e6f648b75 Mon Sep 17 00:00:00 2001 From: Abe M Date: Sat, 26 Aug 2023 21:59:42 -0700 Subject: [PATCH 11/20] Cleanup and added comments --- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index dd1132d1e..f3018a654 100644 --- a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -158,8 +158,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ChimeHQ/Rearrange", "state" : { - "revision" : "0fb658e721c68495f6340c211cc6d4719e6b52d8", - "version" : "1.6.0" + "revision" : "8f97f721d8a08c6e01ab9f7460e53819bef72dfa", + "version" : "1.5.3" } }, { diff --git a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift index a8c49959e..5a3b64fac 100644 --- a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift +++ b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift @@ -129,11 +129,9 @@ struct AreaTabBar: View { else { return } let dragDifference = currentLocation - lastLocation - let previousIndex = currentIndex > 0 ? currentIndex - 1 : nil - let nextIndex = currentIndex < items.count - 1 ? currentIndex + 1 : nil - tabOffsets[tab] = currentLocation - startLocation + // Check for swaps between adjacent tabs swapWithPreviousTab( tab: tab, currentIndex: currentIndex, @@ -180,13 +178,14 @@ struct AreaTabBar: View { guard let previousIndex = currentIndex > 0 ? currentIndex - 1 : nil, dragDifference < 0 else { return } + // Get info about the previous tab let previousTab = items[previousIndex] guard let previousTabLocation = tabLocations[previousTab], let previousTabWidth = tabWidth[previousTab] else { return } + // Did we pass the threshold to swap positions with the previous tab? var isWithinBounds = false - if position == .top { isWithinBounds = currentLocation < max( previousTabLocation.maxX - previousTabWidth * 0.1, @@ -199,6 +198,7 @@ struct AreaTabBar: View { ) } + // Swap tab positions if isWithinBounds { let changing = previousTabWidth - 1 draggingStartLocation! -= changing @@ -216,13 +216,14 @@ struct AreaTabBar: View { guard let nextIndex = currentIndex < items.count - 1 ? currentIndex + 1 : nil, dragDifference > 0 else { return } + // Get info about the next tab let nextTab = items[nextIndex] guard let nextTabLocation = tabLocations[nextTab], let nextTabWidth = tabWidth[nextTab] else { return } + // Did we pass the threshold to swap positions with the next tab? var isWithinBounds = false - if position == .top { isWithinBounds = currentLocation > min( nextTabLocation.minX + nextTabWidth * 0.1, @@ -235,6 +236,7 @@ struct AreaTabBar: View { ) } + // Swap tab positions if isWithinBounds { let changing = nextTabWidth - 1 draggingStartLocation! += changing From ecb34c68a25f90abdf3a945f5b29b6865525cc29 Mon Sep 17 00:00:00 2001 From: Abe M Date: Tue, 29 Aug 2023 20:23:49 -0700 Subject: [PATCH 12/20] Removed file --- CodeEdit/Components/ListTable.swift | 88 ----------------------------- 1 file changed, 88 deletions(-) delete mode 100644 CodeEdit/Components/ListTable.swift diff --git a/CodeEdit/Components/ListTable.swift b/CodeEdit/Components/ListTable.swift deleted file mode 100644 index 63190c1b1..000000000 --- a/CodeEdit/Components/ListTable.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// ListTable.swift -// CodeEdit -// -// Created by Abe Malla on 8/26/23. -// - -import SwiftUI - -struct Item: Identifiable { - let id = UUID() - var name: String -} - -struct ContentView: View { - @State private var items = [Item]() - @State private var showingModal = false - @State private var selection: Item.ID? - - var body: some View { - VStack { - Table(items, selection: $selection) { - TableColumn("Items", value: \.name) - } - } - .paneToolbar { - HStack(spacing: 2) { - Button { - showingModal = true - } label: { - Image(systemName: "plus") - } - - Divider() - .frame(minHeight: 15) - - Button { - removeItem() - } label: { - Image(systemName: "minus") - } - .disabled(selection == nil) - .opacity(selection == nil ? 0.5 : 1) - } - Spacer() - } - .sheet(isPresented: $showingModal) { - NewItemView { text in - items.append(Item(name: text)) - showingModal = false - } - } - .cornerRadius(6) - } - - private func removeItem() { - if let selectedId = selection { - items.removeAll(where: { $0.id == selectedId }) - selection = nil - } - } -} - -struct NewItemView: View { - @State private var text = "" - var completion: (String) -> Void - - var body: some View { - VStack { - Text("Enter new item name") - TextField("Name", text: $text) - .textFieldStyle(.roundedBorder) - Button("Add") { - if !text.isEmpty { - completion(text) - } - } - } - .padding() - } -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - .environmentObject(DebugAreaTabViewModel()) - } -} From 02703c0da39fb53adf279378bc66540d301017a9 Mon Sep 17 00:00:00 2001 From: Abe M Date: Tue, 29 Aug 2023 21:10:18 -0700 Subject: [PATCH 13/20] Fixed variable bindings --- CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift | 3 +-- CodeEdit/Features/DebugArea/DebugAreaView.swift | 2 +- .../DebugArea/ViewModels/DebugAreaViewModel.swift | 3 +++ .../InspectorSidebar/InspectorSidebarView.swift | 13 +++++-------- .../NavigatorSidebar/NavigatorSidebarView.swift | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift index 5a3b64fac..3fe4afe87 100644 --- a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift +++ b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift @@ -16,8 +16,7 @@ struct AreaTabBar: View { @Environment(\.controlActiveState) private var activeState - @State var items: [Tab] - + @Binding var items: [Tab] @Binding var selection: Tab? var position: SettingsData.SidebarTabBarPosition diff --git a/CodeEdit/Features/DebugArea/DebugAreaView.swift b/CodeEdit/Features/DebugArea/DebugAreaView.swift index be9c2f8e1..65db917bf 100644 --- a/CodeEdit/Features/DebugArea/DebugAreaView.swift +++ b/CodeEdit/Features/DebugArea/DebugAreaView.swift @@ -26,7 +26,7 @@ struct DebugAreaView: View { } .safeAreaInset(edge: .leading, spacing: 0) { HStack(spacing: 0) { - AreaTabBar(items: DebugAreaTab.allCases, selection: $selection, position: .side) + AreaTabBar(items: $model.tabItems, selection: $selection, position: .side) Divider() .overlay(Color(nsColor: colorScheme == .dark ? .black : .clear)) } diff --git a/CodeEdit/Features/DebugArea/ViewModels/DebugAreaViewModel.swift b/CodeEdit/Features/DebugArea/ViewModels/DebugAreaViewModel.swift index 5d6d095ee..f8b39f955 100644 --- a/CodeEdit/Features/DebugArea/ViewModels/DebugAreaViewModel.swift +++ b/CodeEdit/Features/DebugArea/ViewModels/DebugAreaViewModel.swift @@ -37,6 +37,9 @@ class DebugAreaViewModel: ObservableObject { /// Search value to filter in drawer @Published var searchText: String = "" + /// The tab bar items for the DebugAreaView + @Published var tabItems: [DebugAreaTab] = DebugAreaTab.allCases + /// Returns the font for status bar items to use private(set) var toolbarFont: Font = .system(size: 11, weight: .medium) diff --git a/CodeEdit/Features/InspectorSidebar/InspectorSidebarView.swift b/CodeEdit/Features/InspectorSidebar/InspectorSidebarView.swift index 004efd39e..2137b0c88 100644 --- a/CodeEdit/Features/InspectorSidebar/InspectorSidebarView.swift +++ b/CodeEdit/Features/InspectorSidebar/InspectorSidebarView.swift @@ -18,13 +18,10 @@ struct InspectorSidebarView: View { var sidebarPosition: SettingsData.SidebarTabBarPosition @State private var selection: InspectorTab? = .file + @State private var items: [InspectorTab] = [.file, .gitHistory] - private var items: [InspectorTab] { - [ - .file, - .gitHistory - ] - + extensionManager + init() { + items += extensionManager .extensions .map { ext in ext.availableFeatures.compactMap { @@ -63,7 +60,7 @@ struct InspectorSidebarView: View { if sidebarPosition == .side { HStack(spacing: 0) { Divider() - AreaTabBar(items: items, selection: $selection, position: sidebarPosition) + AreaTabBar(items: $items, selection: $selection, position: sidebarPosition) } } } @@ -71,7 +68,7 @@ struct InspectorSidebarView: View { if sidebarPosition == .top { VStack(spacing: 0) { Divider() - AreaTabBar(items: items, selection: $selection, position: sidebarPosition) + AreaTabBar(items: $items, selection: $selection, position: sidebarPosition) Divider() } } else { diff --git a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift index 74d4265d2..83327a424 100644 --- a/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift +++ b/CodeEdit/Features/NavigatorSidebar/NavigatorSidebarView.swift @@ -44,7 +44,7 @@ struct NavigatorSidebarView: View { .safeAreaInset(edge: .leading, spacing: 0) { if sidebarPosition == .side { HStack(spacing: 0) { - AreaTabBar(items: viewModel.items, selection: $viewModel.selectedTab, position: sidebarPosition) + AreaTabBar(items: $viewModel.items, selection: $viewModel.selectedTab, position: sidebarPosition) Divider() } } @@ -53,7 +53,7 @@ struct NavigatorSidebarView: View { if sidebarPosition == .top { VStack(spacing: 0) { Divider() - AreaTabBar(items: viewModel.items, selection: $viewModel.selectedTab, position: sidebarPosition) + AreaTabBar(items: $viewModel.items, selection: $viewModel.selectedTab, position: sidebarPosition) Divider() } } else { From e13e2d0d6236bcad0a0a3670755ed1e3e013df93 Mon Sep 17 00:00:00 2001 From: Abe M Date: Wed, 30 Aug 2023 13:25:46 -0700 Subject: [PATCH 14/20] Added ObservedObject to NavigatorSidebarViewModel --- .../WindowCommands/ViewCommands.swift | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/CodeEdit/Features/WindowCommands/ViewCommands.swift b/CodeEdit/Features/WindowCommands/ViewCommands.swift index 35dbdae5a..dfabc59cd 100644 --- a/CodeEdit/Features/WindowCommands/ViewCommands.swift +++ b/CodeEdit/Features/WindowCommands/ViewCommands.swift @@ -105,21 +105,27 @@ struct ViewCommands: Commands { .keyboardShortcut("i", modifiers: [.control, .command]) } - if let windowController = windowController { + if let model = windowController?.navigatorSidebarViewModel { Divider() + NavigatorCommands(model: model) + } + } + } +} - Menu("Navigators", content: { - ForEach( - Array((windowController.navigatorSidebarViewModel?.items ?? []).enumerated()), - id: \.element - ) { index, tab in - Button(tab.title, action: { - windowController.navigatorSidebarViewModel?.setNavigatorTab(tab: tab) - }) - .keyboardShortcut(KeyEquivalent(Character(String(index + 1)))) +extension ViewCommands { + struct NavigatorCommands: View { + @ObservedObject var model: NavigatorSidebarViewModel + + var body: some View { + Menu("Navigators", content: { + ForEach(Array(model.items.enumerated()), id: \.element) { index, tab in + Button(tab.title) { + model.setNavigatorTab(tab: tab) } - }) - } + .keyboardShortcut(KeyEquivalent(Character(String(index + 1)))) + } + }) } } } From 89395a9a9599f05a83dbc9335c251047ce1fb25f Mon Sep 17 00:00:00 2001 From: Abe M Date: Wed, 30 Aug 2023 13:59:40 -0700 Subject: [PATCH 15/20] Limit index shortcuts to 9 --- CodeEdit/Features/WindowCommands/ViewCommands.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeEdit/Features/WindowCommands/ViewCommands.swift b/CodeEdit/Features/WindowCommands/ViewCommands.swift index dfabc59cd..8ec564047 100644 --- a/CodeEdit/Features/WindowCommands/ViewCommands.swift +++ b/CodeEdit/Features/WindowCommands/ViewCommands.swift @@ -119,7 +119,7 @@ extension ViewCommands { var body: some View { Menu("Navigators", content: { - ForEach(Array(model.items.enumerated()), id: \.element) { index, tab in + ForEach(Array(model.items.prefix(9).enumerated()), id: \.element) { index, tab in Button(tab.title) { model.setNavigatorTab(tab: tab) } From 72a7184cdc2b5e926db08158073302b192702aac Mon Sep 17 00:00:00 2001 From: Abe M Date: Fri, 8 Sep 2023 03:55:15 -0700 Subject: [PATCH 16/20] Refactors --- CodeEdit/Features/WindowCommands/ViewCommands.swift | 4 ++-- CodeEdit/WindowSplitView.swift | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CodeEdit/Features/WindowCommands/ViewCommands.swift b/CodeEdit/Features/WindowCommands/ViewCommands.swift index 8ec564047..240543090 100644 --- a/CodeEdit/Features/WindowCommands/ViewCommands.swift +++ b/CodeEdit/Features/WindowCommands/ViewCommands.swift @@ -81,7 +81,7 @@ struct ViewCommands: Commands { } } .disabled(navigationSplitViewVisibility == nil) - .keyboardShortcut("s", modifiers: [.control, .command]) + .keyboardShortcut("0", modifiers: [.command]) Button("\(inspectorVisibility == false ? "Show" : "Hide") Inspector") { inspectorVisibility?.toggle() @@ -93,7 +93,7 @@ struct ViewCommands: Commands { windowController?.toggleFirstPanel() } .disabled(windowController == nil) - .keyboardShortcut("s", modifiers: [.control, .command]) + .keyboardShortcut("0", modifiers: [.command]) .onReceive(NSApp.publisher(for: \.keyWindow)) { window in windowController = window?.windowController as? CodeEditWindowController } diff --git a/CodeEdit/WindowSplitView.swift b/CodeEdit/WindowSplitView.swift index 30abceb26..1fcc654af 100644 --- a/CodeEdit/WindowSplitView.swift +++ b/CodeEdit/WindowSplitView.swift @@ -9,7 +9,6 @@ import SwiftUI struct WindowSplitView: View { @StateObject var workspace: WorkspaceDocument - @StateObject var navigatorViewModel = NavigatorSidebarViewModel() @State var visibility: NavigationSplitViewVisibility = .all @State var showInspector = true @State var window: NSWindow = .init() @@ -17,7 +16,7 @@ struct WindowSplitView: View { var body: some View { WindowObserver(window: window) { NavigationSplitView(columnVisibility: $visibility) { - NavigatorSidebarView(workspace: workspace, viewModel: navigatorViewModel) + NavigatorSidebarView(workspace: workspace, viewModel: NavigatorSidebarViewModel()) .toolbar { ToolbarItem { Button { From 26123875bc6fe4ca8b081db7f0af68c00d1d1d61 Mon Sep 17 00:00:00 2001 From: Abe M Date: Mon, 11 Sep 2023 03:09:08 -0700 Subject: [PATCH 17/20] Refactored swapping code --- .../CodeEditUI/Views/AreaTabBar.swift | 137 ++++++++++-------- 1 file changed, 78 insertions(+), 59 deletions(-) diff --git a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift index 3fe4afe87..0bd5e77ef 100644 --- a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift +++ b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift @@ -131,19 +131,23 @@ struct AreaTabBar: View { tabOffsets[tab] = currentLocation - startLocation // Check for swaps between adjacent tabs - swapWithPreviousTab( + // Left tab + swapTab( tab: tab, currentIndex: currentIndex, currentLocation: currentLocation, dragDifference: dragDifference, - currentTabWidth: currentTabWidth + currentTabWidth: currentTabWidth, + direction: .previous ) - swapWithNextTab( + // Right tab + swapTab( tab: tab, currentIndex: currentIndex, currentLocation: currentLocation, dragDifference: dragDifference, - currentTabWidth: currentTabWidth + currentTabWidth: currentTabWidth, + direction: .next ) // Update the last dragging location if there's enough offset @@ -171,79 +175,94 @@ struct AreaTabBar: View { draggingLastLocation = initialLocation } - private func swapWithPreviousTab( - tab: Tab, currentIndex: Int, currentLocation: CGFloat, dragDifference: CGFloat, currentTabWidth: CGFloat + enum SwapDirection { + case previous + case next + } + + private func swapTab( + tab: Tab, currentIndex: Int, currentLocation: CGFloat, dragDifference: CGFloat, + currentTabWidth: CGFloat, direction: SwapDirection ) { - guard let previousIndex = currentIndex > 0 ? currentIndex - 1 : nil, - dragDifference < 0 else { return } + // Determine the index to swap with based on direction + var swapIndex: Int? + if direction == .previous { + if currentIndex > 0 { + swapIndex = currentIndex - 1 + } + } else { + if currentIndex < items.count - 1 { + swapIndex = currentIndex + 1 + } + } + + // Validate the drag direction + let isValidDragDir = (direction == .previous && dragDifference < 0) || + (direction == .next && dragDifference > 0) + guard let swapIndex = swapIndex, isValidDragDir else { return } - // Get info about the previous tab - let previousTab = items[previousIndex] - guard let previousTabLocation = tabLocations[previousTab], - let previousTabWidth = tabWidth[previousTab] + // Get info about the tab to swap with + let swapTab = items[swapIndex] + guard let swapTabLocation = tabLocations[swapTab], + let swapTabWidth = tabWidth[swapTab] else { return } - // Did we pass the threshold to swap positions with the previous tab? - var isWithinBounds = false + let isWithinBounds: Bool if position == .top { - isWithinBounds = currentLocation < max( - previousTabLocation.maxX - previousTabWidth * 0.1, - previousTabLocation.minX + previousTabWidth * 0.9 - ) + isWithinBounds = direction == .previous ? + isWithinPrevTopBounds(currentLocation, swapTabLocation, swapTabWidth) : + isWithinNextTopBounds(currentLocation, swapTabLocation, swapTabWidth, currentTabWidth) } else { - isWithinBounds = currentLocation < max( - previousTabLocation.maxY - previousTabWidth * 0.1, - previousTabLocation.minY + previousTabWidth * 0.9 - ) + isWithinBounds = direction == .previous ? + isWithinPrevBottomBounds(currentLocation, swapTabLocation, swapTabWidth) : + isWithinNextBottomBounds(currentLocation, swapTabLocation, swapTabWidth, currentTabWidth) } // Swap tab positions if isWithinBounds { - let changing = previousTabWidth - 1 - draggingStartLocation! -= changing + let changing = swapTabWidth - 1 + draggingStartLocation! += direction == .previous ? -changing : changing withAnimation { - tabOffsets[tab]! += changing - items.swapAt(currentIndex, previousIndex) + tabOffsets[tab]! += direction == .previous ? changing : -changing + items.swapAt(currentIndex, swapIndex) } - return } } - private func swapWithNextTab( - tab: Tab, currentIndex: Int, currentLocation: CGFloat, dragDifference: CGFloat, currentTabWidth: CGFloat - ) { - guard let nextIndex = currentIndex < items.count - 1 ? currentIndex + 1 : nil, - dragDifference > 0 else { return } + private func isWithinPrevTopBounds( + _ curLocation: CGFloat, _ swapLocation: CGRect, _ swapWidth: CGFloat + ) -> Bool { + return curLocation < max( + swapLocation.maxX - swapWidth * 0.1, + swapLocation.minX + swapWidth * 0.9 + ) + } - // Get info about the next tab - let nextTab = items[nextIndex] - guard let nextTabLocation = tabLocations[nextTab], - let nextTabWidth = tabWidth[nextTab] - else { return } + private func isWithinNextTopBounds( + _ curLocation: CGFloat, _ swapLocation: CGRect, _ swapWidth: CGFloat, _ curWidth: CGFloat + ) -> Bool { + return curLocation > min( + swapLocation.minX + swapWidth * 0.1, + swapLocation.maxX - curWidth * 0.9 + ) + } - // Did we pass the threshold to swap positions with the next tab? - var isWithinBounds = false - if position == .top { - isWithinBounds = currentLocation > min( - nextTabLocation.minX + nextTabWidth * 0.1, - nextTabLocation.maxX - currentTabWidth * 0.9 - ) - } else { - isWithinBounds = currentLocation > min( - nextTabLocation.minY + nextTabWidth * 0.1, - nextTabLocation.maxY - currentTabWidth * 0.9 - ) - } + private func isWithinPrevBottomBounds( + _ curLocation: CGFloat, _ swapLocation: CGRect, _ swapWidth: CGFloat + ) -> Bool { + return curLocation < max( + swapLocation.maxY - swapWidth * 0.1, + swapLocation.minY + swapWidth * 0.9 + ) + } - // Swap tab positions - if isWithinBounds { - let changing = nextTabWidth - 1 - draggingStartLocation! += changing - withAnimation { - tabOffsets[tab]! -= changing - items.swapAt(currentIndex, nextIndex) - } - } + private func isWithinNextBottomBounds( + _ curLocation: CGFloat, _ swapLocation: CGRect, _ swapWidth: CGFloat, _ curWidth: CGFloat + ) -> Bool { + return curLocation > min( + swapLocation.minY + swapWidth * 0.1, + swapLocation.maxY - curWidth * 0.9 + ) } private func makeTabItemGeometryReader(tab: Tab) -> some View { From 5ddc478578d0afdf80c7bc9105ae51094089cc70 Mon Sep 17 00:00:00 2001 From: Abe M Date: Wed, 13 Sep 2023 05:36:41 -0700 Subject: [PATCH 18/20] Fixed variable that wasnt renamed --- .../Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift index 5e5ec76e1..dfafaf4c2 100644 --- a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift +++ b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift @@ -38,7 +38,7 @@ class UtilityAreaViewModel: ObservableObject { @Published var searchText: String = "" /// The tab bar items for the DebugAreaView - @Published var tabItems: [DebugAreaTab] = DebugAreaTab.allCases + @Published var tabItems: [UtilityAreaTab] = UtilityAreaTab.allCases /// Returns the font for status bar items to use private(set) var toolbarFont: Font = .system(size: 11, weight: .medium) From 8a7815b7fa0d7d9bb9c55e6887ba41ffb7fc975f Mon Sep 17 00:00:00 2001 From: Abe M Date: Thu, 14 Sep 2023 17:17:42 -0700 Subject: [PATCH 19/20] Lint fixes --- .../CEWorkspace/Models/DirectoryEventStream.swift | 2 +- CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift | 10 ++++++++-- .../UtilityArea/ViewModels/UtilityAreaViewModel.swift | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CodeEdit/Features/CEWorkspace/Models/DirectoryEventStream.swift b/CodeEdit/Features/CEWorkspace/Models/DirectoryEventStream.swift index e6fbbb888..514718677 100644 --- a/CodeEdit/Features/CEWorkspace/Models/DirectoryEventStream.swift +++ b/CodeEdit/Features/CEWorkspace/Models/DirectoryEventStream.swift @@ -111,7 +111,7 @@ class DirectoryEventStream { _ eventFlags: UnsafePointer, _ eventIds: UnsafePointer ) { - var eventPaths = eventPaths.bindMemory(to: UnsafePointer.self, capacity: numEvents) + let eventPaths = eventPaths.bindMemory(to: UnsafePointer.self, capacity: numEvents) for idx in 0..: View { case next } + // swiftlint: disable function_parameter_count private func swapTab( - tab: Tab, currentIndex: Int, currentLocation: CGFloat, dragDifference: CGFloat, - currentTabWidth: CGFloat, direction: SwapDirection + tab: Tab, + currentIndex: Int, + currentLocation: CGFloat, + dragDifference: CGFloat, + currentTabWidth: CGFloat, + direction: SwapDirection ) { // Determine the index to swap with based on direction var swapIndex: Int? @@ -231,6 +236,7 @@ struct AreaTabBar: View { } } } + // swiftlint: enable function_parameter_count private func isWithinPrevTopBounds( _ curLocation: CGFloat, _ swapLocation: CGRect, _ swapWidth: CGFloat diff --git a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift index dfafaf4c2..efb6d97b5 100644 --- a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift +++ b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift @@ -39,7 +39,7 @@ class UtilityAreaViewModel: ObservableObject { /// The tab bar items for the DebugAreaView @Published var tabItems: [UtilityAreaTab] = UtilityAreaTab.allCases - + /// Returns the font for status bar items to use private(set) var toolbarFont: Font = .system(size: 11, weight: .medium) From 40f6356960176837a450f8467c621d47f10f9ddb Mon Sep 17 00:00:00 2001 From: Abe M Date: Thu, 14 Sep 2023 17:19:37 -0700 Subject: [PATCH 20/20] Remove comment --- CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift index 8cf04ae7a..de4217537 100644 --- a/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift +++ b/CodeEdit/Features/CodeEditUI/Views/AreaTabBar.swift @@ -83,10 +83,6 @@ struct AreaTabBar: View { y: (position == .side) ? (tabOffsets[icon] ?? 0) : 0 ) .background(makeTabItemGeometryReader(tab: icon)) - .simultaneousGesture(makeAreaTabDragGesture(tab: icon)) -// .opacity(draggingItem?.imageName == icon.systemImage && -// hasChangedLocation && -// drugItemLocation != nil ? 0.0 : 1.0) } if position == .side { Spacer()