From 9e3720d8365e36afa844609518e58c7aaf3c4446 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 29 Mar 2022 14:07:24 +0200 Subject: [PATCH 1/4] started implementation of split view --- .../Documents/CodeEditWindowController.swift | 157 +++++++++++++++++- CodeEdit/Documents/WorkspaceDocument.swift | 14 +- .../InspectorSidebar/InspectorSidebar.swift | 1 + .../FindNavigator/FindNavigator.swift | 1 + .../FindNavigatorResultList.swift | 5 +- .../ProjectNavigator/ProjectNavigator.swift | 1 + CodeEdit/WorkspaceView.swift | 34 ++-- 7 files changed, 178 insertions(+), 35 deletions(-) diff --git a/CodeEdit/Documents/CodeEditWindowController.swift b/CodeEdit/Documents/CodeEditWindowController.swift index f0604eb48c..33df0a8763 100644 --- a/CodeEdit/Documents/CodeEditWindowController.swift +++ b/CodeEdit/Documents/CodeEditWindowController.swift @@ -10,10 +10,159 @@ import SwiftUI import CodeFile import Overlays -class CodeEditWindowController: NSWindowController { +class CodeEditWindowController: NSWindowController, NSToolbarDelegate { var workspace: WorkspaceDocument? var quickOpenPanel: OverlayPanel? + + init(window: NSWindow, workspace: WorkspaceDocument) { + super.init(window: window) + print("INIT") + self.workspace = workspace + + let splitVC = NSSplitViewController() + + let sidebar = NSSplitViewItem( + sidebarWithViewController: NSHostingController( + rootView: NavigatorSidebar(workspace: workspace, windowController: self) + ) + ) + sidebar.minimumThickness = 260 + splitVC.addSplitViewItem(sidebar) + + let content = NSSplitViewItem( + viewController: NSHostingController( + rootView: WorkspaceView(windowController: self, workspace: workspace) + ) + ) + splitVC.addSplitViewItem(content) + + let detail = NSSplitViewItem( + viewController: NSHostingController( + rootView: InspectorSidebar(workspace: workspace, windowController: self) + ) + ) + detail.collapseBehavior = .preferResizingSiblingsWithFixedSplitView + + splitVC.splitView.setHoldingPriority(.dragThatCannotResizeWindow, forSubviewAt: 2) + splitVC.addSplitViewItem(detail) + + self.splitViewController = splitVC + + let toolbar = NSToolbar(identifier: UUID().uuidString) + toolbar.delegate = self + toolbar.displayMode = .labelOnly + window.toolbarStyle = .unifiedCompact + window.titlebarSeparatorStyle = .none + window.toolbar = toolbar + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // Dangerous convenience alias so you can access the NSSplitViewController and manipulate it later on + private var splitViewController: NSSplitViewController! { + get { return contentViewController as? NSSplitViewController } + set { contentViewController = newValue } + } + + @objc func toggleFirstPanel() { + guard let firstSplitView = splitViewController.splitViewItems.first else { return } + firstSplitView.animator().isCollapsed.toggle() + } + + @objc func toggleLastPanel() { + guard let lastSplitView = splitViewController.splitViewItems.last else { return } + lastSplitView.animator().isCollapsed.toggle() + if lastSplitView.animator().isCollapsed { + window?.toolbar?.removeItem(at: 3) + } else { + window?.toolbar?.insertItem(withItemIdentifier: .itemListTrackingSeparator, at: 3) + } + } + + func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return [ + .toggleFirstSidebarItem, + .sidebarTrackingSeparator, + .flexibleSpace, + .itemListTrackingSeparator, + .flexibleSpace, + .toggleLastSidebarItem + ] + } + + func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return [ + .toggleFirstSidebarItem, + .sidebarTrackingSeparator, + .flexibleSpace, + .itemListTrackingSeparator, + .toggleLastSidebarItem + ] + } + + // swiftlint:disable all + func toolbar( + _ toolbar: NSToolbar, + itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, + willBeInsertedIntoToolbar flag: Bool + ) -> NSToolbarItem? { + switch itemIdentifier { + case .itemListTrackingSeparator: + if ((splitViewController) != nil) { + return NSTrackingSeparatorToolbarItem( + identifier: .itemListTrackingSeparator, + splitView: splitViewController.splitView, + dividerIndex: 1 + ) + } else { + return nil + } + case .toggleFirstSidebarItem: + let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier.toggleFirstSidebarItem) + toolbarItem.label = "Sidebar" + toolbarItem.paletteLabel = "Sidebar" + toolbarItem.toolTip = "Toggle Sidebar" + toolbarItem.isBordered = true + toolbarItem.target = self + toolbarItem.action = #selector(self.toggleFirstPanel) + toolbarItem.image = NSImage( + systemSymbolName: "sidebar.leading", + accessibilityDescription: nil + )?.withSymbolConfiguration(.init(scale: .large)) + + let menuItem = NSMenuItem() + menuItem.submenu = nil + menuItem.title = "Sidebar" + + toolbarItem.menuFormRepresentation = menuItem + return toolbarItem + case .toggleLastSidebarItem: + let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier.toggleFirstSidebarItem) + toolbarItem.label = "Sidebar" + toolbarItem.paletteLabel = "Sidebar" + toolbarItem.toolTip = "Toggle Sidebar" + toolbarItem.target = self + toolbarItem.action = #selector(self.toggleLastPanel) + toolbarItem.image = NSImage( + systemSymbolName: "sidebar.trailing", + accessibilityDescription: nil + )?.withSymbolConfiguration(.init(scale: .small)) + + let menuItem = NSMenuItem() + menuItem.submenu = nil + menuItem.title = "Sidebar" + + toolbarItem.menuFormRepresentation = menuItem + return toolbarItem + default: + return NSToolbarItem(itemIdentifier: itemIdentifier) + } + } + override func windowDidLoad() { super.windowDidLoad() @@ -57,3 +206,9 @@ class CodeEditWindowController: NSWindowController { } } } + +private extension NSToolbarItem.Identifier { + static let toggleFirstSidebarItem: NSToolbarItem.Identifier = NSToolbarItem.Identifier("ToggleFirstSidebarItem") + static let toggleLastSidebarItem: NSToolbarItem.Identifier = NSToolbarItem.Identifier("ToggleLastSidebarItem") + static let itemListTrackingSeparator = NSToolbarItem.Identifier("ItemListTrackingSeparator") +} diff --git a/CodeEdit/Documents/WorkspaceDocument.swift b/CodeEdit/Documents/WorkspaceDocument.swift index 8ca68bea4c..b1f3ac1082 100644 --- a/CodeEdit/Documents/WorkspaceDocument.swift +++ b/CodeEdit/Documents/WorkspaceDocument.swift @@ -107,15 +107,11 @@ class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { backing: .buffered, defer: false ) window.center() - window.toolbar = NSToolbar(identifier: UUID().uuidString) - window.toolbarStyle = .unifiedCompact - window.titlebarSeparatorStyle = .line - window.toolbar?.displayMode = .iconOnly - window.toolbar?.insertItem(withItemIdentifier: .toggleSidebar, at: 0) - let windowController = CodeEditWindowController(window: window) - windowController.workspace = self - let contentView = WorkspaceView(windowController: windowController, workspace: self) - window.contentView = NSHostingView(rootView: contentView) + window.minSize = .init(width: 1000, height: 600) + let windowController = CodeEditWindowController( + window: window, + workspace: self + ) self.addWindowController(windowController) } diff --git a/CodeEdit/InspectorSidebar/InspectorSidebar.swift b/CodeEdit/InspectorSidebar/InspectorSidebar.swift index 418198a71a..7b10140084 100644 --- a/CodeEdit/InspectorSidebar/InspectorSidebar.swift +++ b/CodeEdit/InspectorSidebar/InspectorSidebar.swift @@ -31,6 +31,7 @@ struct InspectorSidebar: View { } .frame( minWidth: 250, + idealWidth: 260, minHeight: 0, maxHeight: .infinity, alignment: .center diff --git a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift index 8ba0c5fb4b..cde844a28d 100644 --- a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift +++ b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift @@ -44,6 +44,7 @@ struct FindNavigator: View { Divider() FindNavigatorResultList(state: state) } + .background(.clear) .onSubmit { state.search(searchText) } diff --git a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift index b431abd167..42474d623d 100644 --- a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift +++ b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift @@ -33,7 +33,10 @@ struct FindNavigatorResultList: View { state.workspace.openFile(item: foundFile.file) } } - }.onChange(of: selectedResult) { newValue in + } + .listStyle(.sidebar) + .background(.clear) + .onChange(of: selectedResult) { newValue in if let file = newValue?.file { state.workspace.openFile(item: file) } diff --git a/CodeEdit/NavigatorSidebar/ProjectNavigator/ProjectNavigator.swift b/CodeEdit/NavigatorSidebar/ProjectNavigator/ProjectNavigator.swift index 3edbfedc66..5f2c12b45a 100644 --- a/CodeEdit/NavigatorSidebar/ProjectNavigator/ProjectNavigator.swift +++ b/CodeEdit/NavigatorSidebar/ProjectNavigator/ProjectNavigator.swift @@ -42,6 +42,7 @@ struct ProjectNavigator: View { .padding(.vertical, 8) } } + .listStyle(.sidebar) .listRowInsets(.init()) .onChange(of: selection) { newValue in guard let id = newValue, diff --git a/CodeEdit/WorkspaceView.swift b/CodeEdit/WorkspaceView.swift index 621b9bb6d9..6ae26260c5 100644 --- a/CodeEdit/WorkspaceView.swift +++ b/CodeEdit/WorkspaceView.swift @@ -35,14 +35,21 @@ struct WorkspaceView: View { var showInspector = true var body: some View { - NavigationView { + ZStack { if workspace.workspaceClient != nil { - content + WorkspaceCodeFileView(windowController: windowController, workspace: workspace) + .safeAreaInset(edge: .bottom) { + if let url = workspace.fileURL { + StatusBarView(workspaceURL: url) + } + } + .overlay(alignment: .top) { + Divider() + } } else { EmptyView() } } - .frame(minWidth: 1000, minHeight: 600) .alert(alertTitle, isPresented: $showingAlert, actions: { Button( action: { showingAlert = false }, @@ -55,27 +62,6 @@ struct WorkspaceView: View { } } } - - @ViewBuilder - private var content: some View { - NavigatorSidebar(workspace: workspace, windowController: windowController) - .frame(minWidth: 250) - - HSplitView { - ZStack { - WorkspaceCodeFileView(windowController: windowController, workspace: workspace) - .frame(minWidth: 500, maxHeight: .infinity) - } - .safeAreaInset(edge: .bottom) { - if let url = workspace.fileURL { - StatusBarView(workspaceURL: url) - } - } - .layoutPriority(1) - InspectorSidebar(workspace: workspace, windowController: windowController) - .frame(minWidth: 260, idealWidth: 300, maxHeight: .infinity) - } - } } struct WorkspaceView_Previews: PreviewProvider { From ebcea525920646f4f05c3635d9aad446449c3490 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 29 Mar 2022 14:40:15 +0200 Subject: [PATCH 2/4] cleanup --- .../Documents/CodeEditWindowController.swift | 126 ++++++++---------- 1 file changed, 58 insertions(+), 68 deletions(-) diff --git a/CodeEdit/Documents/CodeEditWindowController.swift b/CodeEdit/Documents/CodeEditWindowController.swift index 33df0a8763..538a600802 100644 --- a/CodeEdit/Documents/CodeEditWindowController.swift +++ b/CodeEdit/Documents/CodeEditWindowController.swift @@ -15,80 +15,67 @@ class CodeEditWindowController: NSWindowController, NSToolbarDelegate { var workspace: WorkspaceDocument? var quickOpenPanel: OverlayPanel? + private var splitViewController: NSSplitViewController! { + get { return contentViewController as? NSSplitViewController } + set { contentViewController = newValue } + } + init(window: NSWindow, workspace: WorkspaceDocument) { super.init(window: window) - print("INIT") self.workspace = workspace + setupSplitView(with: workspace) + setupToolbar() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupSplitView(with workspace: WorkspaceDocument) { let splitVC = NSSplitViewController() - let sidebar = NSSplitViewItem( - sidebarWithViewController: NSHostingController( - rootView: NavigatorSidebar(workspace: workspace, windowController: self) - ) + let navigatorView = NavigatorSidebar(workspace: workspace, windowController: self) + let navigator = NSSplitViewItem( + sidebarWithViewController: NSHostingController(rootView: navigatorView) ) - sidebar.minimumThickness = 260 - splitVC.addSplitViewItem(sidebar) + navigator.minimumThickness = 260 + splitVC.addSplitViewItem(navigator) - let content = NSSplitViewItem( - viewController: NSHostingController( - rootView: WorkspaceView(windowController: self, workspace: workspace) - ) + let workspaceView = WorkspaceView(windowController: self, workspace: workspace) + let mainContent = NSSplitViewItem( + viewController: NSHostingController(rootView: workspaceView) ) - splitVC.addSplitViewItem(content) + splitVC.addSplitViewItem(mainContent) - let detail = NSSplitViewItem( - viewController: NSHostingController( - rootView: InspectorSidebar(workspace: workspace, windowController: self) - ) + let inspectorView = InspectorSidebar(workspace: workspace, windowController: self) + let inspector = NSSplitViewItem( + viewController: NSHostingController(rootView: inspectorView) ) - detail.collapseBehavior = .preferResizingSiblingsWithFixedSplitView - - splitVC.splitView.setHoldingPriority(.dragThatCannotResizeWindow, forSubviewAt: 2) - splitVC.addSplitViewItem(detail) + inspector.minimumThickness = 260 + inspector.isCollapsed = true + inspector.collapseBehavior = .preferResizingSiblingsWithFixedSplitView + splitVC.addSplitViewItem(inspector) self.splitViewController = splitVC + } + private func setupToolbar() { let toolbar = NSToolbar(identifier: UUID().uuidString) toolbar.delegate = self toolbar.displayMode = .labelOnly - window.toolbarStyle = .unifiedCompact - window.titlebarSeparatorStyle = .none - window.toolbar = toolbar - - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // Dangerous convenience alias so you can access the NSSplitViewController and manipulate it later on - private var splitViewController: NSSplitViewController! { - get { return contentViewController as? NSSplitViewController } - set { contentViewController = newValue } - } - - @objc func toggleFirstPanel() { - guard let firstSplitView = splitViewController.splitViewItems.first else { return } - firstSplitView.animator().isCollapsed.toggle() + self.window?.toolbarStyle = .unifiedCompact + self.window?.titlebarSeparatorStyle = .none + self.window?.toolbar = toolbar } - @objc func toggleLastPanel() { - guard let lastSplitView = splitViewController.splitViewItems.last else { return } - lastSplitView.animator().isCollapsed.toggle() - if lastSplitView.animator().isCollapsed { - window?.toolbar?.removeItem(at: 3) - } else { - window?.toolbar?.insertItem(withItemIdentifier: .itemListTrackingSeparator, at: 3) - } - } + // MARK: - Toolbar func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return [ .toggleFirstSidebarItem, .sidebarTrackingSeparator, .flexibleSpace, - .itemListTrackingSeparator, .flexibleSpace, .toggleLastSidebarItem ] @@ -123,9 +110,9 @@ class CodeEditWindowController: NSWindowController, NSToolbarDelegate { } case .toggleFirstSidebarItem: let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier.toggleFirstSidebarItem) - toolbarItem.label = "Sidebar" - toolbarItem.paletteLabel = "Sidebar" - toolbarItem.toolTip = "Toggle Sidebar" + toolbarItem.label = "Navigator Sidebar" + toolbarItem.paletteLabel = " Navigator Sidebar" + toolbarItem.toolTip = "Hide or show the Navigator" toolbarItem.isBordered = true toolbarItem.target = self toolbarItem.action = #selector(self.toggleFirstPanel) @@ -134,29 +121,20 @@ class CodeEditWindowController: NSWindowController, NSToolbarDelegate { accessibilityDescription: nil )?.withSymbolConfiguration(.init(scale: .large)) - let menuItem = NSMenuItem() - menuItem.submenu = nil - menuItem.title = "Sidebar" - - toolbarItem.menuFormRepresentation = menuItem return toolbarItem case .toggleLastSidebarItem: let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier.toggleFirstSidebarItem) - toolbarItem.label = "Sidebar" - toolbarItem.paletteLabel = "Sidebar" - toolbarItem.toolTip = "Toggle Sidebar" + toolbarItem.label = "Inspector Sidebar" + toolbarItem.paletteLabel = "Inspector Sidebar" + toolbarItem.toolTip = "Hide or show the Inspectors" + toolbarItem.isBordered = true toolbarItem.target = self toolbarItem.action = #selector(self.toggleLastPanel) toolbarItem.image = NSImage( systemSymbolName: "sidebar.trailing", accessibilityDescription: nil - )?.withSymbolConfiguration(.init(scale: .small)) - - let menuItem = NSMenuItem() - menuItem.submenu = nil - menuItem.title = "Sidebar" + )?.withSymbolConfiguration(.init(scale: .large)) - toolbarItem.menuFormRepresentation = menuItem return toolbarItem default: return NSToolbarItem(itemIdentifier: itemIdentifier) @@ -165,9 +143,21 @@ class CodeEditWindowController: NSWindowController, NSToolbarDelegate { override func windowDidLoad() { super.windowDidLoad() + } - // Implement this method to handle any initialization after your window controller's - // window has been loaded from its nib file. + @objc func toggleFirstPanel() { + guard let firstSplitView = splitViewController.splitViewItems.first else { return } + firstSplitView.animator().isCollapsed.toggle() + } + + @objc func toggleLastPanel() { + guard let lastSplitView = splitViewController.splitViewItems.last else { return } + lastSplitView.animator().isCollapsed.toggle() + if lastSplitView.animator().isCollapsed { + window?.toolbar?.removeItem(at: 3) + } else { + window?.toolbar?.insertItem(withItemIdentifier: .itemListTrackingSeparator, at: 3) + } } private func getSelectedCodeFile() -> CodeFileDocument? { From cb514a0e0761cddc802941abac0cb74698770ae4 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 29 Mar 2022 14:42:42 +0200 Subject: [PATCH 3/4] more cleanup --- CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift | 1 - .../NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift index cde844a28d..8ba0c5fb4b 100644 --- a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift +++ b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigator.swift @@ -44,7 +44,6 @@ struct FindNavigator: View { Divider() FindNavigatorResultList(state: state) } - .background(.clear) .onSubmit { state.search(searchText) } diff --git a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift index 42474d623d..c4c52f7c4e 100644 --- a/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift +++ b/CodeEdit/NavigatorSidebar/FindNavigator/FindNavigatorResultList.swift @@ -35,7 +35,6 @@ struct FindNavigatorResultList: View { } } .listStyle(.sidebar) - .background(.clear) .onChange(of: selectedResult) { newValue in if let file = newValue?.file { state.workspace.openFile(item: file) From 426267433e87b5cd1e79c295efcad9c97a89e667 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 29 Mar 2022 14:47:48 +0200 Subject: [PATCH 4/4] bug fixes --- CodeEdit/Documents/CodeEditWindowController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/CodeEdit/Documents/CodeEditWindowController.swift b/CodeEdit/Documents/CodeEditWindowController.swift index 538a600802..61e7b24f3d 100644 --- a/CodeEdit/Documents/CodeEditWindowController.swift +++ b/CodeEdit/Documents/CodeEditWindowController.swift @@ -53,6 +53,7 @@ class CodeEditWindowController: NSWindowController, NSToolbarDelegate { viewController: NSHostingController(rootView: inspectorView) ) inspector.minimumThickness = 260 + inspector.maximumThickness = 260 inspector.isCollapsed = true inspector.collapseBehavior = .preferResizingSiblingsWithFixedSplitView splitVC.addSplitViewItem(inspector)