diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index df2d548a33..ccf7f6653f 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -368,6 +368,23 @@ 6CFF967829BEBCF600182D6F /* MainCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFF967729BEBCF600182D6F /* MainCommands.swift */; }; 6CFF967A29BEBD2400182D6F /* ViewCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFF967929BEBD2400182D6F /* ViewCommands.swift */; }; 6CFF967C29BEBD5200182D6F /* WindowCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFF967B29BEBD5200182D6F /* WindowCommands.swift */; }; + 77A01E1F2BB33FB500F0EA38 /* CEWorkspaceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E1E2BB33FB500F0EA38 /* CEWorkspaceSettingsView.swift */; }; + 77A01E232BB423A800F0EA38 /* CEWorkspaceSettingsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E222BB423A800F0EA38 /* CEWorkspaceSettingsPage.swift */; }; + 77A01E252BB4245300F0EA38 /* PageAndCEWorkspaceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E242BB4245300F0EA38 /* PageAndCEWorkspaceSettings.swift */; }; + 77A01E272BB4249800F0EA38 /* CEWorkspaceSettingsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E262BB4249800F0EA38 /* CEWorkspaceSettingsData.swift */; }; + 77A01E2A2BB424EA00F0EA38 /* CEWorkspaceSettingsData+ProjectSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E292BB424EA00F0EA38 /* CEWorkspaceSettingsData+ProjectSettings.swift */; }; + 77A01E2C2BB425B200F0EA38 /* CEWorkspaceSettingsData+TasksSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E2B2BB425B200F0EA38 /* CEWorkspaceSettingsData+TasksSettings.swift */; }; + 77A01E2E2BB4261200F0EA38 /* CEWorkspaceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E2D2BB4261200F0EA38 /* CEWorkspaceSettings.swift */; }; + 77A01E302BB4270F00F0EA38 /* ProjectCEWorkspaceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E2F2BB4270F00F0EA38 /* ProjectCEWorkspaceSettingsView.swift */; }; + 77A01E322BB4274B00F0EA38 /* TasksCEWorkspaceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E312BB4274B00F0EA38 /* TasksCEWorkspaceSettingsView.swift */; }; + 77A01E342BB4282900F0EA38 /* CEWorkspaceSettingsSearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E332BB4282900F0EA38 /* CEWorkspaceSettingsSearchResult.swift */; }; + 77A01E362BB428EE00F0EA38 /* CEWorkspaceSettingsPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E352BB428EE00F0EA38 /* CEWorkspaceSettingsPageView.swift */; }; + 77A01E432BBC3A2800F0EA38 /* CETask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E422BBC3A2800F0EA38 /* CETask.swift */; }; + 77A01E582BBD7ECE00F0EA38 /* AddCETaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E572BBD7ECE00F0EA38 /* AddCETaskView.swift */; }; + 77A01E6D2BC3EA2A00F0EA38 /* NSWindow+Child.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E6C2BC3EA2A00F0EA38 /* NSWindow+Child.swift */; }; + 77A01E802BC5101200F0EA38 /* EnvironmentVariableListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E7F2BC5101200F0EA38 /* EnvironmentVariableListItem.swift */; }; + 77A01E8E2BC9A09C00F0EA38 /* EditCETaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E8D2BC9A09C00F0EA38 /* EditCETaskView.swift */; }; + 77A01E912BC9A33600F0EA38 /* CETaskFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A01E902BC9A33600F0EA38 /* CETaskFormView.swift */; }; 850C631029D6B01D00E1444C /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850C630F29D6B01D00E1444C /* SettingsView.swift */; }; 850C631229D6B03400E1444C /* SettingsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850C631129D6B03400E1444C /* SettingsPage.swift */; }; 852C7E332A587279006BA599 /* SearchableSettingsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852C7E322A587279006BA599 /* SearchableSettingsPage.swift */; }; @@ -893,6 +910,23 @@ 6CFF967729BEBCF600182D6F /* MainCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCommands.swift; sourceTree = ""; }; 6CFF967929BEBD2400182D6F /* ViewCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewCommands.swift; sourceTree = ""; }; 6CFF967B29BEBD5200182D6F /* WindowCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowCommands.swift; sourceTree = ""; }; + 77A01E1E2BB33FB500F0EA38 /* CEWorkspaceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEWorkspaceSettingsView.swift; sourceTree = ""; }; + 77A01E222BB423A800F0EA38 /* CEWorkspaceSettingsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEWorkspaceSettingsPage.swift; sourceTree = ""; }; + 77A01E242BB4245300F0EA38 /* PageAndCEWorkspaceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageAndCEWorkspaceSettings.swift; sourceTree = ""; }; + 77A01E262BB4249800F0EA38 /* CEWorkspaceSettingsData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEWorkspaceSettingsData.swift; sourceTree = ""; }; + 77A01E292BB424EA00F0EA38 /* CEWorkspaceSettingsData+ProjectSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CEWorkspaceSettingsData+ProjectSettings.swift"; sourceTree = ""; }; + 77A01E2B2BB425B200F0EA38 /* CEWorkspaceSettingsData+TasksSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CEWorkspaceSettingsData+TasksSettings.swift"; sourceTree = ""; }; + 77A01E2D2BB4261200F0EA38 /* CEWorkspaceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEWorkspaceSettings.swift; sourceTree = ""; }; + 77A01E2F2BB4270F00F0EA38 /* ProjectCEWorkspaceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectCEWorkspaceSettingsView.swift; sourceTree = ""; }; + 77A01E312BB4274B00F0EA38 /* TasksCEWorkspaceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksCEWorkspaceSettingsView.swift; sourceTree = ""; }; + 77A01E332BB4282900F0EA38 /* CEWorkspaceSettingsSearchResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEWorkspaceSettingsSearchResult.swift; sourceTree = ""; }; + 77A01E352BB428EE00F0EA38 /* CEWorkspaceSettingsPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CEWorkspaceSettingsPageView.swift; sourceTree = ""; }; + 77A01E422BBC3A2800F0EA38 /* CETask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CETask.swift; sourceTree = ""; }; + 77A01E572BBD7ECE00F0EA38 /* AddCETaskView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCETaskView.swift; sourceTree = ""; }; + 77A01E6C2BC3EA2A00F0EA38 /* NSWindow+Child.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+Child.swift"; sourceTree = ""; }; + 77A01E7F2BC5101200F0EA38 /* EnvironmentVariableListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentVariableListItem.swift; sourceTree = ""; }; + 77A01E8D2BC9A09C00F0EA38 /* EditCETaskView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCETaskView.swift; sourceTree = ""; }; + 77A01E902BC9A33600F0EA38 /* CETaskFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CETaskFormView.swift; sourceTree = ""; }; 850C630F29D6B01D00E1444C /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 850C631129D6B03400E1444C /* SettingsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPage.swift; sourceTree = ""; }; 852C7E322A587279006BA599 /* SearchableSettingsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchableSettingsPage.swift; sourceTree = ""; }; @@ -2051,6 +2085,8 @@ isa = PBXGroup; children = ( 588847652992A35800996D95 /* Models */, + 77A01E1A2BB33F1E00F0EA38 /* Views */, + 77A01E1B2BB33F3D00F0EA38 /* Extensions */, ); path = CEWorkspace; sourceTree = ""; @@ -2058,12 +2094,18 @@ 588847652992A35800996D95 /* Models */ = { isa = PBXGroup; children = ( + 77A01E422BBC3A2800F0EA38 /* CETask.swift */, 588847622992A2A200996D95 /* CEWorkspaceFile.swift */, 5894E59629FEF7740077E59C /* CEWorkspaceFile+Recursion.swift */, 58A2E40629C3975D005CB615 /* CEWorkspaceFileIcon.swift */, 58710158298EB80000951BA4 /* CEWorkspaceFileManager.swift */, 6CB52DC82AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift */, + 77A01E2D2BB4261200F0EA38 /* CEWorkspaceSettings.swift */, + 77A01E262BB4249800F0EA38 /* CEWorkspaceSettingsData.swift */, + 77A01E222BB423A800F0EA38 /* CEWorkspaceSettingsPage.swift */, + 77A01E332BB4282900F0EA38 /* CEWorkspaceSettingsSearchResult.swift */, 6C049A362A49E2DB00D42923 /* DirectoryEventStream.swift */, + 77A01E242BB4245300F0EA38 /* PageAndCEWorkspaceSettings.swift */, ); path = Models; sourceTree = ""; @@ -2136,6 +2178,7 @@ children = ( 85CD0C5D2A10CC2500E531FD /* URL */, 6C82D6C429C0129E00495C54 /* NSApplication */, + 77A01E922BCA9C0400F0EA38 /* NSWindow */, 588847672992AAB800996D95 /* Array */, 6CBD1BC42978DE3E006639D5 /* Text */, 5831E3D02934036D00D5A6D2 /* NSTableView */, @@ -2424,6 +2467,46 @@ path = Text; sourceTree = ""; }; + 77A01E1A2BB33F1E00F0EA38 /* Views */ = { + isa = PBXGroup; + children = ( + 77A01E372BB4291700F0EA38 /* Pages */, + 77A01E572BBD7ECE00F0EA38 /* AddCETaskView.swift */, + 77A01E902BC9A33600F0EA38 /* CETaskFormView.swift */, + 77A01E1E2BB33FB500F0EA38 /* CEWorkspaceSettingsView.swift */, + 77A01E352BB428EE00F0EA38 /* CEWorkspaceSettingsPageView.swift */, + 77A01E8D2BC9A09C00F0EA38 /* EditCETaskView.swift */, + 77A01E7F2BC5101200F0EA38 /* EnvironmentVariableListItem.swift */, + ); + path = Views; + sourceTree = ""; + }; + 77A01E1B2BB33F3D00F0EA38 /* Extensions */ = { + isa = PBXGroup; + children = ( + 77A01E292BB424EA00F0EA38 /* CEWorkspaceSettingsData+ProjectSettings.swift */, + 77A01E2B2BB425B200F0EA38 /* CEWorkspaceSettingsData+TasksSettings.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 77A01E372BB4291700F0EA38 /* Pages */ = { + isa = PBXGroup; + children = ( + 77A01E2F2BB4270F00F0EA38 /* ProjectCEWorkspaceSettingsView.swift */, + 77A01E312BB4274B00F0EA38 /* TasksCEWorkspaceSettingsView.swift */, + ); + path = Pages; + sourceTree = ""; + }; + 77A01E922BCA9C0400F0EA38 /* NSWindow */ = { + isa = PBXGroup; + children = ( + 77A01E6C2BC3EA2A00F0EA38 /* NSWindow+Child.swift */, + ); + path = NSWindow; + sourceTree = ""; + }; 85CD0C5D2A10CC2500E531FD /* URL */ = { isa = PBXGroup; children = ( @@ -3201,6 +3284,7 @@ B65B11012B09D5D4002852CF /* GitClient+Pull.swift in Sources */, 2072FA13280D74ED00C7F8D4 /* HistoryInspectorModel.swift in Sources */, 852E62012A5C17E500447138 /* PageAndSettings.swift in Sources */, + 77A01E1F2BB33FB500F0EA38 /* CEWorkspaceSettingsView.swift in Sources */, 587B9DA029300ABD00AC7927 /* PanelDivider.swift in Sources */, 58822534292C280D00E83CDE /* CursorLocation.swift in Sources */, 201169E52837B40300F92B46 /* SourceControlNavigatorRepositoryView.swift in Sources */, @@ -3231,11 +3315,13 @@ 613899B92B6E704500A5CAF6 /* String+Normalise.swift in Sources */, 04BA7C192AE2D7C600584E1C /* GitClient+Branches.swift in Sources */, 587B9E8829301D8F00AC7927 /* GitHubFiles.swift in Sources */, + 77A01E232BB423A800F0EA38 /* CEWorkspaceSettingsPage.swift in Sources */, 587B9DA729300ABD00AC7927 /* HelpButton.swift in Sources */, 6C5B63DE29C76213005454BA /* WindowCodeFileView.swift in Sources */, 58F2EB08292FB2B0004A9BDE /* TextEditingSettings.swift in Sources */, 201169DB2837B34000F92B46 /* SourceControlNavigatorChangedFileView.swift in Sources */, 5882252E292C280D00E83CDE /* StatusBarMaximizeButton.swift in Sources */, + 77A01E2E2BB4261200F0EA38 /* CEWorkspaceSettings.swift in Sources */, 6C4104E9297C970F00F472BA /* AboutDefaultView.swift in Sources */, 587B9E6F29301D8F00AC7927 /* GitLabProjectAccess.swift in Sources */, 587B9E6929301D8F00AC7927 /* GitLabEvent.swift in Sources */, @@ -3265,6 +3351,7 @@ D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */, 6CE6226E2A2A1CDE0013085C /* NavigatorTab.swift in Sources */, 041FC6AD2AE437CE00C1F65A /* SourceControlNavigatorNewBranchView.swift in Sources */, + 77A01E432BBC3A2800F0EA38 /* CETask.swift in Sources */, 6C48D8F72972E5F300D6D205 /* WindowObserver.swift in Sources */, 6CED16E42A3E660D000EC962 /* String+Lines.swift in Sources */, 587B9E6B29301D8F00AC7927 /* GitLabAvatarURL.swift in Sources */, @@ -3282,6 +3369,7 @@ B628B7B72B223BAD00F9775A /* FindModePicker.swift in Sources */, 587B9E6E29301D8F00AC7927 /* GitLabProject.swift in Sources */, 58798234292E30B90085B254 /* FeedbackIssueArea.swift in Sources */, + 77A01E2A2BB424EA00F0EA38 /* CEWorkspaceSettingsData+ProjectSettings.swift in Sources */, 852C7E332A587279006BA599 /* SearchableSettingsPage.swift in Sources */, 587B9E5F29301D8F00AC7927 /* GitLabProjectRouter.swift in Sources */, 587B9E7329301D8F00AC7927 /* GitRouter.swift in Sources */, @@ -3337,6 +3425,7 @@ B6EA1FE729DA341D001BF195 /* Theme.swift in Sources */, 587B9E7529301D8F00AC7927 /* String+QueryParameters.swift in Sources */, B60718312B15A9A3009CDAB4 /* CEOutlineGroup.swift in Sources */, + 77A01E252BB4245300F0EA38 /* PageAndCEWorkspaceSettings.swift in Sources */, 58798219292D92370085B254 /* SearchModeModel.swift in Sources */, 6C5C891B2A3F736500A94FE1 /* FocusedValues.swift in Sources */, 611192062B08CCF600D4459B /* SearchIndexer+Add.swift in Sources */, @@ -3346,6 +3435,7 @@ 58D01C9D293167DC00C5B6B4 /* KeychainSwiftAccessOptions.swift in Sources */, B6E41C8B29DE7AE80088F9F4 /* AccountsSettingsSigninView.swift in Sources */, 6C2C155A29B4F4CC00EA60A5 /* Variadic.swift in Sources */, + 77A01E322BB4274B00F0EA38 /* TasksCEWorkspaceSettingsView.swift in Sources */, B6E41C8F29DE9CD80088F9F4 /* AccountsSettingsDetailsView.swift in Sources */, 5882252B292C280D00E83CDE /* StatusBarCursorLocationLabel.swift in Sources */, 58798252292E78D80085B254 /* ImageFileView.swift in Sources */, @@ -3362,6 +3452,7 @@ 587B9E7B29301D8F00AC7927 /* GitHubRouter.swift in Sources */, 201169E22837B3D800F92B46 /* SourceControlNavigatorChangesView.swift in Sources */, 850C631029D6B01D00E1444C /* SettingsView.swift in Sources */, + 77A01E6D2BC3EA2A00F0EA38 /* NSWindow+Child.swift in Sources */, DE6405A62817734700881FDF /* EditorTabBarNative.swift in Sources */, 581550CF29FBD30400684881 /* StandardTableViewCell.swift in Sources */, B62AEDB82A1FE2DC009A9F52 /* UtilityAreaOutputView.swift in Sources */, @@ -3402,8 +3493,10 @@ 58D01C96293167DC00C5B6B4 /* Date+Formatted.swift in Sources */, B66A4E5629C918A0004573B4 /* SceneID.swift in Sources */, 6C53AAD829A6C4FD00EE9ED6 /* SplitView.swift in Sources */, + 77A01E912BC9A33600F0EA38 /* CETaskFormView.swift in Sources */, B66A4E5329C91831004573B4 /* CodeEditCommands.swift in Sources */, 58822529292C280D00E83CDE /* StatusBarLineEndSelector.swift in Sources */, + 77A01E342BB4282900F0EA38 /* CEWorkspaceSettingsSearchResult.swift in Sources */, 5C4BB1E128212B1E00A92FB2 /* World.swift in Sources */, 581550D029FBD30400684881 /* FileSystemTableViewCell.swift in Sources */, B607183F2B17DB07009CDAB4 /* SourceControlNavigatorRepositoryView+contextMenu.swift in Sources */, @@ -3416,6 +3509,7 @@ 587B9E6329301D8F00AC7927 /* GitLabAccount.swift in Sources */, 6C1CC99B2B1E7CBC0002349B /* FindNavigatorIndexBar.swift in Sources */, 285FEC7027FE4B9800E57D53 /* ProjectNavigatorTableViewCell.swift in Sources */, + 77A01E582BBD7ECE00F0EA38 /* AddCETaskView.swift in Sources */, 6CB9144B29BEC7F100BC47F2 /* (null) in Sources */, 587B9E7429301D8F00AC7927 /* URL+URLParameters.swift in Sources */, 61538B902B111FE800A88846 /* String+AppearancesOfSubstring.swift in Sources */, @@ -3517,10 +3611,13 @@ B6041F4D29D7A4E9000F3454 /* SettingsPageView.swift in Sources */, 587B9E9A29301D8F00AC7927 /* GitType.swift in Sources */, B65B10F82B081A34002852CF /* SourceControlNavigatorNoRemotesView.swift in Sources */, + 77A01E8E2BC9A09C00F0EA38 /* EditCETaskView.swift in Sources */, 58D01C97293167DC00C5B6B4 /* String+SHA256.swift in Sources */, B6EA1FFD29DB792C001BF195 /* ThemeSettingsColorPreview.swift in Sources */, 2806E904297958B9000040F4 /* ContributorRowView.swift in Sources */, 6C578D8C29CD372700DC73B2 /* ExtensionCommands.swift in Sources */, + 77A01E272BB4249800F0EA38 /* CEWorkspaceSettingsData.swift in Sources */, + 77A01E802BC5101200F0EA38 /* EnvironmentVariableListItem.swift in Sources */, B6041F5229D7D6D6000F3454 /* SettingsWindow.swift in Sources */, B6EA1FF829DB78DB001BF195 /* ThemeSettingThemeRow.swift in Sources */, 587B9E7629301D8F00AC7927 /* GitTime.swift in Sources */, @@ -3557,6 +3654,7 @@ 5878DAB2291D627C00DD95A3 /* EditorPathBarView.swift in Sources */, 04BC1CDE2AD9B4B000A83EA5 /* EditorFileTabCloseButton.swift in Sources */, 6C6BD70129CD172700235D17 /* ExtensionsListView.swift in Sources */, + 77A01E362BB428EE00F0EA38 /* CEWorkspaceSettingsPageView.swift in Sources */, 043C321627E3201F006AE443 /* WorkspaceDocument.swift in Sources */, 58F2EAEC292FB2B0004A9BDE /* IgnoredFiles.swift in Sources */, 6CD03B6A29FC773F001BD1D0 /* SettingsInjector.swift in Sources */, @@ -3590,6 +3688,8 @@ 611192002B08CCD700D4459B /* SearchIndexer+Memory.swift in Sources */, 587B9E8129301D8F00AC7927 /* PublicKey.swift in Sources */, 611191FE2B08CCD200D4459B /* SearchIndexer+File.swift in Sources */, + 77A01E302BB4270F00F0EA38 /* ProjectCEWorkspaceSettingsView.swift in Sources */, + 77A01E2C2BB425B200F0EA38 /* CEWorkspaceSettingsData+TasksSettings.swift in Sources */, 5B241BF32B6DDBFF0016E616 /* IgnorePatternListItemView.swift in Sources */, 6CB52DC92AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift in Sources */, 58F2EB0B292FB2B0004A9BDE /* AccountsSettings.swift in Sources */, diff --git a/CodeEdit/Features/CEWorkspace/Extensions/CEWorkspaceSettingsData+ProjectSettings.swift b/CodeEdit/Features/CEWorkspace/Extensions/CEWorkspaceSettingsData+ProjectSettings.swift new file mode 100644 index 0000000000..d3328c1603 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Extensions/CEWorkspaceSettingsData+ProjectSettings.swift @@ -0,0 +1,30 @@ +// +// ProjectCEWorkspaceSettings.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import SwiftUI + +extension CEWorkspaceSettingsData { + /// Workspace settings for the project tab. + struct ProjectSettings: Codable, Hashable, SearchableSettingsPage { + var searchKeys: [String] { + [ + "Project Name", + ] + .map { NSLocalizedString($0, comment: "") } + } + + var projectName: String = "" + + init() {} + + /// Explicit decoder init for setting default values when key is not present in `JSON` + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.projectName = try container.decodeIfPresent(String.self, forKey: .projectName) ?? "" + } + } +} diff --git a/CodeEdit/Features/CEWorkspace/Extensions/CEWorkspaceSettingsData+TasksSettings.swift b/CodeEdit/Features/CEWorkspace/Extensions/CEWorkspaceSettingsData+TasksSettings.swift new file mode 100644 index 0000000000..70e90b37c8 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Extensions/CEWorkspaceSettingsData+TasksSettings.swift @@ -0,0 +1,35 @@ +// +// CEWorkspaceSettingsData+TasksSettings.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import Foundation +import Collections + +extension CEWorkspaceSettingsData { + /// Workspace settings for the tasks tab. + struct TasksSettings: Codable, Hashable, SearchableSettingsPage { + var items: [CETask] = [] + + var searchKeys: [String] { + [ + "Tasks" + ] + .map { NSLocalizedString($0, comment: "") } + } + + /// The tasks functionality behavior of the app + var enabled: Bool = true + + init() {} + + /// Explicit decoder init for setting default values when key is not present in `JSON` + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.items = try container.decodeIfPresent([CETask].self, forKey: .items) ?? [] + self.enabled = try container.decodeIfPresent(Bool.self, forKey: .enabled) ?? true + } + } +} diff --git a/CodeEdit/Features/CEWorkspace/Models/CETask.swift b/CodeEdit/Features/CEWorkspace/Models/CETask.swift new file mode 100644 index 0000000000..46d51016a9 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Models/CETask.swift @@ -0,0 +1,71 @@ +// +// CETask.swift +// CodeEdit +// +// Created by Axel Martinez on 2/4/24. +// + +import SwiftUI + +/// CodeEdit task that will be executed by the task manager. +struct CETask: Identifiable, Hashable, Codable { + var id = UUID() + var name: String = "" + var target: String = "" + var workingDirectory: String = "" + var command: String = "" + var environmentVariables: [EnvironmentVariable] = [] + + var isInvalid: Bool { + name.isEmpty || + command.isEmpty || + target.isEmpty || + workingDirectory.isEmpty + } + + enum CodingKeys: String, CodingKey { + case name + case target + case workingDirectory + case command + case environmentVariables + } + + struct EnvironmentVariable: Identifiable, Hashable, Codable { + var id = UUID() + var name: String = "" + var value: String = "" + + /// Enables encoding the environment variables as a `name`:`value`pair. + private struct CodingKeys: CodingKey { + var stringValue: String + var intValue: Int? + + init?(stringValue: String) { + self.stringValue = stringValue + self.intValue = nil + } + + /// Required by the CodingKey protocol but not being currently used. + init?(intValue: Int) { + self.stringValue = "\(intValue)" + self.intValue = intValue + } + } + + init() {} + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + for key in container.allKeys { + name = key.stringValue + value = try container.decode(String.self, forKey: key) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(value, forKey: CodingKeys(stringValue: name)!) + } + } +} diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettings.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettings.swift new file mode 100644 index 0000000000..a5a3f1f674 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettings.swift @@ -0,0 +1,73 @@ +// +// CEWorkspaceSettings.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import SwiftUI +import Combine + +/// The CodeEdit workspace settings model. +final class CEWorkspaceSettings: ObservableObject { + @ObservedObject private var workspace: WorkspaceDocument + @Published public var preferences: CEWorkspaceSettingsData = .init() + + private var savedSettings = false + private var storeTask: AnyCancellable! + private let fileManager = FileManager.default + + private var folderURL: URL? { + guard let workspaceURL = workspace.fileURL else { + return nil + } + + return workspaceURL + .appendingPathComponent(".codeedit", isDirectory: true) + } + + private var settingsURL: URL? { + folderURL? + .appendingPathComponent("settings") + .appendingPathExtension("json") + } + + init(workspaceDocument: WorkspaceDocument) { + self.workspace = workspaceDocument + + loadSettings() + + self.storeTask = self.$preferences.throttle(for: 2, scheduler: RunLoop.main, latest: true).sink { + if !self.savedSettings, let folderURL = self.folderURL { + try? self.fileManager.createDirectory(at: folderURL, withIntermediateDirectories: false) + self.savedSettings = true + } + + try? self.savePreferences($0) + } + } + + /// Load and construct ``CEWorkspaceSettings`` model from `.codeedit/settings.json` + private func loadSettings() { + if let settingsURL = settingsURL { + if fileManager.fileExists(atPath: settingsURL.path) { + guard let json = try? Data(contentsOf: settingsURL), + let prefs = try? JSONDecoder().decode(CEWorkspaceSettingsData.self, from: json) + else { return } + + self.savedSettings = true + self.preferences = prefs + } + } + } + + /// Save``CEWorkspaceSettings`` model to `.codeedit/settings.json` + private func savePreferences(_ data: CEWorkspaceSettingsData) throws { + guard let settingsURL = settingsURL else { return } + + let data = try JSONEncoder().encode(data) + let json = try JSONSerialization.jsonObject(with: data) + let prettyJSON = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted]) + try prettyJSON.write(to: settingsURL, options: .atomic) + } +} diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsData.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsData.swift new file mode 100644 index 0000000000..c195c25409 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsData.swift @@ -0,0 +1,44 @@ +// +// CEWorkspaceSettingsData.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import SwiftUI +import Foundation + +/// # Workspace Settings +/// +/// The model of the workspace settings for `CodeEdit` that control the behavior of some functionality at the workspace +/// level like the workspace name or defining tasks. A `JSON` representation is persisted in the workspace's +/// `./codeedit/settings.json`. file +struct CEWorkspaceSettingsData: Codable, Hashable { + /// The project global settings + var project: ProjectSettings = .init() + + /// The tasks settings + var tasks: TasksSettings = .init() + + init() {} + + /// Explicit decoder init for setting default values when key is not present in `JSON` + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.project = try container.decodeIfPresent(ProjectSettings.self, forKey: .project) ?? .init() + self.tasks = try container.decodeIfPresent(TasksSettings.self, forKey: .tasks) ?? .init() + } + + func propertiesOf(_ name: CEWorkspaceSettingsPage.Name) -> [CEWorkspaceSettingsPage] { + var settings: [CEWorkspaceSettingsPage] = [] + + switch name { + case .project: + project.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) } + case .tasks: + tasks.searchKeys.forEach { settings.append(.init(name, isSetting: true, settingName: $0)) } + } + + return settings + } +} diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsPage.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsPage.swift new file mode 100644 index 0000000000..c762629a24 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsPage.swift @@ -0,0 +1,50 @@ +// +// CEWorkspaceSettingsPage.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import Foundation +import SwiftUI + +/// Represents a workspace settings tab. +struct CEWorkspaceSettingsPage: Hashable, Equatable, Identifiable { + /// Sidebar icon, with a base color and SF Symbol + enum IconResource: Equatable, Hashable { + case system(_ name: String) + case symbol(_ name: String) + case asset(_ name: String) + } + + /// All the workspace settings pages + enum Name: String { + case project = "Project" + case tasks = "Tasks" + } + + let id: UUID = UUID() + + let name: Name + let baseColor: Color? + let isSetting: Bool + let settingName: String + var nameString: LocalizedStringKey { + LocalizedStringKey(name.rawValue) + } + let icon: IconResource? + + init( + _ name: Name, + baseColor: Color? = nil, + icon: IconResource? = nil, + isSetting: Bool = false, + settingName: String = "" + ) { + self.name = name + self.baseColor = baseColor + self.icon = icon + self.isSetting = isSetting + self.settingName = settingName + } +} diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsSearchResult.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsSearchResult.swift new file mode 100644 index 0000000000..f3bd83eca0 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceSettingsSearchResult.swift @@ -0,0 +1,23 @@ +// +// CEWorkpaceSettingsSearchResult.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import SwiftUI + +// TODO: Extend this struct further to support setting "flashing" +final class CEWorkspaceSettingsSearchResult: Identifiable { + let id: UUID = UUID() + let pageFound: Bool + let pages: [CEWorkspaceSettingsPage] + + init( + pageFound: Bool, + pages: [CEWorkspaceSettingsPage] + ) { + self.pageFound = pageFound + self.pages = pages + } +} diff --git a/CodeEdit/Features/CEWorkspace/Models/PageAndCEWorkspaceSettings.swift b/CodeEdit/Features/CEWorkspace/Models/PageAndCEWorkspaceSettings.swift new file mode 100644 index 0000000000..6830baa7b9 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Models/PageAndCEWorkspaceSettings.swift @@ -0,0 +1,19 @@ +// +// PageAndCEWorkspaceSettings.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import Foundation + +struct PageAndCEWorkspaceSettings: Identifiable, Equatable { + let id: UUID = UUID() + let page: CEWorkspaceSettingsPage + let settings: [CEWorkspaceSettingsPage] + + init(_ page: CEWorkspaceSettingsPage) { + self.page = page + self.settings = CEWorkspaceSettingsData().propertiesOf(page.name) + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/AddCETaskView.swift b/CodeEdit/Features/CEWorkspace/Views/AddCETaskView.swift new file mode 100644 index 0000000000..08f459c091 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/AddCETaskView.swift @@ -0,0 +1,48 @@ +// +// AddCETaskView.swift +// CodeEdit +// +// Created by Axel Martinez on 3/4/24. +// + +import SwiftUI +import Collections + +struct AddCETaskView: View { + @Environment(\.dismiss) + var dismiss + + @Binding private var settings: CEWorkspaceSettingsData.TasksSettings + + @State private var task: CETask + + init(workingDirectory: String, settings: Binding) { + self._settings = settings + self._task = State(initialValue: CETask( + target: "My Mac", + workingDirectory: workingDirectory + )) + } + + var body: some View { + VStack(spacing: 0) { + CETaskFormView( + task: $task + ) + Spacer() + Divider() + HStack { + Button("Remove...") { + self.dismiss() + } + Spacer() + Button("Done") { + self.settings.items.append(task) + self.dismiss() + } + .disabled(task.isInvalid) + } + .padding() + } + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/CETaskFormView.swift b/CodeEdit/Features/CEWorkspace/Views/CETaskFormView.swift new file mode 100644 index 0000000000..75b6ab588c --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/CETaskFormView.swift @@ -0,0 +1,86 @@ +// +// CETaskFormView.swift +// CodeEdit +// +// Created by Axel Martinez on 12/4/24. +// + +import SwiftUI + +struct CETaskFormView: View { + @Binding var task: CETask + + @State private var selectedItemId: UUID? + + var body: some View { + Form { + Section { + TextField(text: $task.name) { + Text("Name") + } + Picker("Target", selection: $task.target) { + Text("My Mac") + .tag("My Mac") + } + } + Section { + TextField(text: $task.command) { + Text("Task") + } + TextField(text: $task.workingDirectory) { + Text("Working Directory") + } + } + Section(content: { + List(selection: $selectedItemId) { + ForEach($task.environmentVariables) { env in + EnvironmentVariableListItem( + item: env, + selectedItemId: $selectedItemId, + deleteHandler: removeEnv + ) + } + } + .frame(minHeight: 56) + .overlay { + if task.environmentVariables.isEmpty { + Text("No environment variables") + .foregroundStyle(Color(.secondaryLabelColor)) + } + } + .actionBar { + Button { + self.task.environmentVariables.append(CETask.EnvironmentVariable()) + } label: { + Image(systemName: "plus") + } + Divider() + Button { + removeSelectedEnv() + } label: { + Image(systemName: "minus") + } + .disabled(selectedItemId == nil) + } + .onDeleteCommand { + removeSelectedEnv() + } + }, header: { + Text("Environment Variables") + }) + } + .formStyle(.grouped) + } + + func removeSelectedEnv() { + if let selectedItemId = selectedItemId { + removeEnv(id: selectedItemId) + } + } + + func removeEnv(id: UUID) { + self.task.environmentVariables.removeAll(where: { + $0.id == id + }) + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/CEWorkspaceSettingsPageView.swift b/CodeEdit/Features/CEWorkspace/Views/CEWorkspaceSettingsPageView.swift new file mode 100644 index 0000000000..367fe14276 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/CEWorkspaceSettingsPageView.swift @@ -0,0 +1,57 @@ +// +// CEWorkspaceSettingsPageView.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import SwiftUI + +struct CEWorkspaceSettingsPageView: View { + var page: CEWorkspaceSettingsPage + var searchText: String + + init(_ page: CEWorkspaceSettingsPage, searchText: String) { + self.page = page + self.searchText = searchText + } + + var body: some View { + NavigationLink(value: page) { + Label { + page.name.rawValue.highlightOccurrences(self.searchText) + .padding(.leading, 2) + } icon: { + Group { + switch page.icon { + case .system(let name): + Image(systemName: name) + .resizable() + .aspectRatio(contentMode: .fit) + case .symbol(let name): + Image(symbol: name) + .resizable() + .aspectRatio(contentMode: .fit) + case .asset(let name): + Image(name) + .resizable() + .aspectRatio(contentMode: .fit) + case .none: EmptyView() + } + } + .shadow(color: Color(NSColor.black).opacity(0.25), radius: 0.5, y: 0.5) + .padding(2.5) + .foregroundColor(.white) + .frame(width: 20, height: 20) + .background( + RoundedRectangle( + cornerRadius: 5, + style: .continuous + ) + .fill((page.baseColor ?? .white).gradient) + .shadow(color: Color(NSColor.black).opacity(0.25), radius: 0.5, y: 0.5) + ) + } + } + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/CEWorkspaceSettingsView.swift b/CodeEdit/Features/CEWorkspace/Views/CEWorkspaceSettingsView.swift new file mode 100644 index 0000000000..14ef0e6963 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/CEWorkspaceSettingsView.swift @@ -0,0 +1,58 @@ +// +// CEWorkspaceSettingsView.swift +// CodeEdit +// +// Created by Axel Martinez on 26/3/24. +// + +import SwiftUI +import CodeEditSymbols + +/// A struct for settings +struct CEWorkspaceSettingsView: View { + @ObservedObject var settings: CEWorkspaceSettings + + @StateObject var viewModel = SettingsViewModel() + @State private var selectedPage: CEWorkspaceSettingsPage = Self.pages[0].page + @State private var searchText: String = "" + + let window: NSWindow? + let workspace: WorkspaceDocument + + static var pages: [PageAndCEWorkspaceSettings] = [ + .init( + CEWorkspaceSettingsPage( + .project, + baseColor: .gray, + icon: .system("gear") + ) + ), + .init( + CEWorkspaceSettingsPage( + .tasks, + baseColor: .blue, + icon: .system("at") + ) + ) + ] + + var body: some View { + VStack(spacing: 0) { + TasksCEWorkspaceSettingsView( + workspace: workspace, + projectSettings: $settings.preferences.project, + settings: $settings.preferences.tasks + ) + Spacer() + Divider() + HStack { + Spacer() + Button("Done") { + window?.close() + } + } + .padding() + } + .environmentObject(viewModel) + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/EditCETaskView.swift b/CodeEdit/Features/CEWorkspace/Views/EditCETaskView.swift new file mode 100644 index 0000000000..f7ea2b99f4 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/EditCETaskView.swift @@ -0,0 +1,41 @@ +// +// EditCETaskView.swift +// CodeEdit +// +// Created by Axel Martinez on 12/4/24. +// + +import SwiftUI +import Collections + +struct EditCETaskView: View { + @Environment(\.dismiss) + var dismiss + + @Binding var task: CETask + @Binding var settings: CEWorkspaceSettingsData.TasksSettings + + var body: some View { + VStack(spacing: 0) { + CETaskFormView( + task: $task + ) + Spacer() + Divider() + HStack { + Button("Remove...") { + self.settings.items.removeAll(where: { + $0.id == self.task.id + }) + self.dismiss() + } + Spacer() + Button("Done") { + self.dismiss() + } + .disabled(task.isInvalid) + } + .padding() + } + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/EnvironmentVariableListItem.swift b/CodeEdit/Features/CEWorkspace/Views/EnvironmentVariableListItem.swift new file mode 100644 index 0000000000..2ec51aa93d --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/EnvironmentVariableListItem.swift @@ -0,0 +1,70 @@ +// +// EnvironmentVariableListItem.swift +// CodeEdit +// +// Created by Axel Martinez on 9/4/24. +// + +import SwiftUI + +struct EnvironmentVariableListItem: View { + @FocusState private var isKeyFocused: Bool + + @Binding var item: CETask.EnvironmentVariable + @Binding var selectedItemId: UUID? + + /// State variables added to prevent an exception when deleting the item in the onChange event. + @State var name: String + @State var value: String + + var delete: (UUID) -> Void + + init( + item: Binding, + selectedItemId: Binding, + deleteHandler: @escaping (UUID) -> Void + ) { + self.delete = deleteHandler + + self._name = State(wrappedValue: item.name.wrappedValue) + self._value = State(wrappedValue: item.value.wrappedValue) + self._item = item + self._selectedItemId = selectedItemId + } + + var body: some View { + HStack { + TextField("", text: $name) + .focused($isKeyFocused) + .disableAutocorrection(true) + .autocorrectionDisabled() + .labelsHidden() + .frame(width: 120) + .onAppear { + if item.name.isEmpty { + isKeyFocused = true + } + } + Divider() + TextField("", text: $value) + .disableAutocorrection(true) + .autocorrectionDisabled() + .labelsHidden() + } + .onChange(of: isKeyFocused) { isFocused in + if isFocused { + if selectedItemId != item.id { + selectedItemId = item.id + } + } else { + if name.isEmpty { + selectedItemId = nil + delete(item.id) + } else { + item.name = name + item.value = value + } + } + } + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/Pages/ProjectCEWorkspaceSettingsView.swift b/CodeEdit/Features/CEWorkspace/Views/Pages/ProjectCEWorkspaceSettingsView.swift new file mode 100644 index 0000000000..47902d53fd --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/Pages/ProjectCEWorkspaceSettingsView.swift @@ -0,0 +1,28 @@ +// +// GeneralCEWorkspaceSettingsView.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import SwiftUI + +/// A view that implements the `Project` workspace settings page +struct ProjectCEWorkspaceSettingsView: View { + @State var settings: CEWorkspaceSettingsData.ProjectSettings + + var body: some View { + SettingsForm { + Section { + projectName + } + } + } +} + +/// The extension of the view with all the preferences +private extension ProjectCEWorkspaceSettingsView { + private var projectName: some View { + TextField("Name", text: $settings.projectName) + } +} diff --git a/CodeEdit/Features/CEWorkspace/Views/Pages/TasksCEWorkspaceSettingsView.swift b/CodeEdit/Features/CEWorkspace/Views/Pages/TasksCEWorkspaceSettingsView.swift new file mode 100644 index 0000000000..cb2758f289 --- /dev/null +++ b/CodeEdit/Features/CEWorkspace/Views/Pages/TasksCEWorkspaceSettingsView.swift @@ -0,0 +1,80 @@ +// +// TasksCEWorkspaceSettingsView.swift +// CodeEdit +// +// Created by Axel Martinez on 27/3/24. +// + +import SwiftUI + +struct TasksCEWorkspaceSettingsView: View { + let workspace: WorkspaceDocument + + // TODO: Separate Project Settings from Task Settings + @Binding var projectSettings: CEWorkspaceSettingsData.ProjectSettings + @Binding var settings: CEWorkspaceSettingsData.TasksSettings + + @State private var selectedTaskId: UUID? + @State private var addTaskSheetPresented: Bool = false + + var body: some View { + SettingsForm { + Section { + TextField("Name", text: $projectSettings.projectName) + Toggle("Tasks", isOn: $settings.enabled) + } + Section( + content: { + if settings.items.isEmpty { + Text("No tasks") + .foregroundColor(.secondary) + .frame(maxWidth: .infinity, alignment: .center) + } else { + ForEach(settings.items) { task in + HStack { + Text(task.name) + Spacer() + Group { + Text(task.command) + Image(systemName: "chevron.right") + } + .font(.system(.body, design: .monospaced)) + } + .contentShape(Rectangle()) + .onTapGesture { + self.selectedTaskId = task.id + self.addTaskSheetPresented.toggle() + } + } + } + }, header: { + Text("Tasks") + }, footer: { + HStack { + Spacer() + Button("Add Task...") { + self.selectedTaskId = nil + self.addTaskSheetPresented.toggle() + } + } + } + ) + } + .scrollDisabled(true) + .sheet(isPresented: $addTaskSheetPresented, content: { + if let selectedIndex = settings.items.firstIndex(where: { + $0.id == selectedTaskId + }) { + EditCETaskView( + task: $settings.items[selectedIndex], + settings: $settings + ) + } else { + AddCETaskView( + workingDirectory: workspace.fileURL?.relativePath ?? "", + settings: $settings + ) + } + }) + } +} diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift index 25a703a626..73f6550f2c 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift @@ -18,6 +18,8 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs var observers: [NSKeyValueObservation] = [] var workspace: WorkspaceDocument? + var workspaceSettings: CEWorkspaceSettings? + var workspaceSettingsWindow: NSWindow? var quickOpenPanel: SearchPanel? var commandPalettePanel: SearchPanel? var navigatorSidebarViewModel: NavigatorSidebarViewModel? @@ -29,6 +31,9 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs init(window: NSWindow, workspace: WorkspaceDocument) { super.init(window: window) self.workspace = workspace + self.workspaceSettings = CEWorkspaceSettings( + workspaceDocument: workspace + ) setupSplitView(with: workspace) let view = CodeEditSplitView(controller: splitViewController).ignoresSafeArea() diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift index 3a367e6601..4cf51eb3da 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift @@ -111,6 +111,31 @@ extension CodeEditWindowController { .isEmpty self.setDocumentEdited(hasEditedDocuments) } + + @IBAction func openWorkspaceSettings(_ sender: Any) { + guard let workspaceSettings, let window = window, let workspace = workspace else { + return + } + + if let workspaceSettingsWindow, workspaceSettingsWindow.isVisible { + workspaceSettingsWindow.makeKeyAndOrderFront(self) + } else { + let settingsWindow = NSWindow() + self.workspaceSettingsWindow = settingsWindow + let contentView = CEWorkspaceSettingsView( + settings: workspaceSettings, + window: settingsWindow, + workspace: workspace + ) + + settingsWindow.contentView = NSHostingView(rootView: contentView) + settingsWindow.titlebarAppearsTransparent = true + settingsWindow.setContentSize(NSSize(width: 515, height: 515)) + settingsWindow.makeKeyAndOrderFront(self) + + window.addCenteredChildWindow(settingsWindow, over: window) + } + } } extension NSToolbarItem.Identifier { diff --git a/CodeEdit/Features/Settings/Models/SettingsSearchResult.swift b/CodeEdit/Features/Settings/Models/SettingsSearchResult.swift index 22f2ab2564..1911ba3e4d 100644 --- a/CodeEdit/Features/Settings/Models/SettingsSearchResult.swift +++ b/CodeEdit/Features/Settings/Models/SettingsSearchResult.swift @@ -5,11 +5,14 @@ // Created by Raymond Vleeshouwer on 17/06/23. // -import Foundation import SwiftUI // TODO: Extend this struct further to support setting "flashing" -class SettingsSearchResult: Identifiable { +final class SettingsSearchResult: Identifiable { + let id: UUID = UUID() + let pageFound: Bool + let pages: [SettingsPage] + init( pageFound: Bool, pages: [SettingsPage] @@ -17,9 +20,4 @@ class SettingsSearchResult: Identifiable { self.pageFound = pageFound self.pages = pages } - - let id: UUID = UUID() - - let pageFound: Bool - let pages: [SettingsPage] } diff --git a/CodeEdit/Features/WindowCommands/FileCommands.swift b/CodeEdit/Features/WindowCommands/FileCommands.swift index 117fe2048d..03b0c199e6 100644 --- a/CodeEdit/Features/WindowCommands/FileCommands.swift +++ b/CodeEdit/Features/WindowCommands/FileCommands.swift @@ -8,6 +8,10 @@ import SwiftUI struct FileCommands: Commands { + @Environment(\.openWindow) + private var openWindow + + @State var windowController: CodeEditWindowController? @FocusedObject var utilityAreaViewModel: UtilityAreaViewModel? @@ -79,6 +83,16 @@ struct FileCommands: Commands { Divider() + Button("Workspace Settings") { + NSApp.sendAction(#selector(CodeEditWindowController.openWorkspaceSettings(_:)), to: nil, from: nil) + } + .disabled(windowController?.workspace == nil) + .onReceive(NSApp.publisher(for: \.keyWindow)) { window in + windowController = window?.windowController as? CodeEditWindowController + } + + Divider() + Button("Save") { NSApp.sendAction(#selector(CodeEditWindowController.saveDocument(_:)), to: nil, from: nil) } diff --git a/CodeEdit/Utils/Extensions/NSWindow/NSWindow+Child.swift b/CodeEdit/Utils/Extensions/NSWindow/NSWindow+Child.swift new file mode 100644 index 0000000000..2686fb13d3 --- /dev/null +++ b/CodeEdit/Utils/Extensions/NSWindow/NSWindow+Child.swift @@ -0,0 +1,25 @@ +// +// NSWindow+Child.swift +// CodeEdit +// +// Created by Axel Martinez on 8/4/24. +// + +import AppKit + +extension NSWindow { + func addCenteredChildWindow(_ childWindow: NSWindow, over parentWindow: NSWindow) { + let parentFrame = parentWindow.frame + let parentCenterX = parentFrame.origin.x + (parentFrame.size.width / 2) + let parentCenterY = parentFrame.origin.y + (parentFrame.size.height / 2) + + let childWidth = childWindow.frame.size.width + let childHeight = childWindow.frame.size.height + let newChildOriginX = parentCenterX - (childWidth / 2) + let newChildOriginY = parentCenterY - (childHeight / 2) + + childWindow.setFrameOrigin(NSPoint(x: newChildOriginX, y: newChildOriginY)) + + parentWindow.addChildWindow(childWindow, ordered: .above) + } +}