Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB1E27E7458B00138301 /* WorkspaceCodeFileView.swift */; };
0485EB2327E7791400138301 /* QuickOpenPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485EB2227E7791400138301 /* QuickOpenPreviewView.swift */; };
0485EB2527E7B9C800138301 /* Overlays in Frameworks */ = {isa = PBXBuildFile; productRef = 0485EB2427E7B9C800138301 /* Overlays */; };
2806CBD427EE9FC0003E5523 /* ProjectNavigatorContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2806CBD327EE9FC0003E5523 /* ProjectNavigatorContextMenu.swift */; };
2813F93827ECC4AA00E305E4 /* FindNavigatorResultList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E201B327E9989900CB86D0 /* FindNavigatorResultList.swift */; };
2813F93927ECC4C300E305E4 /* LeadingSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287776E627E3413200D46668 /* LeadingSidebar.swift */; };
2813F93A27ECC4CC00E305E4 /* ProjectNavigatorItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287776EC27E350D800D46668 /* ProjectNavigatorItem.swift */; };
Expand Down Expand Up @@ -97,6 +98,7 @@
12F71F7127E833A000095416 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
20F8067027E65A5200EB7827 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/Localizable.strings; sourceTree = "<group>"; };
2805D9E827E5ED180032BC56 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
2806CBD327EE9FC0003E5523 /* ProjectNavigatorContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectNavigatorContextMenu.swift; sourceTree = "<group>"; };
2859B94027EB5BC00069BE88 /* TerminalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSettingsView.swift; sourceTree = "<group>"; };
286471AA27ED51FD0039369D /* ProjectNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectNavigator.swift; sourceTree = "<group>"; };
286620A427E4AB6900E18C2B /* BreadcrumbsComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbsComponent.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -221,6 +223,7 @@
children = (
286471AA27ED51FD0039369D /* ProjectNavigator.swift */,
287776EC27E350D800D46668 /* ProjectNavigatorItem.swift */,
2806CBD327EE9FC0003E5523 /* ProjectNavigatorContextMenu.swift */,
);
path = ProjectNavigator;
sourceTree = "<group>";
Expand Down Expand Up @@ -562,6 +565,7 @@
buildActionMask = 2147483647;
files = (
2813F93A27ECC4CC00E305E4 /* ProjectNavigatorItem.swift in Sources */,
2806CBD427EE9FC0003E5523 /* ProjectNavigatorContextMenu.swift in Sources */,
2813F93927ECC4C300E305E4 /* LeadingSidebar.swift in Sources */,
2813F93827ECC4AA00E305E4 /* FindNavigatorResultList.swift in Sources */,
2B7A583527E4BA0100D25D4E /* AppDelegate.swift in Sources */,
Expand Down
39 changes: 26 additions & 13 deletions CodeEdit/NavigatorSidebar/ProjectNavigator/ProjectNavigator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,37 @@
import SwiftUI
import WorkspaceClient

/// # Project Navigator - Sidebar
///
/// A list that functions as a project navigator, showing collapsable folders
/// and files.
///
/// When selecting a file it will open in the editor.
///
struct ProjectNavigator: View {
@ObservedObject var workspace: WorkspaceDocument
var windowController: NSWindowController

/// The `ID` of the currently selected file/folder. If none is selected this is `nil`
@State private var selection: WorkspaceClient.FileItem.ID?

var body: some View {
List(selection: $selection) {
Text(workspace.workspaceClient?.folderURL()?.lastPathComponent ?? "Empty")
.font(.callout.bold())
.foregroundColor(.secondary)
.padding(.bottom, 4)
ForEach(workspace.selectionState.fileItems.sortItems(foldersOnTop: workspace.sortFoldersOnTop)) { item in
ProjectNavigatorItem(
item: item,
workspace: workspace,
windowController: windowController,
shouldloadChildren: .constant(true), // First level of children should always be loaded
selectedId: $selection
)
Section {
ForEach(workspace.selectionState.fileItems.sortItems(foldersOnTop: workspace.sortFoldersOnTop)) { item in
ProjectNavigatorItem(
item: item,
workspace: workspace,
windowController: windowController,
shouldloadChildren: .constant(true), // First level of children should always be loaded
selectedId: $selection
)
}
} header: {
Text(projectName)
.padding(.vertical, 8)
}
}
.environment(\.defaultMinListRowHeight, 8)
.listRowInsets(.init())
.onChange(of: selection) { newValue in
guard let id = newValue,
Expand All @@ -43,4 +51,9 @@ struct ProjectNavigator: View {
selection = newValue
}
}

/// The name of the project (name of the selected top-level folder)
private var projectName: String {
workspace.workspaceClient?.folderURL()?.lastPathComponent ?? "Project"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//
// ProjectNavigatorContextMenu.swift
// CodeEdit
//
// Created by Lukas Pistrol on 26.03.22.
//

import SwiftUI
import WorkspaceClient

// TODO: Some implementations still need to be done
/// # Project Navigator Context Menu
///
/// A context menu which shows when right clicking on a folder or file
/// in the ``ProjectNavigator``.
///
struct ProjectNavigatorContextMenu: View {

private var item: WorkspaceClient.FileItem
private var isFolder: Bool

init(_ item: WorkspaceClient.FileItem, isFolder: Bool) {
self.item = item
self.isFolder = isFolder
}

@ViewBuilder
var body: some View {
showInFinder
openSection
showFileInspector
addSection
delete
newFolderSection
sortSection
findInSelectedGroups
sourceControl
}

private var showInFinder: some View {
Group {
Button("Show in Finder") {
item.showInFinder()
}
Divider()
}
}

private var openSection: some View {
Group {
Button("Open in Tab") {
// Open a new tab
}
Button("Open in New Window") {
// Open a new window
}
Divider()
}
}

private var showFileInspector: some View {
Group {
Button("Show File Inspector") {
// Show File Inspector
}
Divider()
}
}

private var addSection: some View {
Group {
Button("New File") {
item.addFile(fileName: "randomFile.txt")
}
Button("Add files to folder") {
// Add Files to Folder
}
Divider()
}
}

private var delete: some View {
Group {
Button("Delete") {
item.delete()
}
Divider()
}
}

private var newFolderSection: some View {
Group {
Button("New Folder") {
item.addFolder(folderName: "Test Folder")
}
Button("New Folder from Selection") {
// New Group from Selection
}
Divider()
}
}

private var sortSection: some View {
Group {
Button("Sort by Name") {
// Sort folder items by name
}
.disabled(!isFolder)
Button("Sort by Type") {
// Sort folder items by file type
}
.disabled(!isFolder)
Divider()
}
}

private var findInSelectedGroups: some View {
Group {
Button("Find in Selected Groups...") {
// Find in selected groups...
}
.disabled(!isFolder)
Divider()
}
}

private var sourceControl: some View {
Menu("Source Control") {
Button("Commit Selected File") {
// Commit selected file
}
Divider()
Button("Discard Changes in Selected File") {
// Discard changes made to the selected file
}
Divider()
Button("Add") {
// Add file to git
}
Button("Mark as Resolved") {
// Mark file as resolved
}
}
}
}
121 changes: 22 additions & 99 deletions CodeEdit/NavigatorSidebar/ProjectNavigator/ProjectNavigatorItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import SwiftUI
import WorkspaceClient
import CodeFile

/// # Project Navigator Item
///
/// Displays a File or Folder in the ``ProjectNavigator``.
///
struct ProjectNavigatorItem: View {
@Environment(\.controlActiveState) var activeState
@Environment(\.colorScheme) var colorScheme
@AppStorage(FileIconStyle.storageKey) var iconStyle: FileIconStyle = .default

/// The `FileItem` for this view
Expand Down Expand Up @@ -48,27 +50,22 @@ struct ProjectNavigatorItem: View {
}
}

func sidebarFileItem(_ item: WorkspaceClient.FileItem) -> some View {
HStack {
Image(systemName: item.systemImage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 12, height: 12, alignment: .center)
.foregroundColor(iconColor)
.opacity(activeState == .inactive ? 0.45 : 1)
Text(item.url.lastPathComponent)
.font(.subheadline)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
}
.listRowInsets(.init())
.contextMenu { contextMenuContent(false) }
/// A `Label` representing a file
private func sidebarFileItem(_ item: WorkspaceClient.FileItem) -> some View {
Label(item.url.lastPathComponent, systemImage: item.systemImage)
.accentColor(iconColor)
.contextMenu {
ProjectNavigatorContextMenu(item, isFolder: false)
}
}

/// A `DisclosureGroup` representing a folder.
///
/// When expanded, it shows files/folders inside
@ViewBuilder
func sidebarFolderItem(_ item: WorkspaceClient.FileItem) -> some View {
private func sidebarFolderItem(_ item: WorkspaceClient.FileItem) -> some View {
DisclosureGroup(isExpanded: $isExpanded) {
if shouldloadChildren { // Only load when expanded -> Improves performance massively
if shouldloadChildren { // Only load when parent is expanded -> Improves performance massively
ForEach(item.children!.sortItems(foldersOnTop: workspace.sortFoldersOnTop)) { child in
ProjectNavigatorItem(
item: child,
Expand All @@ -80,90 +77,16 @@ struct ProjectNavigatorItem: View {
}
}
} label: {
HStack {
Image(systemName: item.systemImage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 12, height: 12, alignment: .center)
.foregroundColor(.secondary)
.opacity(activeState == .inactive ? 0.45 : 1)
Text(item.url.lastPathComponent)
.font(.subheadline)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
}
.contextMenu { contextMenuContent(true) }
Label(item.url.lastPathComponent, systemImage: item.systemImage)
.accentColor(.secondary)
.contextMenu {
ProjectNavigatorContextMenu(item, isFolder: true)
}
}
}

/// Returns a color depending on the set icon color style in preferences
private var iconColor: Color {
return iconStyle == .color ? item.iconColor : .secondary
}

// TODO: Some implementations still need to be done
/// maximum number of views in a container is exceeded (in SwiftUI). The max = 10
@ViewBuilder
private func contextMenuContent(_ isFolder: Bool) -> some View {
Button("Show in Finder") { item.showInFinder() }
Group {
Divider()
Button("Open in Tab") {
// Open a new tab
}
Button("Open in New Window") {
// Open a new window
}
Divider()
}
Button("Show File Inspector") { /* Show File Inspector */ }
Group {
Divider()
Button("New File") {
item.addFile(fileName: "randomFile.txt")
}
Button("Add files to folder") {
// Add Files to Folder
}
Divider()
}
Button("Delete") { item.delete() }
Group {
Divider()
Button("New Folder") {
item.addFolder(folderName: "Test Folder")
}
Button("New Folder from Selection") {
// New Group from Selection
}
Divider()
}
Group {
Button("Sort by Name") {
// Sort folder items by name
}.disabled(isFolder ? false : true)
Button("Sort by Type") {
// Sort folder items by file type
}.disabled(isFolder ? false : true)
Divider()
}
Button("Find in Selected Groups...") {}
.disabled(isFolder ? false : true)
Divider()
Menu("Source Control") {
Button("Commit Selected File") {
// Commit selected file
}
Divider()
Button("Discard Changes in Selected File") {
// Discard changes made to the selected file
}
Divider()
Button("Add") {
// Add file to git
}
Button("Mark as Resolved") {
// Mark file as resolved
}
}
}
}