From b85892fd88ab92df5e0b80cebee5028931c23533 Mon Sep 17 00:00:00 2001
From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com>
Date: Sun, 23 Jun 2024 09:49:11 -0500
Subject: [PATCH 1/7] Fix ActivityView Resize, Inspector Bar
---
.../ActivityViewer/ActivityViewer.swift | 40 +++++++++----------
.../CodeEditSplitViewController.swift | 25 ------------
.../CodeEditWindowController+Toolbar.swift | 21 +++++++++-
.../CodeEditWindowController.swift | 2 +-
.../CodeEditWindowControllerExtensions.swift | 20 +++-------
5 files changed, 47 insertions(+), 61 deletions(-)
diff --git a/CodeEdit/Features/ActivityViewer/ActivityViewer.swift b/CodeEdit/Features/ActivityViewer/ActivityViewer.swift
index 51cd6650b..4b7c6987f 100644
--- a/CodeEdit/Features/ActivityViewer/ActivityViewer.swift
+++ b/CodeEdit/Features/ActivityViewer/ActivityViewer.swift
@@ -13,30 +13,30 @@ struct ActivityViewer: View {
var colorScheme
@ObservedObject var taskNotificationHandler: TaskNotificationHandler
+
var body: some View {
- HStack {
- HStack(spacing: 0) {
- // This is only a placeholder for the task popover(coming in the next pr)
- Rectangle()
- .frame(height: 22)
- .hidden()
+ HStack(spacing: 0) {
+ // This is only a placeholder for the task popover(coming in the next pr)
+ Rectangle()
+ .frame(height: 22)
+ .hidden()
+ .fixedSize()
- Spacer()
+ Spacer(minLength: 0)
- TaskNotificationView(taskNotificationHandler: taskNotificationHandler)
- }
- .padding(.horizontal, 10)
- .background {
- if colorScheme == .dark {
- RoundedRectangle(cornerRadius: 5)
- .opacity(0.10)
- } else {
- RoundedRectangle(cornerRadius: 5)
- .opacity(0.1)
- }
+ TaskNotificationView(taskNotificationHandler: taskNotificationHandler)
+ .fixedSize()
+ }
+ .fixedSize(horizontal: false, vertical: false)
+ .padding(.horizontal, 10)
+ .background {
+ if colorScheme == .dark {
+ RoundedRectangle(cornerRadius: 5)
+ .opacity(0.10)
+ } else {
+ RoundedRectangle(cornerRadius: 5)
+ .opacity(0.1)
}
- .frame(minWidth: 200, idealWidth: 680)
}
- .frame(height: 22)
}
}
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
index 9f270f061..b0c71d191 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
@@ -74,8 +74,6 @@ final class CodeEditSplitViewController: NSSplitViewController {
.inspectorCollapsed
) as? Bool ?? true
}
-
- self.insertToolbarItemIfNeeded()
}
override func viewDidAppear() {
@@ -107,11 +105,9 @@ final class CodeEditSplitViewController: NSSplitViewController {
let proposedWidth = view.frame.width - proposedPosition
if proposedWidth <= CodeEditWindowController.minSidebarWidth / 2 {
splitViewItems.last?.isCollapsed = true
- removeToolbarItemIfNeeded()
return proposedPosition
}
splitViewItems.last?.isCollapsed = false
- insertToolbarItemIfNeeded()
return min(view.frame.width - CodeEditWindowController.minSidebarWidth, proposedPosition)
}
return proposedPosition
@@ -139,27 +135,6 @@ final class CodeEditSplitViewController: NSSplitViewController {
workspace.addToWorkspaceState(key: .inspectorCollapsed, value: isCollapsed)
}
- /// Quick fix for list tracking separator needing to be added again after closing,
- /// then opening the inspector with a drag.
- private func insertToolbarItemIfNeeded() {
- guard !(
- view.window?.toolbar?.items.contains(where: { $0.itemIdentifier == .itemListTrackingSeparator }) ?? true
- ) else {
- return
- }
- view.window?.toolbar?.insertItem(withItemIdentifier: .itemListTrackingSeparator, at: 4)
- }
-
- /// Quick fix for list tracking separator needing to be removed after closing the inspector with a drag
- private func removeToolbarItemIfNeeded() {
- guard let index = view.window?.toolbar?.items.firstIndex(
- where: { $0.itemIdentifier == .itemListTrackingSeparator }
- ) else {
- return
- }
- view.window?.toolbar?.removeItem(at: index)
- }
-
func hideInspectorToolbarBackground() {
let controller = self.view.window?.perform(Selector(("titlebarViewController"))).takeUnretainedValue()
if let controller = controller as? NSViewController {
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift
index ba7b06b3e..4512ef589 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift
@@ -118,15 +118,34 @@ extension CodeEditWindowController {
case .activityViewer:
let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier.activityViewer)
toolbarItem.visibilityPriority = .user
- toolbarItem.view = NSHostingView(
+ let view = NSHostingView(
rootView: ActivityViewer(
taskNotificationHandler: taskNotificationHandler
)
)
+ let weakWidth = view.widthAnchor.constraint(equalToConstant: 650)
+ weakWidth.priority = .defaultLow
+ let strongWidth = view.widthAnchor.constraint(greaterThanOrEqualToConstant: 200)
+ strongWidth.priority = .defaultHigh
+
+ NSLayoutConstraint.activate([
+ weakWidth,
+ strongWidth
+ ])
+
+ toolbarItem.view = view
return toolbarItem
default:
return NSToolbarItem(itemIdentifier: itemIdentifier)
}
}
}
+
+class CETrackingSeparatorToolbarItem: NSTrackingSeparatorToolbarItem {
+ init(_ splitView: NSSplitView, dividerIndex: Int) {
+ super.init(itemIdentifier: .itemListTrackingSeparator)
+ self.splitView = splitView
+ self.dividerIndex = dividerIndex
+ }
+}
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift
index 7da694c2e..94d1fde8c 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift
@@ -113,7 +113,7 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs
.environmentObject(workspace.editorManager)
}
- let inspector = NSSplitViewItem(viewController: NSHostingController(rootView: inspectorView))
+ let inspector = NSSplitViewItem(inspectorWithViewController: NSHostingController(rootView: inspectorView))
inspector.titlebarSeparatorStyle = .none
inspector.minimumThickness = Self.minSidebarWidth
inspector.isCollapsed = true
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
index f3bea3c5f..d49f492ee 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
@@ -20,25 +20,17 @@ extension CodeEditWindowController {
@objc
func toggleLastPanel() {
- guard let lastSplitView = splitViewController.splitViewItems.last else { return }
-
- if let toolbar = window?.toolbar,
- lastSplitView.isCollapsed,
- !toolbar.items.map(\.itemIdentifier).contains(.itemListTrackingSeparator) {
- window?.toolbar?.insertItem(withItemIdentifier: .itemListTrackingSeparator, at: 4)
+ guard let lastSplitView = splitViewController.splitViewItems.last,
+ let codeEditSplitVC = splitViewController as? CodeEditSplitViewController else {
+ return
}
+
NSAnimationContext.runAnimationGroup { _ in
lastSplitView.animator().isCollapsed.toggle()
- } completionHandler: { [weak self] in
- if lastSplitView.isCollapsed {
- self?.window?.animator().toolbar?.removeItem(at: 4)
- }
}
- if let codeEditSplitVC = splitViewController as? CodeEditSplitViewController {
- codeEditSplitVC.saveInspectorCollapsedState(isCollapsed: lastSplitView.isCollapsed)
- codeEditSplitVC.hideInspectorToolbarBackground()
- }
+ codeEditSplitVC.saveInspectorCollapsedState(isCollapsed: lastSplitView.isCollapsed)
+ codeEditSplitVC.hideInspectorToolbarBackground()
}
/// These are example items that added as commands to command palette
From 314f986186e4b5cd1fdaa4fa5f3bcb0790bb039b Mon Sep 17 00:00:00 2001
From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com>
Date: Sun, 23 Jun 2024 17:34:02 -0500
Subject: [PATCH 2/7] Fix The Toolbar, Window Resizing
---
.../CodeEditSplitViewController.swift | 173 +++++++++++-------
.../CodeEditWindowController.swift | 77 +++-----
.../CodeEditWindowControllerExtensions.swift | 10 +-
.../Documents/WorkspaceDocument.swift | 8 +-
.../Views/InspectorAreaView.swift | 8 +-
5 files changed, 137 insertions(+), 139 deletions(-)
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
index b0c71d191..e87c8095c 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
@@ -8,45 +8,21 @@
import Cocoa
import SwiftUI
-struct CodeEditSplitView: NSViewControllerRepresentable {
- let controller: NSSplitViewController
-
- func makeNSViewController(context: Context) -> NSSplitViewController {
- controller
- }
-
- func updateNSViewController(_ nsViewController: NSSplitViewController, context: Context) {}
-}
-
-private extension CGFloat {
- static let snapWidth: CGFloat = 272
-
- static let minSnapWidth: CGFloat = snapWidth - 10
- static let maxSnapWidth: CGFloat = snapWidth + 10
-}
-
final class CodeEditSplitViewController: NSSplitViewController {
- private var workspace: WorkspaceDocument
- private var setWidthFromState = false
- private var viewIsReady = false
-
- // Properties
- private(set) var isSnapped: Bool = false {
- willSet {
- if newValue, newValue != isSnapped && viewIsReady {
- feedbackPerformer.perform(.alignment, performanceTime: .now)
- }
- }
- }
+ static let minSidebarWidth: CGFloat = 242
+ static let maxSnapWidth: CGFloat = minSidebarWidth + 10
+ static let minSnapWidth: CGFloat = minSidebarWidth + 10
- // Dependencies
- private let feedbackPerformer: NSHapticFeedbackPerformer
+ private var workspace: WorkspaceDocument
+ private var navigatorViewModel: NavigatorSidebarViewModel
+ private weak var windowRef: NSWindow?
// MARK: - Initialization
- init(workspace: WorkspaceDocument, feedbackPerformer: NSHapticFeedbackPerformer) {
+ init(workspace: WorkspaceDocument, navigatorViewModel: NavigatorSidebarViewModel, windowRef: NSWindow) {
self.workspace = workspace
- self.feedbackPerformer = feedbackPerformer
+ self.navigatorViewModel = navigatorViewModel
+ self.windowRef = windowRef
super.init(nibName: nil, bundle: nil)
}
@@ -55,13 +31,67 @@ final class CodeEditSplitViewController: NSSplitViewController {
fatalError("init(coder:) has not been implemented")
}
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ guard let windowRef else {
+ // swiftlint:disable:next line_length
+ assertionFailure("No WindowRef found, not initialized properly or the window was dereferenced and the controller was not.")
+ return
+ }
+
+ splitView.translatesAutoresizingMaskIntoConstraints = false
+
+ let settingsView = SettingsInjector {
+ NavigatorAreaView(workspace: workspace, viewModel: navigatorViewModel)
+ .environmentObject(workspace)
+ .environmentObject(workspace.editorManager)
+ }
+
+ let navigator = NSSplitViewItem(sidebarWithViewController: NSHostingController(rootView: settingsView))
+ navigator.titlebarSeparatorStyle = .none
+ navigator.isSpringLoaded = true
+ navigator.minimumThickness = Self.minSidebarWidth
+ navigator.collapseBehavior = .useConstraints
+
+ addSplitViewItem(navigator)
+
+ let workspaceView = SettingsInjector {
+ WindowObserver(window: windowRef) {
+ WorkspaceView()
+ .environmentObject(workspace)
+ .environmentObject(workspace.editorManager)
+ .environmentObject(workspace.statusBarViewModel)
+ .environmentObject(workspace.utilityAreaModel)
+ }
+ }
+
+ let mainContent = NSSplitViewItem(viewController: NSHostingController(rootView: workspaceView))
+ mainContent.titlebarSeparatorStyle = .line
+ mainContent.minimumThickness = 200
+
+ addSplitViewItem(mainContent)
+
+ let inspectorView = SettingsInjector {
+ InspectorAreaView(viewModel: InspectorAreaViewModel())
+ .environmentObject(workspace)
+ .environmentObject(workspace.editorManager)
+ }
+
+ let inspector = NSSplitViewItem(inspectorWithViewController: NSHostingController(rootView: inspectorView))
+ inspector.titlebarSeparatorStyle = .none
+ inspector.minimumThickness = Self.minSidebarWidth
+ inspector.maximumThickness = .greatestFiniteMagnitude
+ inspector.collapseBehavior = .useConstraints
+ inspector.isSpringLoaded = true
+
+ addSplitViewItem(inspector)
+ }
+
override func viewWillAppear() {
super.viewWillAppear()
- viewIsReady = false
- let width = workspace.getFromWorkspaceState(.splitViewWidth) as? CGFloat
- splitView.setPosition(width ?? .snapWidth, ofDividerAt: .zero)
- setWidthFromState = true
+ let navigatorWidth = workspace.getFromWorkspaceState(.splitViewWidth) as? CGFloat
+ splitView.setPosition(navigatorWidth ?? Self.minSidebarWidth, ofDividerAt: 0)
if let firstSplitView = splitViewItems.first {
firstSplitView.isCollapsed = workspace.getFromWorkspaceState(
@@ -76,44 +106,62 @@ final class CodeEditSplitViewController: NSSplitViewController {
}
}
- override func viewDidAppear() {
- viewIsReady = true
- hideInspectorToolbarBackground()
- }
-
// MARK: - NSSplitViewDelegate
+ /// Perform the spring loaded navigator splits.
+ /// - Note: This could be removed. The only additional functionality this provides over using just the
+ /// `NSSplitViewItem.isSpringLoaded` & `NSSplitViewItem.minimumThickness` is the haptic feedback we add.
+ /// - Parameters:
+ /// - splitView: The split view to use.
+ /// - proposedPosition: The proposed drag position.
+ /// - dividerIndex: The index of the divider being dragged.
+ /// - Returns: The position to move the divider to.
override func splitView(
_ splitView: NSSplitView,
constrainSplitPosition proposedPosition: CGFloat,
ofSubviewAt dividerIndex: Int
) -> CGFloat {
- if dividerIndex == 0 {
+ switch dividerIndex {
+ case 0:
// Navigator
- if (CGFloat.minSnapWidth...CGFloat.maxSnapWidth).contains(proposedPosition) {
- isSnapped = true
- return .snapWidth
+ if (Self.minSnapWidth...Self.maxSnapWidth).contains(proposedPosition) {
+ return Self.minSidebarWidth
} else {
- isSnapped = false
- if proposedPosition <= CodeEditWindowController.minSidebarWidth / 2 {
- splitViewItems.first?.isCollapsed = true
+ if proposedPosition <= Self.minSidebarWidth / 2 {
+ hapticCollapse(splitViewItems.first, collapseAction: true)
return 0
}
- return max(CodeEditWindowController.minSidebarWidth, proposedPosition)
+ hapticCollapse(splitViewItems.first, collapseAction: false)
+ return max(Self.minSidebarWidth, proposedPosition)
}
- } else if dividerIndex == 1 {
+ case 1:
let proposedWidth = view.frame.width - proposedPosition
- if proposedWidth <= CodeEditWindowController.minSidebarWidth / 2 {
- splitViewItems.last?.isCollapsed = true
+ if proposedWidth <= Self.minSidebarWidth / 2 {
+ hapticCollapse(splitViewItems.last, collapseAction: true)
return proposedPosition
}
- splitViewItems.last?.isCollapsed = false
- return min(view.frame.width - CodeEditWindowController.minSidebarWidth, proposedPosition)
+ hapticCollapse(splitViewItems.last, collapseAction: false)
+ return min(view.frame.width - Self.minSidebarWidth, proposedPosition)
+ default:
+ return proposedPosition
+ }
+ }
+
+ /// Performs a haptic feedback while collapsing or revealing a split item.
+ /// If the item was not previously in the new intended state, a haptic `.alignment` feedback is sent.
+ /// - Parameters:
+ /// - item: The item to collapse or reveal
+ /// - collapseAction: Whether or not to collapse the item. Set to true to collapse it.
+ private func hapticCollapse(_ item: NSSplitViewItem?, collapseAction: Bool) {
+ if item?.isCollapsed == !collapseAction {
+ NSHapticFeedbackManager.defaultPerformer.perform(.alignment, performanceTime: .now)
}
- return proposedPosition
+ item?.isCollapsed = collapseAction
}
+ /// Save the width of the inspector and navigator between sessions.
override func splitViewDidResizeSubviews(_ notification: Notification) {
+ super.splitViewDidResizeSubviews(notification)
guard let resizedDivider = notification.userInfo?["NSSplitViewDividerIndex"] as? Int else {
return
}
@@ -121,7 +169,7 @@ final class CodeEditSplitViewController: NSSplitViewController {
if resizedDivider == 0 {
let panel = splitView.subviews[0]
let width = panel.frame.size.width
- if width > 0 && setWidthFromState {
+ if width > 0 {
workspace.addToWorkspaceState(key: .splitViewWidth, value: width)
}
}
@@ -134,15 +182,4 @@ final class CodeEditSplitViewController: NSSplitViewController {
func saveInspectorCollapsedState(isCollapsed: Bool) {
workspace.addToWorkspaceState(key: .inspectorCollapsed, value: isCollapsed)
}
-
- func hideInspectorToolbarBackground() {
- let controller = self.view.window?.perform(Selector(("titlebarViewController"))).takeUnretainedValue()
- if let controller = controller as? NSViewController {
- let effectViewCount = controller.view.subviews.filter { $0 is NSVisualEffectView }.count
- guard effectViewCount > 2 else { return }
- if let view = controller.view.subviews[0] as? NSVisualEffectView {
- view.isHidden = true
- }
- }
- }
}
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift
index 94d1fde8c..0b4066585 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift
@@ -10,8 +10,6 @@ import SwiftUI
import Combine
final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, ObservableObject {
- static let minSidebarWidth: CGFloat = 242
-
@Published var navigatorCollapsed = false
@Published var inspectorCollapsed = false
@Published var toolbarCollapsed = false
@@ -43,11 +41,20 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs
self.workspaceSettings = CEWorkspaceSettings(workspaceDocument: workspace)
setupSplitView(with: workspace)
- let view = CodeEditSplitView(controller: splitViewController).ignoresSafeArea()
-
+ // Previous:
// An NSHostingController is used, so the root viewController of the window is a SwiftUI-managed one.
// This allows us to use some SwiftUI features, like focusedSceneObject.
- contentViewController = NSHostingController(rootView: view)
+ // -----
+ // let view = CodeEditSplitView(controller: splitViewController).ignoresSafeArea()
+ // contentViewController = NSHostingController(rootView: view)
+ // -----
+ //
+ // New:
+ // The previous decision led to a very jank split controller mechanism because SwiftUI's layout system is not
+ // very compatible with AppKit's when it comes to the inspector/navigator toolbar & split view system.
+ // -----
+ contentViewController = splitViewController
+ // -----
observers = [
splitViewController.splitViewItems.first!.observe(\.isCollapsed, changeHandler: { [weak self] item, _ in
@@ -70,60 +77,18 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs
}
private func setupSplitView(with workspace: WorkspaceDocument) {
- let feedbackPerformer = NSHapticFeedbackManager.defaultPerformer
- let splitVC = CodeEditSplitViewController(workspace: workspace, feedbackPerformer: feedbackPerformer)
-
- let navigatorViewModel = NavigatorSidebarViewModel()
- navigatorSidebarViewModel = navigatorViewModel
-
- let settingsView = SettingsInjector {
- NavigatorAreaView(workspace: workspace, viewModel: navigatorViewModel)
- .environmentObject(workspace)
- .environmentObject(workspace.editorManager)
+ guard let window else {
+ assertionFailure("No window found for this controller. Cannot set up content.")
+ return
}
- let navigator = NSSplitViewItem(
- sidebarWithViewController: NSHostingController(rootView: settingsView)
+ let navigatorModel = NavigatorSidebarViewModel()
+ navigatorSidebarViewModel = navigatorModel
+ self.splitViewController = CodeEditSplitViewController(
+ workspace: workspace,
+ navigatorViewModel: navigatorModel,
+ windowRef: window
)
- navigator.titlebarSeparatorStyle = .none
- navigator.minimumThickness = Self.minSidebarWidth
- navigator.collapseBehavior = .useConstraints
-
- splitVC.addSplitViewItem(navigator)
-
- let workspaceView = SettingsInjector {
- WindowObserver(window: window!) {
- WorkspaceView()
- .environmentObject(workspace)
- .environmentObject(workspace.editorManager)
- .environmentObject(workspace.statusBarViewModel)
- .environmentObject(workspace.utilityAreaModel)
- }
- }
-
- let mainContent = NSSplitViewItem(viewController: NSHostingController(rootView: workspaceView))
- mainContent.titlebarSeparatorStyle = .line
- mainContent.holdingPriority = .init(50)
-
- splitVC.addSplitViewItem(mainContent)
-
- let inspectorView = SettingsInjector {
- InspectorAreaView(viewModel: InspectorAreaViewModel())
- .environmentObject(workspace)
- .environmentObject(workspace.editorManager)
- }
-
- let inspector = NSSplitViewItem(inspectorWithViewController: NSHostingController(rootView: inspectorView))
- inspector.titlebarSeparatorStyle = .none
- inspector.minimumThickness = Self.minSidebarWidth
- inspector.isCollapsed = true
- inspector.canCollapse = true
- inspector.collapseBehavior = .useConstraints
- inspector.isSpringLoaded = true
-
- splitVC.addSplitViewItem(inspector)
-
- self.splitViewController = splitVC
self.listenToDocumentEdited(workspace: workspace)
}
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
index d49f492ee..96a3be550 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
@@ -13,9 +13,9 @@ extension CodeEditWindowController {
func toggleFirstPanel() {
guard let firstSplitView = splitViewController.splitViewItems.first else { return }
firstSplitView.animator().isCollapsed.toggle()
- if let codeEditSplitVC = splitViewController as? CodeEditSplitViewController {
- codeEditSplitVC.saveNavigatorCollapsedState(isCollapsed: firstSplitView.isCollapsed)
- }
+// if let codeEditSplitVC = splitViewController as? CodeEditSplitViewController {
+// codeEditSplitVC.saveNavigatorCollapsedState(isCollapsed: firstSplitView.isCollapsed)
+// }
}
@objc
@@ -29,8 +29,8 @@ extension CodeEditWindowController {
lastSplitView.animator().isCollapsed.toggle()
}
- codeEditSplitVC.saveInspectorCollapsedState(isCollapsed: lastSplitView.isCollapsed)
- codeEditSplitVC.hideInspectorToolbarBackground()
+// codeEditSplitVC.saveInspectorCollapsedState(isCollapsed: lastSplitView.isCollapsed)
+// codeEditSplitVC.hideInspectorToolbarBackground()
}
/// These are example items that added as commands to command palette
diff --git a/CodeEdit/Features/Documents/WorkspaceDocument.swift b/CodeEdit/Features/Documents/WorkspaceDocument.swift
index c9fccc70d..71a187b39 100644
--- a/CodeEdit/Features/Documents/WorkspaceDocument.swift
+++ b/CodeEdit/Features/Documents/WorkspaceDocument.swift
@@ -80,14 +80,16 @@ final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
backing: .buffered,
defer: false
)
+ // Note For anyone hoping to switch back to a Root-SwiftUI window:
+ // See Commit 0200c87 for more details and to see what was previously here.
+ // -----
// Setting the "min size" like this is hacky, but SwiftUI overrides the contentRect and
// any of the built-in window size functions & autosave stuff. So we have to set it like this.
// SwiftUI also ignores this value, so it just manages to set the initial window size. *Hopefully* this
// is fixed in the future.
+ // ----
if let rectString = getFromWorkspaceState(.workspaceWindowSize) as? String {
- window.minSize = NSRectFromString(rectString).size
- } else {
- window.minSize = .init(width: 1400, height: 900)
+ window.setContentSize(NSRectFromString(rectString).size)
}
let windowController = CodeEditWindowController(
window: window,
diff --git a/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift b/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift
index 2708e784e..f8c4a5757 100644
--- a/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift
+++ b/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift
@@ -5,6 +5,7 @@
// Created by Austin Condiff on 3/21/22.
//
+import AppKit
import SwiftUI
struct InspectorAreaView: View {
@@ -52,13 +53,6 @@ struct InspectorAreaView: View {
}
}
.clipShape(Rectangle())
- .frame(
- minWidth: CodeEditWindowController.minSidebarWidth,
- idealWidth: 300,
- minHeight: 0,
- maxHeight: .infinity,
- alignment: .top
- )
.safeAreaInset(edge: .trailing, spacing: 0) {
if sidebarPosition == .side {
HStack(spacing: 0) {
From 813219720a3563d96a546ad3e165f76257023000 Mon Sep 17 00:00:00 2001
From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com>
Date: Sun, 23 Jun 2024 17:43:28 -0500
Subject: [PATCH 3/7] Remove Testing Class
---
.../Controllers/CodeEditWindowController+Toolbar.swift | 8 --------
1 file changed, 8 deletions(-)
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift
index 4512ef589..f9acda1f1 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift
@@ -141,11 +141,3 @@ extension CodeEditWindowController {
}
}
}
-
-class CETrackingSeparatorToolbarItem: NSTrackingSeparatorToolbarItem {
- init(_ splitView: NSSplitView, dividerIndex: Int) {
- super.init(itemIdentifier: .itemListTrackingSeparator)
- self.splitView = splitView
- self.dividerIndex = dividerIndex
- }
-}
From 379f5427dd93ede5273d30aac66498427c377608 Mon Sep 17 00:00:00 2001
From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com>
Date: Sun, 23 Jun 2024 19:39:17 -0500
Subject: [PATCH 4/7] Remove Unnecessary Import
---
CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift | 2 --
1 file changed, 2 deletions(-)
diff --git a/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift b/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift
index f8c4a5757..b46742f64 100644
--- a/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift
+++ b/CodeEdit/Features/InspectorArea/Views/InspectorAreaView.swift
@@ -5,7 +5,6 @@
// Created by Austin Condiff on 3/21/22.
//
-import AppKit
import SwiftUI
struct InspectorAreaView: View {
@@ -52,7 +51,6 @@ struct InspectorAreaView: View {
NoSelectionInspectorView()
}
}
- .clipShape(Rectangle())
.safeAreaInset(edge: .trailing, spacing: 0) {
if sidebarPosition == .side {
HStack(spacing: 0) {
From 2591c8f96849b9f5fd329214e010df2a9d4ebef1 Mon Sep 17 00:00:00 2001
From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com>
Date: Sun, 23 Jun 2024 19:56:38 -0500
Subject: [PATCH 5/7] Uncomment Some Stuff
---
.../Controllers/CodeEditWindowControllerExtensions.swift | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
index 96a3be550..f1a858615 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift
@@ -13,9 +13,9 @@ extension CodeEditWindowController {
func toggleFirstPanel() {
guard let firstSplitView = splitViewController.splitViewItems.first else { return }
firstSplitView.animator().isCollapsed.toggle()
-// if let codeEditSplitVC = splitViewController as? CodeEditSplitViewController {
-// codeEditSplitVC.saveNavigatorCollapsedState(isCollapsed: firstSplitView.isCollapsed)
-// }
+ if let codeEditSplitVC = splitViewController as? CodeEditSplitViewController {
+ codeEditSplitVC.saveNavigatorCollapsedState(isCollapsed: firstSplitView.isCollapsed)
+ }
}
@objc
@@ -29,8 +29,7 @@ extension CodeEditWindowController {
lastSplitView.animator().isCollapsed.toggle()
}
-// codeEditSplitVC.saveInspectorCollapsedState(isCollapsed: lastSplitView.isCollapsed)
-// codeEditSplitVC.hideInspectorToolbarBackground()
+ codeEditSplitVC.saveInspectorCollapsedState(isCollapsed: lastSplitView.isCollapsed)
}
/// These are example items that added as commands to command palette
From 89a76bc7472c698b9ba291b1075a692f3808bd8b Mon Sep 17 00:00:00 2001
From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com>
Date: Sun, 23 Jun 2024 20:17:10 -0500
Subject: [PATCH 6/7] Enable All Tests
---
.../xcshareddata/xcschemes/CodeEdit.xcscheme | 12 --
.../CodeEditSplitViewController.swift | 30 ++--
.../Documents/DocumentsUnitTests.swift | 151 +++++++++++-------
.../Mocks/NSHapticFeedbackPerformerMock.swift | 9 +-
4 files changed, 116 insertions(+), 86 deletions(-)
diff --git a/CodeEdit.xcodeproj/xcshareddata/xcschemes/CodeEdit.xcscheme b/CodeEdit.xcodeproj/xcshareddata/xcschemes/CodeEdit.xcscheme
index 8720f4dbb..c6868c5f6 100644
--- a/CodeEdit.xcodeproj/xcshareddata/xcschemes/CodeEdit.xcscheme
+++ b/CodeEdit.xcodeproj/xcshareddata/xcschemes/CodeEdit.xcscheme
@@ -77,18 +77,6 @@
-
-
-
-
-
-
-
-
diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
index e87c8095c..af83e56ac 100644
--- a/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
+++ b/CodeEdit/Features/Documents/Controllers/CodeEditSplitViewController.swift
@@ -10,19 +10,27 @@ import SwiftUI
final class CodeEditSplitViewController: NSSplitViewController {
static let minSidebarWidth: CGFloat = 242
- static let maxSnapWidth: CGFloat = minSidebarWidth + 10
- static let minSnapWidth: CGFloat = minSidebarWidth + 10
+ static let maxSnapWidth: CGFloat = snapWidth + 10
+ static let snapWidth: CGFloat = 272
+ static let minSnapWidth: CGFloat = snapWidth - 10
private var workspace: WorkspaceDocument
private var navigatorViewModel: NavigatorSidebarViewModel
private weak var windowRef: NSWindow?
+ private unowned var hapticPerformer: NSHapticFeedbackPerformer
// MARK: - Initialization
- init(workspace: WorkspaceDocument, navigatorViewModel: NavigatorSidebarViewModel, windowRef: NSWindow) {
+ init(
+ workspace: WorkspaceDocument,
+ navigatorViewModel: NavigatorSidebarViewModel,
+ windowRef: NSWindow,
+ hapticPerformer: NSHapticFeedbackPerformer = NSHapticFeedbackManager.defaultPerformer
+ ) {
self.workspace = workspace
self.navigatorViewModel = navigatorViewModel
self.windowRef = windowRef
+ self.hapticPerformer = hapticPerformer
super.init(nibName: nil, bundle: nil)
}
@@ -125,12 +133,11 @@ final class CodeEditSplitViewController: NSSplitViewController {
case 0:
// Navigator
if (Self.minSnapWidth...Self.maxSnapWidth).contains(proposedPosition) {
- return Self.minSidebarWidth
+ return Self.snapWidth
+ } else if proposedPosition <= Self.minSidebarWidth / 2 {
+ hapticCollapse(splitViewItems.first, collapseAction: true)
+ return 0
} else {
- if proposedPosition <= Self.minSidebarWidth / 2 {
- hapticCollapse(splitViewItems.first, collapseAction: true)
- return 0
- }
hapticCollapse(splitViewItems.first, collapseAction: false)
return max(Self.minSidebarWidth, proposedPosition)
}
@@ -139,9 +146,10 @@ final class CodeEditSplitViewController: NSSplitViewController {
if proposedWidth <= Self.minSidebarWidth / 2 {
hapticCollapse(splitViewItems.last, collapseAction: true)
return proposedPosition
+ } else {
+ hapticCollapse(splitViewItems.last, collapseAction: false)
+ return min(view.frame.width - Self.minSidebarWidth, proposedPosition)
}
- hapticCollapse(splitViewItems.last, collapseAction: false)
- return min(view.frame.width - Self.minSidebarWidth, proposedPosition)
default:
return proposedPosition
}
@@ -154,7 +162,7 @@ final class CodeEditSplitViewController: NSSplitViewController {
/// - collapseAction: Whether or not to collapse the item. Set to true to collapse it.
private func hapticCollapse(_ item: NSSplitViewItem?, collapseAction: Bool) {
if item?.isCollapsed == !collapseAction {
- NSHapticFeedbackManager.defaultPerformer.perform(.alignment, performanceTime: .now)
+ hapticPerformer.perform(.alignment, performanceTime: .now)
}
item?.isCollapsed = collapseAction
}
diff --git a/CodeEditTests/Features/Documents/DocumentsUnitTests.swift b/CodeEditTests/Features/Documents/DocumentsUnitTests.swift
index 6cce3e50f..c116d7a19 100644
--- a/CodeEditTests/Features/Documents/DocumentsUnitTests.swift
+++ b/CodeEditTests/Features/Documents/DocumentsUnitTests.swift
@@ -12,17 +12,26 @@ final class DocumentsUnitTests: XCTestCase {
// Properties
private var splitViewController: CodeEditSplitViewController!
private var hapticFeedbackPerformerMock: NSHapticFeedbackPerformerMock!
+ private var navigatorViewModel: NavigatorSidebarViewModel!
+ private var window: NSWindow!
// MARK: - Lifecycle
override func setUp() {
super.setUp()
- hapticFeedbackPerformerMock = .init()
- splitViewController = .init(workspace: WorkspaceDocument(), feedbackPerformer: hapticFeedbackPerformerMock)
+ hapticFeedbackPerformerMock = NSHapticFeedbackPerformerMock()
+ navigatorViewModel = .init()
+ window = NSWindow()
+ splitViewController = .init(
+ workspace: WorkspaceDocument(),
+ navigatorViewModel: navigatorViewModel,
+ windowRef: window,
+ hapticPerformer: hapticFeedbackPerformerMock
+ )
+ splitViewController.viewDidLoad()
}
override func tearDown() {
- hapticFeedbackPerformerMock = nil
splitViewController = nil
super.tearDown()
}
@@ -30,83 +39,103 @@ final class DocumentsUnitTests: XCTestCase {
// MARK: - Tests
func testSplitViewControllerSnappedWhenWidthInAppropriateRange() {
- // Given
- let position = (260...280).randomElement() ?? .zero
-
- // When
- let result = splitViewController.splitView(
- splitViewController.splitView,
- constrainSplitPosition: .init(position),
- ofSubviewAt: .zero
- )
+ for _ in 0..<10 {
+ // Given
+ let position = CGFloat.random(
+ in: (CodeEditSplitViewController.minSnapWidth...CodeEditSplitViewController.maxSnapWidth)
+ )
- // Then
- XCTAssertEqual(result, 272)
+ // When
+ let result = splitViewController.splitView(
+ splitViewController.splitView,
+ constrainSplitPosition: .init(position),
+ ofSubviewAt: .zero
+ )
+
+ // Then
+ XCTAssertEqual(result, CodeEditSplitViewController.snapWidth)
+ }
}
func testSplitViewControllerStopSnappedWhenWidthIsLowerAppropriateRange() {
- // Given
- // 242 is the minimum width of the sidebar
- let position = (242..<260).randomElement() ?? .zero
-
- // When
- let result = splitViewController.splitView(
- splitViewController.splitView,
- constrainSplitPosition: .init(position),
- ofSubviewAt: .zero
- )
+ for _ in 0..<10 {
+ // Given
+ let position = CGFloat.random(in: 0..<(CodeEditSplitViewController.minSidebarWidth / 2))
- // Then
- XCTAssertEqual(result, .init(position))
+ // When
+ let result = splitViewController.splitView(
+ splitViewController.splitView,
+ constrainSplitPosition: .init(position),
+ ofSubviewAt: .zero
+ )
+
+ // Then
+ XCTAssertEqual(result, .zero)
+ }
}
func testSplitViewControllerStopSnappedWhenWidthIsHigherAppropriateRange() {
- // Given
- let position = (281...500).randomElement() ?? .zero
-
- // When
- let result = splitViewController.splitView(
- splitViewController.splitView,
- constrainSplitPosition: .init(position),
- ofSubviewAt: .zero
- )
+ for _ in 0..<10 {
+ // Given
+ let position = CGFloat.random(in: (CodeEditSplitViewController.maxSnapWidth...500))
- // Then
- XCTAssertEqual(result, .init(position))
- }
-
- func testSplitViewControllerProducedHapticFeedback() {
- // Given
- let position = (260...280).randomElement() ?? .zero
-
- // When
- _ = splitViewController.splitView(
- splitViewController.splitView,
- constrainSplitPosition: .init(position),
- ofSubviewAt: .zero
- )
+ // When
+ let result = splitViewController.splitView(
+ splitViewController.splitView,
+ constrainSplitPosition: .init(position),
+ ofSubviewAt: .zero
+ )
- // Then
- XCTAssertTrue(hapticFeedbackPerformerMock.invokedPerform)
- XCTAssertEqual(hapticFeedbackPerformerMock.invokedPerformCount, 1)
+ // Then
+ XCTAssertEqual(result, .init(position))
+ }
}
- func testSplitViewControllerProducedHapticFeedbackOnceWhenPlentyChangesOccur() {
- // Given
- let firstPosition = (260...280).randomElement() ?? .zero
- let secondPosition = 300
+ // Test moving from collapsed to uncollapsed makes a haptic.
+ func testSplitViewControllerProducedHapticFeedback() {
+ for _ in 0..<10 {
+ // Given
+ splitViewController.splitViewItems.first?.isCollapsed = true
+ let position = CGFloat.random(
+ in: (CodeEditSplitViewController.minSidebarWidth / 2)...CodeEditSplitViewController.minSidebarWidth
+ )
- // When
- [firstPosition, secondPosition].forEach { position in
+ // When
_ = splitViewController.splitView(
splitViewController.splitView,
constrainSplitPosition: .init(position),
ofSubviewAt: .zero
)
+
+ // Then
+ XCTAssertTrue(hapticFeedbackPerformerMock.invokedPerform)
+ XCTAssertEqual(hapticFeedbackPerformerMock.invokedPerformCount, 1)
+ hapticFeedbackPerformerMock.reset()
}
+ }
- // Then
- XCTAssertTrue(hapticFeedbackPerformerMock.invokedPerform)
- XCTAssertEqual(hapticFeedbackPerformerMock.invokedPerformCount, 1)
+ func testSplitViewControllerProducedHapticFeedbackOnceWhenPlentyChangesOccur() {
+ for _ in 0..<10 {
+ // Given
+ splitViewController.splitViewItems.first?.isCollapsed = true
+ let firstPosition = CGFloat.random(in: 0..<(CodeEditSplitViewController.minSidebarWidth / 2))
+ let secondPosition = CGFloat.random(
+ in: (CodeEditSplitViewController.minSidebarWidth / 2)...CodeEditSplitViewController.minSidebarWidth
+ )
+
+ // When
+ [firstPosition, secondPosition].forEach { position in
+ _ = splitViewController.splitView(
+ splitViewController.splitView,
+ constrainSplitPosition: .init(position),
+ ofSubviewAt: .zero
+ )
+ }
+
+ // Then
+ XCTAssertTrue(hapticFeedbackPerformerMock.invokedPerform)
+ XCTAssertEqual(hapticFeedbackPerformerMock.invokedPerformCount, 1)
+ hapticFeedbackPerformerMock.reset()
+ }
}
}
diff --git a/CodeEditTests/Features/Documents/Mocks/NSHapticFeedbackPerformerMock.swift b/CodeEditTests/Features/Documents/Mocks/NSHapticFeedbackPerformerMock.swift
index 7a8b12e27..5ee85dbf7 100644
--- a/CodeEditTests/Features/Documents/Mocks/NSHapticFeedbackPerformerMock.swift
+++ b/CodeEditTests/Features/Documents/Mocks/NSHapticFeedbackPerformerMock.swift
@@ -9,14 +9,19 @@ import Cocoa
final class NSHapticFeedbackPerformerMock: NSObject, NSHapticFeedbackPerformer {
- var invokedPerform = false
+ var invokedPerform: Bool {
+ invokedPerformCount > 0
+ }
var invokedPerformCount = 0
func perform(
_ pattern: NSHapticFeedbackManager.FeedbackPattern,
performanceTime: NSHapticFeedbackManager.PerformanceTime
) {
- invokedPerform = true
invokedPerformCount += 1
}
+
+ func reset() {
+ invokedPerformCount = 0
+ }
}
From 2c892a44bd2e9a848aff0b35562a4806c6ac995f Mon Sep 17 00:00:00 2001
From: Austin Condiff
Date: Tue, 25 Jun 2024 21:57:15 -0500
Subject: [PATCH 7/7] Update
CodeEdit/Features/ActivityViewer/ActivityViewer.swift
---
CodeEdit/Features/ActivityViewer/ActivityViewer.swift | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/CodeEdit/Features/ActivityViewer/ActivityViewer.swift b/CodeEdit/Features/ActivityViewer/ActivityViewer.swift
index 4b7c6987f..1cc2cca5f 100644
--- a/CodeEdit/Features/ActivityViewer/ActivityViewer.swift
+++ b/CodeEdit/Features/ActivityViewer/ActivityViewer.swift
@@ -30,13 +30,8 @@ struct ActivityViewer: View {
.fixedSize(horizontal: false, vertical: false)
.padding(.horizontal, 10)
.background {
- if colorScheme == .dark {
- RoundedRectangle(cornerRadius: 5)
- .opacity(0.10)
- } else {
- RoundedRectangle(cornerRadius: 5)
- .opacity(0.1)
- }
+ RoundedRectangle(cornerRadius: 5)
+ .opacity(0.1)
}
}
}