diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 24c89ab47a..fd2141d790 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -120,7 +120,6 @@ 587B61012934170A00D5CD8F /* UnitTests_Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B61002934170A00D5CD8F /* UnitTests_Extensions.swift */; }; 587B612E293419B700D5CD8F /* CodeFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B612D293419B700D5CD8F /* CodeFileTests.swift */; }; 587B9D57292FC27A00AC7927 /* FolderMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9D54292FC27A00AC7927 /* FolderMonitor.swift */; }; - 587B9D9E29300ABD00AC7927 /* FontPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9D8729300ABD00AC7927 /* FontPickerView.swift */; }; 587B9D9F29300ABD00AC7927 /* SegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9D8829300ABD00AC7927 /* SegmentedControl.swift */; }; 587B9DA029300ABD00AC7927 /* PanelDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9D8929300ABD00AC7927 /* PanelDivider.swift */; }; 587B9DA229300ABD00AC7927 /* EffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587B9D8B29300ABD00AC7927 /* EffectView.swift */; }; @@ -233,49 +232,20 @@ 58D01C9A293167DC00C5B6B4 /* CodeEditKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D01C90293167DC00C5B6B4 /* CodeEditKeychain.swift */; }; 58D01C9B293167DC00C5B6B4 /* CodeEditKeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D01C91293167DC00C5B6B4 /* CodeEditKeychainConstants.swift */; }; 58D01C9D293167DC00C5B6B4 /* KeychainSwiftAccessOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D01C93293167DC00C5B6B4 /* KeychainSwiftAccessOptions.swift */; }; - 58F2EAE7292FB2B0004A9BDE /* GeneralPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAA2292FB2B0004A9BDE /* GeneralPreferencesView.swift */; }; - 58F2EAE8292FB2B0004A9BDE /* TerminalPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAA4292FB2B0004A9BDE /* TerminalPreferencesView.swift */; }; - 58F2EAE9292FB2B0004A9BDE /* SourceControlPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAA6292FB2B0004A9BDE /* SourceControlPreferencesView.swift */; }; - 58F2EAEA292FB2B0004A9BDE /* SourceControlGeneralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAA7292FB2B0004A9BDE /* SourceControlGeneralView.swift */; }; - 58F2EAEB292FB2B0004A9BDE /* SourceControlGitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAA8292FB2B0004A9BDE /* SourceControlGitView.swift */; }; 58F2EAEC292FB2B0004A9BDE /* IgnoredFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAAA292FB2B0004A9BDE /* IgnoredFiles.swift */; }; - 58F2EAED292FB2B0004A9BDE /* IgnoredFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAAB292FB2B0004A9BDE /* IgnoredFileView.swift */; }; - 58F2EAEE292FB2B0004A9BDE /* TextEditingPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAAD292FB2B0004A9BDE /* TextEditingPreferencesView.swift */; }; - 58F2EAEF292FB2B0004A9BDE /* ThemePreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAAF292FB2B0004A9BDE /* ThemePreferencesView.swift */; }; - 58F2EAF0292FB2B0004A9BDE /* PreviewThemeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAB0292FB2B0004A9BDE /* PreviewThemeView.swift */; }; - 58F2EAF1292FB2B0004A9BDE /* TerminalThemeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAB1292FB2B0004A9BDE /* TerminalThemeView.swift */; }; - 58F2EAF2292FB2B0004A9BDE /* EditorThemeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAB2292FB2B0004A9BDE /* EditorThemeView.swift */; }; - 58F2EAF3292FB2B0004A9BDE /* ThemePreviewIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAB3292FB2B0004A9BDE /* ThemePreviewIcon.swift */; }; - 58F2EAF4292FB2B0004A9BDE /* ThemeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAB5292FB2B0004A9BDE /* ThemeModel.swift */; }; - 58F2EAF5292FB2B0004A9BDE /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAB6292FB2B0004A9BDE /* Theme.swift */; }; - 58F2EAF6292FB2B0004A9BDE /* AccountPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAB8292FB2B0004A9BDE /* AccountPreferencesView.swift */; }; - 58F2EAF7292FB2B0004A9BDE /* SourceControlProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EABA292FB2B0004A9BDE /* SourceControlProvider.swift */; }; - 58F2EAF8292FB2B0004A9BDE /* AccountListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EABC292FB2B0004A9BDE /* AccountListItemView.swift */; }; - 58F2EAF9292FB2B0004A9BDE /* AccountSelectionDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EABD292FB2B0004A9BDE /* AccountSelectionDialog.swift */; }; - 58F2EAFA292FB2B0004A9BDE /* GitLabHostedLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAC0292FB2B0004A9BDE /* GitLabHostedLoginView.swift */; }; - 58F2EAFB292FB2B0004A9BDE /* GitLabLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAC1292FB2B0004A9BDE /* GitLabLoginView.swift */; }; - 58F2EAFC292FB2B0004A9BDE /* GitAccountItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAC2292FB2B0004A9BDE /* GitAccountItemView.swift */; }; - 58F2EAFD292FB2B0004A9BDE /* GitHubLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAC4292FB2B0004A9BDE /* GitHubLoginView.swift */; }; - 58F2EAFE292FB2B0004A9BDE /* GitHubEnterpriseLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAC5292FB2B0004A9BDE /* GitHubEnterpriseLoginView.swift */; }; - 58F2EAFF292FB2B0004A9BDE /* KeybindingsPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAC7292FB2B0004A9BDE /* KeybindingsPreferencesView.swift */; }; - 58F2EB00292FB2B0004A9BDE /* LocationsPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAC9292FB2B0004A9BDE /* LocationsPreferencesView.swift */; }; + 58F2EAEF292FB2B0004A9BDE /* ThemeSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAAF292FB2B0004A9BDE /* ThemeSettingsView.swift */; }; 58F2EB02292FB2B0004A9BDE /* Loopable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EACD292FB2B0004A9BDE /* Loopable.swift */; }; 58F2EB03292FB2B0004A9BDE /* Documentation.docc in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EACE292FB2B0004A9BDE /* Documentation.docc */; }; - 58F2EB04292FB2B0004A9BDE /* SourceControlPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD1292FB2B0004A9BDE /* SourceControlPreferences.swift */; }; - 58F2EB05292FB2B0004A9BDE /* AppPreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD2292FB2B0004A9BDE /* AppPreferencesModel.swift */; }; - 58F2EB06292FB2B0004A9BDE /* KeybindingsPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD4292FB2B0004A9BDE /* KeybindingsPreferences.swift */; }; - 58F2EB07292FB2B0004A9BDE /* GeneralPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD6292FB2B0004A9BDE /* GeneralPreferences.swift */; }; - 58F2EB08292FB2B0004A9BDE /* TextEditingPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD8292FB2B0004A9BDE /* TextEditingPreferences.swift */; }; - 58F2EB09292FB2B0004A9BDE /* TerminalPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EADA292FB2B0004A9BDE /* TerminalPreferences.swift */; }; - 58F2EB0A292FB2B0004A9BDE /* AppPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EADB292FB2B0004A9BDE /* AppPreferences.swift */; }; - 58F2EB0B292FB2B0004A9BDE /* AccountsPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EADD292FB2B0004A9BDE /* AccountsPreferences.swift */; }; - 58F2EB0C292FB2B0004A9BDE /* SourceControlAccounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EADE292FB2B0004A9BDE /* SourceControlAccounts.swift */; }; - 58F2EB0D292FB2B0004A9BDE /* ThemePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAE0292FB2B0004A9BDE /* ThemePreferences.swift */; }; + 58F2EB04292FB2B0004A9BDE /* SourceControlSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD1292FB2B0004A9BDE /* SourceControlSettings.swift */; }; + 58F2EB05292FB2B0004A9BDE /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD2292FB2B0004A9BDE /* Settings.swift */; }; + 58F2EB06292FB2B0004A9BDE /* KeybindingsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD4292FB2B0004A9BDE /* KeybindingsSettings.swift */; }; + 58F2EB07292FB2B0004A9BDE /* GeneralSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD6292FB2B0004A9BDE /* GeneralSettings.swift */; }; + 58F2EB08292FB2B0004A9BDE /* TextEditingSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAD8292FB2B0004A9BDE /* TextEditingSettings.swift */; }; + 58F2EB09292FB2B0004A9BDE /* TerminalSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EADA292FB2B0004A9BDE /* TerminalSettings.swift */; }; + 58F2EB0A292FB2B0004A9BDE /* SettingsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EADB292FB2B0004A9BDE /* SettingsData.swift */; }; + 58F2EB0B292FB2B0004A9BDE /* AccountsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EADD292FB2B0004A9BDE /* AccountsSettings.swift */; }; + 58F2EB0D292FB2B0004A9BDE /* ThemeSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAE0292FB2B0004A9BDE /* ThemeSettings.swift */; }; 58F2EB0E292FB2B0004A9BDE /* SoftwareUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAE1292FB2B0004A9BDE /* SoftwareUpdater.swift */; }; - 58F2EB0F292FB2B0004A9BDE /* PreferencesSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAE3292FB2B0004A9BDE /* PreferencesSection.swift */; }; - 58F2EB10292FB2B0004A9BDE /* PreferencesColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAE4292FB2B0004A9BDE /* PreferencesColorPicker.swift */; }; - 58F2EB11292FB2B0004A9BDE /* PreferencesToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAE5292FB2B0004A9BDE /* PreferencesToolbar.swift */; }; - 58F2EB12292FB2B0004A9BDE /* PreferencesPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F2EAE6292FB2B0004A9BDE /* PreferencesPlaceholderView.swift */; }; 58F2EB17292FB74D004A9BDE /* CodeEditTextView in Frameworks */ = {isa = PBXBuildFile; productRef = 58F2EB16292FB74D004A9BDE /* CodeEditTextView */; }; 58F2EB1A292FB91C004A9BDE /* Preferences in Frameworks */ = {isa = PBXBuildFile; productRef = 58F2EB19292FB91C004A9BDE /* Preferences */; }; 58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 58F2EB1D292FB954004A9BDE /* Sparkle */; }; @@ -283,6 +253,7 @@ 58FD7609291EA1CB0051D6E4 /* CommandPaletteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD7607291EA1CB0051D6E4 /* CommandPaletteView.swift */; }; 5C4BB1E128212B1E00A92FB2 /* World.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4BB1E028212B1E00A92FB2 /* World.swift */; }; 6C05A8AF284D0CA3007F4EAA /* WorkspaceDocument+Listeners.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C05A8AE284D0CA3007F4EAA /* WorkspaceDocument+Listeners.swift */; }; + 6C0D0C6829E861B000AE4D3F /* SettingsSidebarFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C0D0C6729E861B000AE4D3F /* SettingsSidebarFix.swift */; }; 6C147C4029A328BC0089B630 /* SplitViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C147C3F29A328560089B630 /* SplitViewData.swift */; }; 6C147C4129A328BF0089B630 /* TabGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C147C3E29A3281D0089B630 /* TabGroup.swift */; }; 6C147C4229A328C10089B630 /* TabGroupData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C147C3D29A3281D0089B630 /* TabGroupData.swift */; }; @@ -307,6 +278,7 @@ 6C53AAD829A6C4FD00EE9ED6 /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C53AAD729A6C4FD00EE9ED6 /* SplitView.swift */; }; 6C5AB9D729C1496E003B5F96 /* SceneID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C5AB9D629C1496E003B5F96 /* SceneID.swift */; }; 6C5B63DE29C76213005454BA /* WindowCodeFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C5B63DD29C76213005454BA /* WindowCodeFileView.swift */; }; + 6C5FDF7A29E6160000BC08C0 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C5FDF7929E6160000BC08C0 /* AppSettings.swift */; }; 6C7256D729A3D7D000C2D3E0 /* SplitViewControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7256D629A3D7D000C2D3E0 /* SplitViewControllerView.swift */; }; 6C81916729B3E80700B75C92 /* ModifierKeysObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C81916629B3E80700B75C92 /* ModifierKeysObserver.swift */; }; 6C81916B29B41DD300B75C92 /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 6C81916A29B41DD300B75C92 /* DequeModule */; }; @@ -326,12 +298,22 @@ 6CBD1BC62978DE53006639D5 /* Font+Caption3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBD1BC52978DE53006639D5 /* Font+Caption3.swift */; }; 6CC9E4B229B5669900C97388 /* Environment+ActiveTabGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC9E4B129B5669900C97388 /* Environment+ActiveTabGroup.swift */; }; 6CDA84AD284C1BA000C1CC3A /* TabBarContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CDA84AC284C1BA000C1CC3A /* TabBarContextMenu.swift */; }; + 6CDEFC9629E22C2700B7C684 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 6CDEFC9529E22C2700B7C684 /* Introspect */; }; 6CFF967429BEBCC300182D6F /* FindCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFF967329BEBCC300182D6F /* FindCommands.swift */; }; 6CFF967629BEBCD900182D6F /* FileCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CFF967529BEBCD900182D6F /* FileCommands.swift */; }; 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 */; }; + 850C631029D6B01D00E1444C /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850C630F29D6B01D00E1444C /* SettingsView.swift */; }; + 850C631229D6B03400E1444C /* SettingsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850C631129D6B03400E1444C /* SettingsPage.swift */; }; + B6041F4D29D7A4E9000F3454 /* SettingsPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6041F4C29D7A4E9000F3454 /* SettingsPageView.swift */; }; + B6041F5229D7D6D6000F3454 /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6041F5129D7D6D6000F3454 /* SettingsWindow.swift */; }; B60BE8BD297A167600841125 /* AcknowledgementRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60BE8BC297A167600841125 /* AcknowledgementRowView.swift */; }; + B61A606129F188AB009B43F9 /* ExternalLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B61A606029F188AB009B43F9 /* ExternalLink.swift */; }; + B61A606929F4481A009B43F9 /* MonospacedFontPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B61A606829F4481A009B43F9 /* MonospacedFontPicker.swift */; }; + B61DA9DF29D929E100BF4A43 /* GeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B61DA9DE29D929E100BF4A43 /* GeneralSettingsView.swift */; }; + B640A99E29E2184700715F20 /* SettingsForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = B640A99D29E2184700715F20 /* SettingsForm.swift */; }; + B640A9A129E2188F00715F20 /* SettingsDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B640A9A029E2188F00715F20 /* SettingsDetailsView.swift */; }; B658FB3427DA9E1000EA4DBD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B658FB3327DA9E1000EA4DBD /* Assets.xcassets */; }; B658FB3727DA9E1000EA4DBD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B658FB3627DA9E1000EA4DBD /* Preview Assets.xcassets */; }; B66A4E4529C8E86D004573B4 /* CommandsFixes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66A4E4429C8E86D004573B4 /* CommandsFixes.swift */; }; @@ -346,8 +328,28 @@ B6C6A43029771F7100A3D28F /* TabBarItemBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C6A42F29771F7100A3D28F /* TabBarItemBackground.swift */; }; B6D7EA592971078500301FAC /* InspectorSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6D7EA582971078500301FAC /* InspectorSection.swift */; }; B6D7EA5C297107DD00301FAC /* InspectorField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6D7EA5B297107DD00301FAC /* InspectorField.swift */; }; + B6E41C7029DD157F0088F9F4 /* AccountsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E41C6F29DD157F0088F9F4 /* AccountsSettingsView.swift */; }; + B6E41C7429DD40010088F9F4 /* View+HideSidebarToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E41C7329DD40010088F9F4 /* View+HideSidebarToggle.swift */; }; + B6E41C7929DE02800088F9F4 /* AccountSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E41C7829DE02800088F9F4 /* AccountSelectionView.swift */; }; + B6E41C7C29DE2B110088F9F4 /* AccoundsSettingsProviderRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E41C7B29DE2B110088F9F4 /* AccoundsSettingsProviderRow.swift */; }; + B6E41C8B29DE7AE80088F9F4 /* AccountsSettingsSigninView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E41C8A29DE7AE80088F9F4 /* AccountsSettingsSigninView.swift */; }; + B6E41C8F29DE9CD80088F9F4 /* AccountsSettingsDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E41C8E29DE9CD80088F9F4 /* AccountsSettingsDetailsView.swift */; }; + B6E41C9429DEAE260088F9F4 /* SourceControlAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E41C9329DEAE260088F9F4 /* SourceControlAccount.swift */; }; + B6EA1FE529DA33DB001BF195 /* ThemeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA1FE429DA33DB001BF195 /* ThemeModel.swift */; }; + B6EA1FE729DA341D001BF195 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA1FE629DA341D001BF195 /* Theme.swift */; }; + B6EA1FF529DA380E001BF195 /* TextEditingSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA1FF429DA380E001BF195 /* TextEditingSettingsView.swift */; }; + B6EA1FF829DB78DB001BF195 /* ThemeSettingThemeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA1FF729DB78DB001BF195 /* ThemeSettingThemeRow.swift */; }; + B6EA1FFB29DB78F6001BF195 /* ThemeSettingsThemeDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA1FFA29DB78F6001BF195 /* ThemeSettingsThemeDetails.swift */; }; + B6EA1FFD29DB792C001BF195 /* ThemeSettingsColorPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA1FFC29DB792C001BF195 /* ThemeSettingsColorPreview.swift */; }; + B6EA200029DB7966001BF195 /* SettingsColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA1FFF29DB7966001BF195 /* SettingsColorPicker.swift */; }; + B6EA200229DB7F81001BF195 /* View+ConstrainHeightToWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EA200129DB7F81001BF195 /* View+ConstrainHeightToWindow.swift */; }; B6EE989027E8879A00CDD8AB /* InspectorSidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE988F27E8879A00CDD8AB /* InspectorSidebarView.swift */; }; B6EE989227E887C600CDD8AB /* InspectorSidebarToolbarTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EE989127E887C600CDD8AB /* InspectorSidebarToolbarTop.swift */; }; + B6F0517029D9E36800D72287 /* LocationsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F0516F29D9E36800D72287 /* LocationsSettingsView.swift */; }; + B6F0517729D9E3AD00D72287 /* SourceControlGeneralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F0517629D9E3AD00D72287 /* SourceControlGeneralView.swift */; }; + B6F0517929D9E3C900D72287 /* SourceControlGitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F0517829D9E3C900D72287 /* SourceControlGitView.swift */; }; + B6F0517B29D9E46400D72287 /* SourceControlSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F0517A29D9E46400D72287 /* SourceControlSettingsView.swift */; }; + B6F0517D29D9E4B100D72287 /* TerminalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F0517C29D9E4B100D72287 /* TerminalSettingsView.swift */; }; D7012EE827E757850001E1EF /* FindNavigatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7012EE727E757850001E1EF /* FindNavigatorView.swift */; }; D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */; }; D7211D4727E06BFE008F2ED7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D7211D4927E06BFE008F2ED7 /* Localizable.strings */; }; @@ -526,7 +528,6 @@ 587B61002934170A00D5CD8F /* UnitTests_Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnitTests_Extensions.swift; sourceTree = ""; }; 587B612D293419B700D5CD8F /* CodeFileTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeFileTests.swift; sourceTree = ""; }; 587B9D54292FC27A00AC7927 /* FolderMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderMonitor.swift; sourceTree = ""; }; - 587B9D8729300ABD00AC7927 /* FontPickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontPickerView.swift; sourceTree = ""; }; 587B9D8829300ABD00AC7927 /* SegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentedControl.swift; sourceTree = ""; }; 587B9D8929300ABD00AC7927 /* PanelDivider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PanelDivider.swift; sourceTree = ""; }; 587B9D8B29300ABD00AC7927 /* EffectView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EffectView.swift; sourceTree = ""; }; @@ -640,53 +641,25 @@ 58D01C90293167DC00C5B6B4 /* CodeEditKeychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeEditKeychain.swift; sourceTree = ""; }; 58D01C91293167DC00C5B6B4 /* CodeEditKeychainConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeEditKeychainConstants.swift; sourceTree = ""; }; 58D01C93293167DC00C5B6B4 /* KeychainSwiftAccessOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwiftAccessOptions.swift; sourceTree = ""; }; - 58F2EAA2292FB2B0004A9BDE /* GeneralPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesView.swift; sourceTree = ""; }; - 58F2EAA4292FB2B0004A9BDE /* TerminalPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TerminalPreferencesView.swift; sourceTree = ""; }; - 58F2EAA6292FB2B0004A9BDE /* SourceControlPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceControlPreferencesView.swift; sourceTree = ""; }; - 58F2EAA7292FB2B0004A9BDE /* SourceControlGeneralView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceControlGeneralView.swift; sourceTree = ""; }; - 58F2EAA8292FB2B0004A9BDE /* SourceControlGitView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceControlGitView.swift; sourceTree = ""; }; 58F2EAAA292FB2B0004A9BDE /* IgnoredFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IgnoredFiles.swift; sourceTree = ""; }; - 58F2EAAB292FB2B0004A9BDE /* IgnoredFileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IgnoredFileView.swift; sourceTree = ""; }; - 58F2EAAD292FB2B0004A9BDE /* TextEditingPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEditingPreferencesView.swift; sourceTree = ""; }; - 58F2EAAF292FB2B0004A9BDE /* ThemePreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemePreferencesView.swift; sourceTree = ""; }; - 58F2EAB0292FB2B0004A9BDE /* PreviewThemeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewThemeView.swift; sourceTree = ""; }; - 58F2EAB1292FB2B0004A9BDE /* TerminalThemeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TerminalThemeView.swift; sourceTree = ""; }; - 58F2EAB2292FB2B0004A9BDE /* EditorThemeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditorThemeView.swift; sourceTree = ""; }; - 58F2EAB3292FB2B0004A9BDE /* ThemePreviewIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemePreviewIcon.swift; sourceTree = ""; }; - 58F2EAB5292FB2B0004A9BDE /* ThemeModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeModel.swift; sourceTree = ""; }; - 58F2EAB6292FB2B0004A9BDE /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; - 58F2EAB8292FB2B0004A9BDE /* AccountPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountPreferencesView.swift; sourceTree = ""; }; - 58F2EABA292FB2B0004A9BDE /* SourceControlProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceControlProvider.swift; sourceTree = ""; }; - 58F2EABC292FB2B0004A9BDE /* AccountListItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountListItemView.swift; sourceTree = ""; }; - 58F2EABD292FB2B0004A9BDE /* AccountSelectionDialog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountSelectionDialog.swift; sourceTree = ""; }; - 58F2EAC0292FB2B0004A9BDE /* GitLabHostedLoginView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitLabHostedLoginView.swift; sourceTree = ""; }; - 58F2EAC1292FB2B0004A9BDE /* GitLabLoginView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitLabLoginView.swift; sourceTree = ""; }; - 58F2EAC2292FB2B0004A9BDE /* GitAccountItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitAccountItemView.swift; sourceTree = ""; }; - 58F2EAC4292FB2B0004A9BDE /* GitHubLoginView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubLoginView.swift; sourceTree = ""; }; - 58F2EAC5292FB2B0004A9BDE /* GitHubEnterpriseLoginView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubEnterpriseLoginView.swift; sourceTree = ""; }; - 58F2EAC7292FB2B0004A9BDE /* KeybindingsPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeybindingsPreferencesView.swift; sourceTree = ""; }; - 58F2EAC9292FB2B0004A9BDE /* LocationsPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationsPreferencesView.swift; sourceTree = ""; }; + 58F2EAAF292FB2B0004A9BDE /* ThemeSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeSettingsView.swift; sourceTree = ""; }; 58F2EACD292FB2B0004A9BDE /* Loopable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Loopable.swift; sourceTree = ""; }; 58F2EACE292FB2B0004A9BDE /* Documentation.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Documentation.docc; sourceTree = ""; }; - 58F2EAD1292FB2B0004A9BDE /* SourceControlPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceControlPreferences.swift; sourceTree = ""; }; - 58F2EAD2292FB2B0004A9BDE /* AppPreferencesModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppPreferencesModel.swift; sourceTree = ""; }; - 58F2EAD4292FB2B0004A9BDE /* KeybindingsPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeybindingsPreferences.swift; sourceTree = ""; }; - 58F2EAD6292FB2B0004A9BDE /* GeneralPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferences.swift; sourceTree = ""; }; - 58F2EAD8292FB2B0004A9BDE /* TextEditingPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEditingPreferences.swift; sourceTree = ""; }; - 58F2EADA292FB2B0004A9BDE /* TerminalPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TerminalPreferences.swift; sourceTree = ""; }; - 58F2EADB292FB2B0004A9BDE /* AppPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppPreferences.swift; sourceTree = ""; }; - 58F2EADD292FB2B0004A9BDE /* AccountsPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsPreferences.swift; sourceTree = ""; }; - 58F2EADE292FB2B0004A9BDE /* SourceControlAccounts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceControlAccounts.swift; sourceTree = ""; }; - 58F2EAE0292FB2B0004A9BDE /* ThemePreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemePreferences.swift; sourceTree = ""; }; + 58F2EAD1292FB2B0004A9BDE /* SourceControlSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceControlSettings.swift; sourceTree = ""; }; + 58F2EAD2292FB2B0004A9BDE /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; + 58F2EAD4292FB2B0004A9BDE /* KeybindingsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeybindingsSettings.swift; sourceTree = ""; }; + 58F2EAD6292FB2B0004A9BDE /* GeneralSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralSettings.swift; sourceTree = ""; }; + 58F2EAD8292FB2B0004A9BDE /* TextEditingSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEditingSettings.swift; sourceTree = ""; }; + 58F2EADA292FB2B0004A9BDE /* TerminalSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TerminalSettings.swift; sourceTree = ""; }; + 58F2EADB292FB2B0004A9BDE /* SettingsData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsData.swift; sourceTree = ""; }; + 58F2EADD292FB2B0004A9BDE /* AccountsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsSettings.swift; sourceTree = ""; }; + 58F2EAE0292FB2B0004A9BDE /* ThemeSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeSettings.swift; sourceTree = ""; }; 58F2EAE1292FB2B0004A9BDE /* SoftwareUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoftwareUpdater.swift; sourceTree = ""; }; - 58F2EAE3292FB2B0004A9BDE /* PreferencesSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesSection.swift; sourceTree = ""; }; - 58F2EAE4292FB2B0004A9BDE /* PreferencesColorPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesColorPicker.swift; sourceTree = ""; }; - 58F2EAE5292FB2B0004A9BDE /* PreferencesToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesToolbar.swift; sourceTree = ""; }; - 58F2EAE6292FB2B0004A9BDE /* PreferencesPlaceholderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesPlaceholderView.swift; sourceTree = ""; }; 58FD7605291EA1CB0051D6E4 /* CommandPaletteViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandPaletteViewModel.swift; sourceTree = ""; }; 58FD7607291EA1CB0051D6E4 /* CommandPaletteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandPaletteView.swift; sourceTree = ""; }; 5C4BB1E028212B1E00A92FB2 /* World.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = World.swift; sourceTree = ""; }; 6C05A8AE284D0CA3007F4EAA /* WorkspaceDocument+Listeners.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WorkspaceDocument+Listeners.swift"; sourceTree = ""; }; + 6C0D0C6729E861B000AE4D3F /* SettingsSidebarFix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSidebarFix.swift; sourceTree = ""; }; 6C147C3D29A3281D0089B630 /* TabGroupData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabGroupData.swift; sourceTree = ""; }; 6C147C3E29A3281D0089B630 /* TabGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabGroup.swift; sourceTree = ""; }; 6C147C3F29A328560089B630 /* SplitViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewData.swift; sourceTree = ""; }; @@ -710,6 +683,7 @@ 6C53AAD729A6C4FD00EE9ED6 /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = ""; }; 6C5AB9D629C1496E003B5F96 /* SceneID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneID.swift; sourceTree = ""; }; 6C5B63DD29C76213005454BA /* WindowCodeFileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WindowCodeFileView.swift; sourceTree = ""; }; + 6C5FDF7929E6160000BC08C0 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; 6C7256D629A3D7D000C2D3E0 /* SplitViewControllerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewControllerView.swift; sourceTree = ""; }; 6C81916629B3E80700B75C92 /* ModifierKeysObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifierKeysObserver.swift; sourceTree = ""; }; 6C82D6B229BFD88700495C54 /* NavigateCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigateCommands.swift; sourceTree = ""; }; @@ -729,7 +703,16 @@ 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 = ""; }; + 850C630F29D6B01D00E1444C /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + 850C631129D6B03400E1444C /* SettingsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPage.swift; sourceTree = ""; }; + B6041F4C29D7A4E9000F3454 /* SettingsPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPageView.swift; sourceTree = ""; }; + B6041F5129D7D6D6000F3454 /* SettingsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindow.swift; sourceTree = ""; }; B60BE8BC297A167600841125 /* AcknowledgementRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcknowledgementRowView.swift; sourceTree = ""; }; + B61A606029F188AB009B43F9 /* ExternalLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalLink.swift; sourceTree = ""; }; + B61A606829F4481A009B43F9 /* MonospacedFontPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonospacedFontPicker.swift; sourceTree = ""; }; + B61DA9DE29D929E100BF4A43 /* GeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsView.swift; sourceTree = ""; }; + B640A99D29E2184700715F20 /* SettingsForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsForm.swift; sourceTree = ""; }; + B640A9A029E2188F00715F20 /* SettingsDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailsView.swift; sourceTree = ""; }; B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CodeEdit.app; sourceTree = BUILT_PRODUCTS_DIR; }; B658FB3127DA9E0F00EA4DBD /* WorkspaceView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = WorkspaceView.swift; sourceTree = ""; tabWidth = 4; }; B658FB3327DA9E1000EA4DBD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -749,8 +732,28 @@ B6C6A42F29771F7100A3D28F /* TabBarItemBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarItemBackground.swift; sourceTree = ""; }; B6D7EA582971078500301FAC /* InspectorSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSection.swift; sourceTree = ""; }; B6D7EA5B297107DD00301FAC /* InspectorField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorField.swift; sourceTree = ""; }; + B6E41C6F29DD157F0088F9F4 /* AccountsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsSettingsView.swift; sourceTree = ""; }; + B6E41C7329DD40010088F9F4 /* View+HideSidebarToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+HideSidebarToggle.swift"; sourceTree = ""; }; + B6E41C7829DE02800088F9F4 /* AccountSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSelectionView.swift; sourceTree = ""; }; + B6E41C7B29DE2B110088F9F4 /* AccoundsSettingsProviderRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccoundsSettingsProviderRow.swift; sourceTree = ""; }; + B6E41C8A29DE7AE80088F9F4 /* AccountsSettingsSigninView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsSettingsSigninView.swift; sourceTree = ""; }; + B6E41C8E29DE9CD80088F9F4 /* AccountsSettingsDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsSettingsDetailsView.swift; sourceTree = ""; }; + B6E41C9329DEAE260088F9F4 /* SourceControlAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlAccount.swift; sourceTree = ""; }; + B6EA1FE429DA33DB001BF195 /* ThemeModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeModel.swift; sourceTree = ""; }; + B6EA1FE629DA341D001BF195 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; + B6EA1FF429DA380E001BF195 /* TextEditingSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEditingSettingsView.swift; sourceTree = ""; }; + B6EA1FF729DB78DB001BF195 /* ThemeSettingThemeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSettingThemeRow.swift; sourceTree = ""; }; + B6EA1FFA29DB78F6001BF195 /* ThemeSettingsThemeDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSettingsThemeDetails.swift; sourceTree = ""; }; + B6EA1FFC29DB792C001BF195 /* ThemeSettingsColorPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSettingsColorPreview.swift; sourceTree = ""; }; + B6EA1FFF29DB7966001BF195 /* SettingsColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsColorPicker.swift; sourceTree = ""; }; + B6EA200129DB7F81001BF195 /* View+ConstrainHeightToWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+ConstrainHeightToWindow.swift"; sourceTree = ""; }; B6EE988F27E8879A00CDD8AB /* InspectorSidebarView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = InspectorSidebarView.swift; sourceTree = ""; tabWidth = 4; }; B6EE989127E887C600CDD8AB /* InspectorSidebarToolbarTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorSidebarToolbarTop.swift; sourceTree = ""; }; + B6F0516F29D9E36800D72287 /* LocationsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsSettingsView.swift; sourceTree = ""; }; + B6F0517629D9E3AD00D72287 /* SourceControlGeneralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlGeneralView.swift; sourceTree = ""; }; + B6F0517829D9E3C900D72287 /* SourceControlGitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlGitView.swift; sourceTree = ""; }; + B6F0517A29D9E46400D72287 /* SourceControlSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlSettingsView.swift; sourceTree = ""; }; + B6F0517C29D9E4B100D72287 /* TerminalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSettingsView.swift; sourceTree = ""; }; D7012EE727E757850001E1EF /* FindNavigatorView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = FindNavigatorView.swift; sourceTree = ""; tabWidth = 4; }; D7211D4227E066CE008F2ED7 /* Localized+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Localized+Ex.swift"; sourceTree = ""; }; D7211D4827E06BFE008F2ED7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -782,6 +785,7 @@ 58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */, 6C147C4529A329350089B630 /* OrderedCollections in Frameworks */, 5879828A292ED15F0085B254 /* SwiftTerm in Frameworks */, + 6CDEFC9629E22C2700B7C684 /* Introspect in Frameworks */, 2816F594280CF50500DD548B /* CodeEditSymbols in Frameworks */, 6C81916B29B41DD300B75C92 /* DequeModule in Frameworks */, ); @@ -1077,21 +1081,21 @@ children = ( 582213EE2918345500EFE361 /* About */, 5878DA7D291862BC00DD95A3 /* Acknowledgements */, - 284DC84B2978B5EB00BF2770 /* Contributors */, - 58F2EA9E292FB2B0004A9BDE /* AppPreferences */, - 5878DAAB291D627C00DD95A3 /* PathBar */, 587B9D50292FC27A00AC7927 /* CodeEditExtension */, 587B9D7529300ABD00AC7927 /* CodeEditUI */, 58798244292E78D80085B254 /* CodeFile */, 58FD7603291EA1CB0051D6E4 /* Commands */, + 284DC84B2978B5EB00BF2770 /* Contributors */, 043C321227E31FE8006AE443 /* Documents */, 58798228292E30B90085B254 /* Feedback */, 587B9E0129301D8F00AC7927 /* Git */, B6EE988E27E8877C00CDD8AB /* InspectorSidebar */, 58A5DF9D29339F6400D1BD5D /* Keybindings */, 287776EA27E350A100D46668 /* NavigatorSidebar */, + 5878DAAB291D627C00DD95A3 /* PathBar */, 5878DAA0291AE76700DD95A3 /* QuickOpen */, 58798210292D92370085B254 /* Search */, + B67B270029D7868000FB9301 /* Settings */, 6C147C4729A329E50089B630 /* SplitView */, 588224FF292C280D00E83CDE /* StatusBar */, 287776EB27E350BA00D46668 /* Tabs */, @@ -1470,7 +1474,6 @@ children = ( 6CABB1A029C5593800340467 /* OverlayView.swift */, 587B9D8B29300ABD00AC7927 /* EffectView.swift */, - 587B9D8729300ABD00AC7927 /* FontPickerView.swift */, 587B9D9029300ABD00AC7927 /* HelpButton.swift */, 587B9D8D29300ABD00AC7927 /* OverlayPanel.swift */, 587B9D8929300ABD00AC7927 /* PanelDivider.swift */, @@ -1865,252 +1868,25 @@ path = KeyChain; sourceTree = ""; }; - 58F2EA9E292FB2B0004A9BDE /* AppPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAE2292FB2B0004A9BDE /* HelperViews */, - 58F2EACF292FB2B0004A9BDE /* Model */, - 58F2EAA0292FB2B0004A9BDE /* Sections */, - 58F2EAE6292FB2B0004A9BDE /* PreferencesPlaceholderView.swift */, - 58F2EAE1292FB2B0004A9BDE /* SoftwareUpdater.swift */, - ); - path = AppPreferences; - sourceTree = ""; - }; - 58F2EAA0292FB2B0004A9BDE /* Sections */ = { - isa = PBXGroup; - children = ( - 58F2EAA1292FB2B0004A9BDE /* GeneralPreferences */, - 58F2EAA3292FB2B0004A9BDE /* TerminalPreferences */, - 58F2EAA5292FB2B0004A9BDE /* SourceControlPreferences */, - 58F2EAAC292FB2B0004A9BDE /* TextEditingPreferences */, - 58F2EAAE292FB2B0004A9BDE /* ThemePreferences */, - 58F2EAB7292FB2B0004A9BDE /* AccountsPreferences */, - 58F2EAC6292FB2B0004A9BDE /* KeybindingsPreferences */, - 58F2EAC8292FB2B0004A9BDE /* LocationsPreferences */, - ); - path = Sections; - sourceTree = ""; - }; - 58F2EAA1292FB2B0004A9BDE /* GeneralPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAA2292FB2B0004A9BDE /* GeneralPreferencesView.swift */, - ); - path = GeneralPreferences; - sourceTree = ""; - }; - 58F2EAA3292FB2B0004A9BDE /* TerminalPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAA4292FB2B0004A9BDE /* TerminalPreferencesView.swift */, - ); - path = TerminalPreferences; - sourceTree = ""; - }; - 58F2EAA5292FB2B0004A9BDE /* SourceControlPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAA9292FB2B0004A9BDE /* Model */, - 58F2EAAB292FB2B0004A9BDE /* IgnoredFileView.swift */, - 58F2EAA6292FB2B0004A9BDE /* SourceControlPreferencesView.swift */, - 58F2EAA7292FB2B0004A9BDE /* SourceControlGeneralView.swift */, - 58F2EAA8292FB2B0004A9BDE /* SourceControlGitView.swift */, - ); - path = SourceControlPreferences; - sourceTree = ""; - }; - 58F2EAA9292FB2B0004A9BDE /* Model */ = { + 58F2EAA9292FB2B0004A9BDE /* Models */ = { isa = PBXGroup; children = ( + 58F2EAD1292FB2B0004A9BDE /* SourceControlSettings.swift */, 58F2EAAA292FB2B0004A9BDE /* IgnoredFiles.swift */, ); - path = Model; - sourceTree = ""; - }; - 58F2EAAC292FB2B0004A9BDE /* TextEditingPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAAD292FB2B0004A9BDE /* TextEditingPreferencesView.swift */, - ); - path = TextEditingPreferences; - sourceTree = ""; - }; - 58F2EAAE292FB2B0004A9BDE /* ThemePreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAB4292FB2B0004A9BDE /* Model */, - 58F2EAAF292FB2B0004A9BDE /* ThemePreferencesView.swift */, - 58F2EAB0292FB2B0004A9BDE /* PreviewThemeView.swift */, - 58F2EAB1292FB2B0004A9BDE /* TerminalThemeView.swift */, - 58F2EAB2292FB2B0004A9BDE /* EditorThemeView.swift */, - 58F2EAB3292FB2B0004A9BDE /* ThemePreviewIcon.swift */, - ); - path = ThemePreferences; - sourceTree = ""; - }; - 58F2EAB4292FB2B0004A9BDE /* Model */ = { - isa = PBXGroup; - children = ( - 58F2EAB6292FB2B0004A9BDE /* Theme.swift */, - 58F2EAB5292FB2B0004A9BDE /* ThemeModel.swift */, - ); - path = Model; - sourceTree = ""; - }; - 58F2EAB7292FB2B0004A9BDE /* AccountsPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EABB292FB2B0004A9BDE /* Accounts */, - 58F2EAB9292FB2B0004A9BDE /* Models */, - 58F2EAB8292FB2B0004A9BDE /* AccountPreferencesView.swift */, - ); - path = AccountsPreferences; - sourceTree = ""; - }; - 58F2EAB9292FB2B0004A9BDE /* Models */ = { - isa = PBXGroup; - children = ( - 58F2EABA292FB2B0004A9BDE /* SourceControlProvider.swift */, - ); path = Models; sourceTree = ""; }; - 58F2EABB292FB2B0004A9BDE /* Accounts */ = { + 58F2EAAE292FB2B0004A9BDE /* ThemeSettings */ = { isa = PBXGroup; children = ( - 58F2EABC292FB2B0004A9BDE /* AccountListItemView.swift */, - 58F2EABD292FB2B0004A9BDE /* AccountSelectionDialog.swift */, - 58F2EABE292FB2B0004A9BDE /* Login */, + B6F0518D29DA29F900D72287 /* Models */, + 58F2EAAF292FB2B0004A9BDE /* ThemeSettingsView.swift */, + B6EA1FF729DB78DB001BF195 /* ThemeSettingThemeRow.swift */, + B6EA1FFA29DB78F6001BF195 /* ThemeSettingsThemeDetails.swift */, + B6EA1FFC29DB792C001BF195 /* ThemeSettingsColorPreview.swift */, ); - path = Accounts; - sourceTree = ""; - }; - 58F2EABE292FB2B0004A9BDE /* Login */ = { - isa = PBXGroup; - children = ( - 58F2EABF292FB2B0004A9BDE /* Gitlab */, - 58F2EAC2292FB2B0004A9BDE /* GitAccountItemView.swift */, - 58F2EAC3292FB2B0004A9BDE /* Github */, - ); - path = Login; - sourceTree = ""; - }; - 58F2EABF292FB2B0004A9BDE /* Gitlab */ = { - isa = PBXGroup; - children = ( - 58F2EAC0292FB2B0004A9BDE /* GitLabHostedLoginView.swift */, - 58F2EAC1292FB2B0004A9BDE /* GitLabLoginView.swift */, - ); - path = Gitlab; - sourceTree = ""; - }; - 58F2EAC3292FB2B0004A9BDE /* Github */ = { - isa = PBXGroup; - children = ( - 58F2EAC4292FB2B0004A9BDE /* GitHubLoginView.swift */, - 58F2EAC5292FB2B0004A9BDE /* GitHubEnterpriseLoginView.swift */, - ); - path = Github; - sourceTree = ""; - }; - 58F2EAC6292FB2B0004A9BDE /* KeybindingsPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAC7292FB2B0004A9BDE /* KeybindingsPreferencesView.swift */, - ); - path = KeybindingsPreferences; - sourceTree = ""; - }; - 58F2EAC8292FB2B0004A9BDE /* LocationsPreferences */ = { - isa = PBXGroup; - children = ( - 58F2EAC9292FB2B0004A9BDE /* LocationsPreferencesView.swift */, - ); - path = LocationsPreferences; - sourceTree = ""; - }; - 58F2EACF292FB2B0004A9BDE /* Model */ = { - isa = PBXGroup; - children = ( - 58F2EADC292FB2B0004A9BDE /* Accounts */, - 58F2EAD5292FB2B0004A9BDE /* General */, - 58F2EAD3292FB2B0004A9BDE /* Keybindings */, - 58F2EAD0292FB2B0004A9BDE /* Source Control */, - 58F2EAD9292FB2B0004A9BDE /* Terminal */, - 58F2EAD7292FB2B0004A9BDE /* Text Editing */, - 58F2EADF292FB2B0004A9BDE /* Theme */, - 58F2EADB292FB2B0004A9BDE /* AppPreferences.swift */, - 58F2EAD2292FB2B0004A9BDE /* AppPreferencesModel.swift */, - ); - path = Model; - sourceTree = ""; - }; - 58F2EAD0292FB2B0004A9BDE /* Source Control */ = { - isa = PBXGroup; - children = ( - 58F2EAD1292FB2B0004A9BDE /* SourceControlPreferences.swift */, - ); - path = "Source Control"; - sourceTree = ""; - }; - 58F2EAD3292FB2B0004A9BDE /* Keybindings */ = { - isa = PBXGroup; - children = ( - 58F2EAD4292FB2B0004A9BDE /* KeybindingsPreferences.swift */, - ); - path = Keybindings; - sourceTree = ""; - }; - 58F2EAD5292FB2B0004A9BDE /* General */ = { - isa = PBXGroup; - children = ( - 58F2EAD6292FB2B0004A9BDE /* GeneralPreferences.swift */, - ); - path = General; - sourceTree = ""; - }; - 58F2EAD7292FB2B0004A9BDE /* Text Editing */ = { - isa = PBXGroup; - children = ( - 58F2EAD8292FB2B0004A9BDE /* TextEditingPreferences.swift */, - ); - path = "Text Editing"; - sourceTree = ""; - }; - 58F2EAD9292FB2B0004A9BDE /* Terminal */ = { - isa = PBXGroup; - children = ( - 58F2EADA292FB2B0004A9BDE /* TerminalPreferences.swift */, - ); - path = Terminal; - sourceTree = ""; - }; - 58F2EADC292FB2B0004A9BDE /* Accounts */ = { - isa = PBXGroup; - children = ( - 58F2EADD292FB2B0004A9BDE /* AccountsPreferences.swift */, - 58F2EADE292FB2B0004A9BDE /* SourceControlAccounts.swift */, - ); - path = Accounts; - sourceTree = ""; - }; - 58F2EADF292FB2B0004A9BDE /* Theme */ = { - isa = PBXGroup; - children = ( - 58F2EAE0292FB2B0004A9BDE /* ThemePreferences.swift */, - ); - path = Theme; - sourceTree = ""; - }; - 58F2EAE2292FB2B0004A9BDE /* HelperViews */ = { - isa = PBXGroup; - children = ( - 58F2EAE3292FB2B0004A9BDE /* PreferencesSection.swift */, - 58F2EAE4292FB2B0004A9BDE /* PreferencesColorPicker.swift */, - 58F2EAE5292FB2B0004A9BDE /* PreferencesToolbar.swift */, - ); - path = HelperViews; + path = ThemeSettings; sourceTree = ""; }; 58FD7603291EA1CB0051D6E4 /* Commands */ = { @@ -2235,6 +2011,42 @@ path = Text; sourceTree = ""; }; + B61DA9DD29D929BF00BF4A43 /* Pages */ = { + isa = PBXGroup; + children = ( + B6CF632629E5417C0085880A /* Keybindings */, + B6E41C6E29DD15540088F9F4 /* AccountsSettings */, + B6EA1FF329DA37D3001BF195 /* TextEditingSettings */, + B6F0516E29D9E35300D72287 /* LocationsSettings */, + B6F0516D29D9E34200D72287 /* SourceControlSettings */, + B6F0516C29D9E32700D72287 /* TerminalSettings */, + 58F2EAAE292FB2B0004A9BDE /* ThemeSettings */, + B61DA9E129D929F900BF4A43 /* GeneralSettings */, + ); + path = Pages; + sourceTree = ""; + }; + B61DA9E129D929F900BF4A43 /* GeneralSettings */ = { + isa = PBXGroup; + children = ( + B6CF632529E541520085880A /* Models */, + B61DA9DE29D929E100BF4A43 /* GeneralSettingsView.swift */, + ); + path = GeneralSettings; + sourceTree = ""; + }; + B640A9A329E218E300715F20 /* Models */ = { + isa = PBXGroup; + children = ( + 58F2EADB292FB2B0004A9BDE /* SettingsData.swift */, + 58F2EAD2292FB2B0004A9BDE /* Settings.swift */, + 6C5FDF7929E6160000BC08C0 /* AppSettings.swift */, + 6C0D0C6729E861B000AE4D3F /* SettingsSidebarFix.swift */, + 850C631129D6B03400E1444C /* SettingsPage.swift */, + ); + path = Models; + sourceTree = ""; + }; B658FB2327DA9E0F00EA4DBD = { isa = PBXGroup; children = ( @@ -2293,6 +2105,105 @@ path = "Preview Content"; sourceTree = ""; }; + B67B270029D7868000FB9301 /* Settings */ = { + isa = PBXGroup; + children = ( + B640A9A329E218E300715F20 /* Models */, + B6CF632A29E5436C0085880A /* Views */, + B61DA9DD29D929BF00BF4A43 /* Pages */, + B6041F5129D7D6D6000F3454 /* SettingsWindow.swift */, + 850C630F29D6B01D00E1444C /* SettingsView.swift */, + 58F2EAE1292FB2B0004A9BDE /* SoftwareUpdater.swift */, + ); + path = Settings; + sourceTree = ""; + }; + B6CF632529E541520085880A /* Models */ = { + isa = PBXGroup; + children = ( + 58F2EAD6292FB2B0004A9BDE /* GeneralSettings.swift */, + ); + path = Models; + sourceTree = ""; + }; + B6CF632629E5417C0085880A /* Keybindings */ = { + isa = PBXGroup; + children = ( + B6CF632729E5418A0085880A /* Models */, + ); + path = Keybindings; + sourceTree = ""; + }; + B6CF632729E5418A0085880A /* Models */ = { + isa = PBXGroup; + children = ( + 58F2EAD4292FB2B0004A9BDE /* KeybindingsSettings.swift */, + ); + path = Models; + sourceTree = ""; + }; + B6CF632829E541AC0085880A /* Models */ = { + isa = PBXGroup; + children = ( + 58F2EADA292FB2B0004A9BDE /* TerminalSettings.swift */, + ); + path = Models; + sourceTree = ""; + }; + B6CF632929E541C00085880A /* Models */ = { + isa = PBXGroup; + children = ( + 58F2EAD8292FB2B0004A9BDE /* TextEditingSettings.swift */, + ); + path = Models; + sourceTree = ""; + }; + B6CF632A29E5436C0085880A /* Views */ = { + isa = PBXGroup; + children = ( + B6041F4C29D7A4E9000F3454 /* SettingsPageView.swift */, + B640A99D29E2184700715F20 /* SettingsForm.swift */, + B640A9A029E2188F00715F20 /* SettingsDetailsView.swift */, + B6EA1FFF29DB7966001BF195 /* SettingsColorPicker.swift */, + B61A606829F4481A009B43F9 /* MonospacedFontPicker.swift */, + B61A606029F188AB009B43F9 /* ExternalLink.swift */, + B6EA200129DB7F81001BF195 /* View+ConstrainHeightToWindow.swift */, + B6E41C7329DD40010088F9F4 /* View+HideSidebarToggle.swift */, + ); + path = Views; + sourceTree = ""; + }; + B6E41C6E29DD15540088F9F4 /* AccountsSettings */ = { + isa = PBXGroup; + children = ( + B6E41C9229DEAE0A0088F9F4 /* Models */, + B6E41C6F29DD157F0088F9F4 /* AccountsSettingsView.swift */, + B6E41C7B29DE2B110088F9F4 /* AccoundsSettingsProviderRow.swift */, + B6E41C7829DE02800088F9F4 /* AccountSelectionView.swift */, + B6E41C8A29DE7AE80088F9F4 /* AccountsSettingsSigninView.swift */, + B6E41C8E29DE9CD80088F9F4 /* AccountsSettingsDetailsView.swift */, + ); + path = AccountsSettings; + sourceTree = ""; + }; + B6E41C9229DEAE0A0088F9F4 /* Models */ = { + isa = PBXGroup; + children = ( + B6E41C9329DEAE260088F9F4 /* SourceControlAccount.swift */, + 58F2EADD292FB2B0004A9BDE /* AccountsSettings.swift */, + ); + path = Models; + sourceTree = ""; + }; + B6EA1FF329DA37D3001BF195 /* TextEditingSettings */ = { + isa = PBXGroup; + children = ( + B6CF632929E541C00085880A /* Models */, + B6EA1FF429DA380E001BF195 /* TextEditingSettingsView.swift */, + ); + path = TextEditingSettings; + sourceTree = ""; + }; B6EE988E27E8877C00CDD8AB /* InspectorSidebar */ = { isa = PBXGroup; children = ( @@ -2306,6 +2217,44 @@ path = InspectorSidebar; sourceTree = ""; }; + B6F0516C29D9E32700D72287 /* TerminalSettings */ = { + isa = PBXGroup; + children = ( + B6CF632829E541AC0085880A /* Models */, + B6F0517C29D9E4B100D72287 /* TerminalSettingsView.swift */, + ); + path = TerminalSettings; + sourceTree = ""; + }; + B6F0516D29D9E34200D72287 /* SourceControlSettings */ = { + isa = PBXGroup; + children = ( + 58F2EAA9292FB2B0004A9BDE /* Models */, + B6F0517A29D9E46400D72287 /* SourceControlSettingsView.swift */, + B6F0517629D9E3AD00D72287 /* SourceControlGeneralView.swift */, + B6F0517829D9E3C900D72287 /* SourceControlGitView.swift */, + ); + path = SourceControlSettings; + sourceTree = ""; + }; + B6F0516E29D9E35300D72287 /* LocationsSettings */ = { + isa = PBXGroup; + children = ( + B6F0516F29D9E36800D72287 /* LocationsSettingsView.swift */, + ); + path = LocationsSettings; + sourceTree = ""; + }; + B6F0518D29DA29F900D72287 /* Models */ = { + isa = PBXGroup; + children = ( + 58F2EAE0292FB2B0004A9BDE /* ThemeSettings.swift */, + B6EA1FE629DA341D001BF195 /* Theme.swift */, + B6EA1FE429DA33DB001BF195 /* ThemeModel.swift */, + ); + path = Models; + sourceTree = ""; + }; D7012EE627E757660001E1EF /* FindNavigator */ = { isa = PBXGroup; children = ( @@ -2373,6 +2322,7 @@ 58F2EB1D292FB954004A9BDE /* Sparkle */, 6C147C4429A329350089B630 /* OrderedCollections */, 6C81916A29B41DD300B75C92 /* DequeModule */, + 6CDEFC9529E22C2700B7C684 /* Introspect */, ); productName = CodeEdit; productReference = B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */; @@ -2464,6 +2414,7 @@ 58F2EB1C292FB954004A9BDE /* XCRemoteSwiftPackageReference "Sparkle" */, 583E529A29361BAB001AB554 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */, 6C147C4329A329350089B630 /* XCRemoteSwiftPackageReference "swift-collections" */, + 6CDEFC9429E22C2700B7C684 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */, ); productRefGroup = B658FB2D27DA9E0F00EA4DBD /* Products */; projectDirPath = ""; @@ -2593,18 +2544,18 @@ 587B9E7D29301D8F00AC7927 /* GitHubPullRequestRouter.swift in Sources */, 5878DA842918642000DD95A3 /* ParsePackagesResolved.swift in Sources */, 5879824F292E78D80085B254 /* CodeFileView.swift in Sources */, + 6C0D0C6829E861B000AE4D3F /* SettingsSidebarFix.swift in Sources */, 587B9E8429301D8F00AC7927 /* GitHubUser.swift in Sources */, 2072FA13280D74ED00C7F8D4 /* HistoryInspectorModel.swift in Sources */, 587B9DA029300ABD00AC7927 /* PanelDivider.swift in Sources */, 58822534292C280D00E83CDE /* CursorLocation.swift in Sources */, 201169E52837B40300F92B46 /* SourceControlNavigatorRepositoriesView.swift in Sources */, 587B9E6A29301D8F00AC7927 /* GitLabPermissions.swift in Sources */, + B6EA1FF529DA380E001BF195 /* TextEditingSettingsView.swift in Sources */, D7DC4B76298FFBE900D6C83D /* OutlintViewController+OutlineTableViewCellDelegate.swift in Sources */, 587B9E9229301D8F00AC7927 /* BitBucketAccount.swift in Sources */, - 58F2EAFE292FB2B0004A9BDE /* GitHubEnterpriseLoginView.swift in Sources */, DE513F52281B672D002260B9 /* TabBarAccessory.swift in Sources */, 2813F93927ECC4C300E305E4 /* NavigatorSidebarView.swift in Sources */, - 58F2EAF7292FB2B0004A9BDE /* SourceControlProvider.swift in Sources */, 587B9E8A29301D8F00AC7927 /* GitHubIssue.swift in Sources */, 6CAAF69429BCD78600A1F48A /* (null) in Sources */, DE513F54281DE5D0002260B9 /* TabBarXcode.swift in Sources */, @@ -2612,6 +2563,7 @@ 6C14CEB028777D3C001468FE /* FindNavigatorListViewController.swift in Sources */, 587B9E7F29301D8F00AC7927 /* GitHubUserRouter.swift in Sources */, 20EBB50D280C383700F3A5DA /* LanguageType.swift in Sources */, + B61A606129F188AB009B43F9 /* ExternalLink.swift in Sources */, 587B9E9729301D8F00AC7927 /* BitBucketAccount+Token.swift in Sources */, 587B9E7729301D8F00AC7927 /* String+PercentEncoding.swift in Sources */, 587B9E5B29301D8F00AC7927 /* GitCheckoutBranchView.swift in Sources */, @@ -2620,19 +2572,19 @@ 201169D92837B31200F92B46 /* SourceControlSearchToolbar.swift in Sources */, 587B9DA729300ABD00AC7927 /* HelpButton.swift in Sources */, 6C5B63DE29C76213005454BA /* WindowCodeFileView.swift in Sources */, - 58F2EB08292FB2B0004A9BDE /* TextEditingPreferences.swift in Sources */, + 58F2EB08292FB2B0004A9BDE /* TextEditingSettings.swift in Sources */, 201169DB2837B34000F92B46 /* SourceControlNavigatorChangedFileView.swift in Sources */, 5882252E292C280D00E83CDE /* StatusBarMaximizeButton.swift in Sources */, 6C4104E9297C970F00F472BA /* AboutDefaultView.swift in Sources */, 587B9E6F29301D8F00AC7927 /* GitLabProjectAccess.swift in Sources */, 587B9E6929301D8F00AC7927 /* GitLabEvent.swift in Sources */, 587B9E5E29301D8F00AC7927 /* GitLabCommitRouter.swift in Sources */, - 58F2EAF4292FB2B0004A9BDE /* ThemeModel.swift in Sources */, - 58F2EB0D292FB2B0004A9BDE /* ThemePreferences.swift in Sources */, + 58F2EB0D292FB2B0004A9BDE /* ThemeSettings.swift in Sources */, 587B9D9F29300ABD00AC7927 /* SegmentedControl.swift in Sources */, 6C7256D729A3D7D000C2D3E0 /* SplitViewControllerView.swift in Sources */, + B6EA1FE529DA33DB001BF195 /* ThemeModel.swift in Sources */, + B6EA200029DB7966001BF195 /* SettingsColorPicker.swift in Sources */, 58FD7609291EA1CB0051D6E4 /* CommandPaletteView.swift in Sources */, - 58F2EAFC292FB2B0004A9BDE /* GitAccountItemView.swift in Sources */, 587B9E8F29301D8F00AC7927 /* BitBucketUserRouter.swift in Sources */, B66A4E5129C917D5004573B4 /* AboutWindow.swift in Sources */, 58F2EB03292FB2B0004A9BDE /* Documentation.docc in Sources */, @@ -2642,17 +2594,15 @@ D7E201AE27E8B3C000CB86D0 /* String+Ranges.swift in Sources */, 6C48D8F72972E5F300D6D205 /* WindowObserver.swift in Sources */, 587B9E6B29301D8F00AC7927 /* GitLabAvatarURL.swift in Sources */, - 58F2EB10292FB2B0004A9BDE /* PreferencesColorPicker.swift in Sources */, 58798233292E30B90085B254 /* FeedbackToolbar.swift in Sources */, 587B9E6829301D8F00AC7927 /* GitLabAccountModel.swift in Sources */, 5878DAA7291AE76700DD95A3 /* QuickOpenViewModel.swift in Sources */, 6CFF967429BEBCC300182D6F /* FindCommands.swift in Sources */, - 58F2EAED292FB2B0004A9BDE /* IgnoredFileView.swift in Sources */, 587B9E6529301D8F00AC7927 /* GitLabGroupAccess.swift in Sources */, 6C91D57229B176FF0059A90D /* TabManager.swift in Sources */, 6C82D6BC29C00CD900495C54 /* FirstResponderPropertyWrapper.swift in Sources */, 58D01C9B293167DC00C5B6B4 /* CodeEditKeychainConstants.swift in Sources */, - 58F2EB0C292FB2B0004A9BDE /* SourceControlAccounts.swift in Sources */, + B640A99E29E2184700715F20 /* SettingsForm.swift in Sources */, 20EBB50F280C389300F3A5DA /* FileInspectorModel.swift in Sources */, 5878DAB1291D627C00DD95A3 /* PathBarComponent.swift in Sources */, 58822533292C280D00E83CDE /* StatusBarTabType.swift in Sources */, @@ -2670,7 +2620,6 @@ 587B9E9429301D8F00AC7927 /* BitBucketTokenConfiguration.swift in Sources */, 581BFB672926431000D251EC /* WelcomeWindowView.swift in Sources */, 58A5DFA329339F6400D1BD5D /* CommandManager.swift in Sources */, - 58F2EAF5292FB2B0004A9BDE /* Theme.swift in Sources */, 2072FA1C280D874000C7F8D4 /* IndentUsing.swift in Sources */, 58798284292ED0FB0085B254 /* TerminalEmulatorView.swift in Sources */, 6C82D6B329BFD88700495C54 /* NavigateCommands.swift in Sources */, @@ -2681,26 +2630,31 @@ 201169E72837B5CA00F92B46 /* SourceControlModel.swift in Sources */, 58822528292C280D00E83CDE /* StatusBarEncodingSelector.swift in Sources */, B6C6A43029771F7100A3D28F /* TabBarItemBackground.swift in Sources */, - 58F2EB0F292FB2B0004A9BDE /* PreferencesSection.swift in Sources */, + B6F0517D29D9E4B100D72287 /* TerminalSettingsView.swift in Sources */, 587B9E8C29301D8F00AC7927 /* GitHubOpenness.swift in Sources */, 587B9E8229301D8F00AC7927 /* GitHubPreviewHeader.swift in Sources */, 58F2EB02292FB2B0004A9BDE /* Loopable.swift in Sources */, 28B8F884280FFE4600596236 /* NSTableView+Background.swift in Sources */, - 58F2EB06292FB2B0004A9BDE /* KeybindingsPreferences.swift in Sources */, + 58F2EB06292FB2B0004A9BDE /* KeybindingsSettings.swift in Sources */, 200412EF280F3EAC00BCAF5C /* HistoryInspectorNoHistoryView.swift in Sources */, 587B9E8E29301D8F00AC7927 /* BitBucketRepositoryRouter.swift in Sources */, + B61A606929F4481A009B43F9 /* MonospacedFontPicker.swift in Sources */, + B61DA9DF29D929E100BF4A43 /* GeneralSettingsView.swift in Sources */, B6D7EA5C297107DD00301FAC /* InspectorField.swift in Sources */, 043C321427E31FF6006AE443 /* CodeEditDocumentController.swift in Sources */, 587B9E6629301D8F00AC7927 /* GitLabProjectHook.swift in Sources */, 587B9E9329301D8F00AC7927 /* BitBucketOAuthConfiguration.swift in Sources */, 6C18620A298BF5A800C663EA /* RecentProjectsListView.swift in Sources */, - 58F2EB0A292FB2B0004A9BDE /* AppPreferences.swift in Sources */, + 58F2EB0A292FB2B0004A9BDE /* SettingsData.swift in Sources */, 20EBB503280C327C00F3A5DA /* HistoryInspectorView.swift in Sources */, + B6EA1FE729DA341D001BF195 /* Theme.swift in Sources */, 587B9E7529301D8F00AC7927 /* String+QueryParameters.swift in Sources */, 58798219292D92370085B254 /* SearchModeModel.swift in Sources */, B66A4E4F29C917B8004573B4 /* WelcomeWindow.swift in Sources */, 58D01C9D293167DC00C5B6B4 /* KeychainSwiftAccessOptions.swift in Sources */, + B6E41C8B29DE7AE80088F9F4 /* AccountsSettingsSigninView.swift in Sources */, 6C2C155A29B4F4CC00EA60A5 /* Variadic.swift in Sources */, + B6E41C8F29DE9CD80088F9F4 /* AccountsSettingsDetailsView.swift in Sources */, 5882252B292C280D00E83CDE /* StatusBarCursorLocationLabel.swift in Sources */, 587B9D57292FC27A00AC7927 /* FolderMonitor.swift in Sources */, 58798252292E78D80085B254 /* ImageFileView.swift in Sources */, @@ -2711,11 +2665,12 @@ 587B9E8729301D8F00AC7927 /* GitHubRepositories.swift in Sources */, 587B9DA329300ABD00AC7927 /* SettingsTextEditor.swift in Sources */, 2072FA1A280D872600C7F8D4 /* LineEndings.swift in Sources */, + B6F0517B29D9E46400D72287 /* SourceControlSettingsView.swift in Sources */, 6C147C4D29A32AA30089B630 /* WorkspaceTabGroupView.swift in Sources */, - 58F2EAFB292FB2B0004A9BDE /* GitLabLoginView.swift in Sources */, 587B9E7B29301D8F00AC7927 /* GitHubRouter.swift in Sources */, 201169E22837B3D800F92B46 /* SourceControlNavigatorChangesView.swift in Sources */, 20EBB507280C32D300F3A5DA /* QuickHelpInspectorView.swift in Sources */, + 850C631029D6B01D00E1444C /* SettingsView.swift in Sources */, DE6405A62817734700881FDF /* TabBarNative.swift in Sources */, 587B9E5C29301D8F00AC7927 /* Parameters.swift in Sources */, 58798235292E30B90085B254 /* FeedbackModel.swift in Sources */, @@ -2731,46 +2686,45 @@ D7211D4327E066CE008F2ED7 /* Localized+Ex.swift in Sources */, 581BFB692926431000D251EC /* WelcomeActionView.swift in Sources */, 20D839AE280E0CA700B27357 /* HistoryPopoverView.swift in Sources */, - 58F2EAFD292FB2B0004A9BDE /* GitHubLoginView.swift in Sources */, + B6E41C7029DD157F0088F9F4 /* AccountsSettingsView.swift in Sources */, 587D9B782933BF5700BF7490 /* Interface.swift in Sources */, 6CFF967A29BEBD2400182D6F /* ViewCommands.swift in Sources */, 2072FA1E280D891500C7F8D4 /* FileLocation.swift in Sources */, + 850C631229D6B03400E1444C /* SettingsPage.swift in Sources */, 587B9E6729301D8F00AC7927 /* GitLabEventData.swift in Sources */, B66A4E4529C8E86D004573B4 /* CommandsFixes.swift in Sources */, 5882252F292C280D00E83CDE /* StatusBarClearButton.swift in Sources */, - 58F2EB04292FB2B0004A9BDE /* SourceControlPreferences.swift in Sources */, + 58F2EB04292FB2B0004A9BDE /* SourceControlSettings.swift in Sources */, 582213F0291834A500EFE361 /* AboutView.swift in Sources */, - 58F2EB12292FB2B0004A9BDE /* PreferencesPlaceholderView.swift in Sources */, 6CC9E4B229B5669900C97388 /* Environment+ActiveTabGroup.swift in Sources */, 58822526292C280D00E83CDE /* StatusBarBreakpointButton.swift in Sources */, 58D01C96293167DC00C5B6B4 /* Date+Formatted.swift in Sources */, B66A4E5629C918A0004573B4 /* SceneID.swift in Sources */, 6C53AAD829A6C4FD00EE9ED6 /* SplitView.swift in Sources */, B66A4E5329C91831004573B4 /* CodeEditCommands.swift in Sources */, - 587B9D9E29300ABD00AC7927 /* FontPickerView.swift in Sources */, 58822529292C280D00E83CDE /* StatusBarLineEndSelector.swift in Sources */, - 58F2EAE7292FB2B0004A9BDE /* GeneralPreferencesView.swift in Sources */, 5C4BB1E128212B1E00A92FB2 /* World.swift in Sources */, D7E201B227E8D50000CB86D0 /* FindNavigatorModeSelector.swift in Sources */, 287776E927E34BC700D46668 /* TabBarView.swift in Sources */, B60BE8BD297A167600841125 /* AcknowledgementRowView.swift in Sources */, + B6F0517029D9E36800D72287 /* LocationsSettingsView.swift in Sources */, 587B9E6329301D8F00AC7927 /* GitLabAccount.swift in Sources */, 285FEC7027FE4B9800E57D53 /* OutlineTableViewCell.swift in Sources */, 6CB9144B29BEC7F100BC47F2 /* (null) in Sources */, - 58F2EAF8292FB2B0004A9BDE /* AccountListItemView.swift in Sources */, 587B9E7429301D8F00AC7927 /* URL+URLParameters.swift in Sources */, 581BFB6B2926431000D251EC /* RecentProjectItem.swift in Sources */, 6C5AB9D729C1496E003B5F96 /* SceneID.swift in Sources */, 587B9DA429300ABD00AC7927 /* OverlayPanel.swift in Sources */, - 58F2EAEE292FB2B0004A9BDE /* TextEditingPreferencesView.swift in Sources */, 58D01C95293167DC00C5B6B4 /* Bundle+Info.swift in Sources */, 587D9B762933BF5700BF7490 /* Mocks.swift in Sources */, B6C6A42A297716A500A3D28F /* TabBarItemCloseButton.swift in Sources */, 58A5DF7D2931787A00D1BD5D /* ShellClient.swift in Sources */, 5879821A292D92370085B254 /* SearchResultModel.swift in Sources */, + B6F0517729D9E3AD00D72287 /* SourceControlGeneralView.swift in Sources */, 587B9E8929301D8F00AC7927 /* GitHubGist.swift in Sources */, 0485EB1F27E7458B00138301 /* WorkspaceCodeFileView.swift in Sources */, 58D01C94293167DC00C5B6B4 /* Color+HEX.swift in Sources */, + B640A9A129E2188F00715F20 /* SettingsDetailsView.swift in Sources */, 58798251292E78D80085B254 /* OtherFileView.swift in Sources */, 587D9B752933BF5700BF7490 /* FileItem.swift in Sources */, 587B9E7929301D8F00AC7927 /* GitHubIssueRouter.swift in Sources */, @@ -2784,30 +2738,31 @@ 5882252C292C280D00E83CDE /* StatusBarDrawer.swift in Sources */, 2847019E27FDDF7600F87B6B /* OutlineView.swift in Sources */, 6C14CEB32877A68F001468FE /* FindNavigatorMatchListCell.swift in Sources */, - 58F2EAF6292FB2B0004A9BDE /* AccountPreferencesView.swift in Sources */, 20EBB501280C325D00F3A5DA /* FileInspectorView.swift in Sources */, 58822531292C280D00E83CDE /* View+isHovering.swift in Sources */, 587B9E9929301D8F00AC7927 /* GitChangedFile.swift in Sources */, - 58F2EAF2292FB2B0004A9BDE /* EditorThemeView.swift in Sources */, 6C147C4B29A32A7B0089B630 /* Environment+SplitEditor.swift in Sources */, 2897E1C72979A29200741E32 /* OffsettableScrollView.swift in Sources */, 58F2EB0E292FB2B0004A9BDE /* SoftwareUpdater.swift in Sources */, 587B9E9529301D8F00AC7927 /* BitBucketUser.swift in Sources */, 587B9E7C29301D8F00AC7927 /* GitHubRepositoryRouter.swift in Sources */, 286471AB27ED51FD0039369D /* ProjectNavigatorView.swift in Sources */, + B6E41C7C29DE2B110088F9F4 /* AccoundsSettingsProviderRow.swift in Sources */, 6CAAF68A29BC9C2300A1F48A /* (null) in Sources */, 6CFF967629BEBCD900182D6F /* FileCommands.swift in Sources */, B685DE7929CC9CCD002860C8 /* StatusBarIcon.swift in Sources */, 587B9DA629300ABD00AC7927 /* ToolbarBranchPicker.swift in Sources */, - 58F2EB05292FB2B0004A9BDE /* AppPreferencesModel.swift in Sources */, + 58F2EB05292FB2B0004A9BDE /* Settings.swift in Sources */, 6CBD1BC62978DE53006639D5 /* Font+Caption3.swift in Sources */, - 58F2EAF1292FB2B0004A9BDE /* TerminalThemeView.swift in Sources */, 287776EF27E3515300D46668 /* TabBarItemView.swift in Sources */, + B6E41C9429DEAE260088F9F4 /* SourceControlAccount.swift in Sources */, 2806E9022979588B000040F4 /* Contributor.swift in Sources */, 58D01C98293167DC00C5B6B4 /* String+RemoveOccurrences.swift in Sources */, 5878DAA8291AE76700DD95A3 /* QuickOpenItem.swift in Sources */, 58FD7608291EA1CB0051D6E4 /* CommandPaletteViewModel.swift in Sources */, 5878DA872918642F00DD95A3 /* AcknowledgementsViewModel.swift in Sources */, + B6E41C7929DE02800088F9F4 /* AccountSelectionView.swift in Sources */, + B6EA1FFB29DB78F6001BF195 /* ThemeSettingsThemeDetails.swift in Sources */, 587B9E7029301D8F00AC7927 /* GitLabUser.swift in Sources */, 04660F6A27E51E5C00477777 /* CodeEditWindowController.swift in Sources */, 58798218292D92370085B254 /* String+SafeOffset.swift in Sources */, @@ -2816,16 +2771,21 @@ 6CABB19E29C5591D00340467 /* NSTableViewWrapper.swift in Sources */, 5879821B292D92370085B254 /* SearchResultMatchModel.swift in Sources */, 587B9E5929301D8F00AC7927 /* GitCheckoutBranchView+CheckoutBranch.swift in Sources */, - 58F2EB09292FB2B0004A9BDE /* TerminalPreferences.swift in Sources */, + 58F2EB09292FB2B0004A9BDE /* TerminalSettings.swift in Sources */, 587D9B742933BF5700BF7490 /* FileItem+Array.swift in Sources */, 587D9B772933BF5700BF7490 /* Live.swift in Sources */, + B6EA200229DB7F81001BF195 /* View+ConstrainHeightToWindow.swift in Sources */, 6C48D8F42972DB1A00D6D205 /* Env+Window.swift in Sources */, + 6C5FDF7A29E6160000BC08C0 /* AppSettings.swift in Sources */, 2072FA18280D871200C7F8D4 /* TextEncoding.swift in Sources */, - 58F2EAF3292FB2B0004A9BDE /* ThemePreviewIcon.swift in Sources */, - 58F2EB07292FB2B0004A9BDE /* GeneralPreferences.swift in Sources */, + 58F2EB07292FB2B0004A9BDE /* GeneralSettings.swift in Sources */, + B6041F4D29D7A4E9000F3454 /* SettingsPageView.swift in Sources */, 587B9E9A29301D8F00AC7927 /* GitType.swift in Sources */, 58D01C97293167DC00C5B6B4 /* String+SHA256.swift in Sources */, + B6EA1FFD29DB792C001BF195 /* ThemeSettingsColorPreview.swift in Sources */, 2806E904297958B9000040F4 /* ContributorRowView.swift in Sources */, + B6041F5229D7D6D6000F3454 /* SettingsWindow.swift in Sources */, + B6EA1FF829DB78DB001BF195 /* ThemeSettingThemeRow.swift in Sources */, 587B9E7629301D8F00AC7927 /* GitTime.swift in Sources */, 2072FA16280D83A500C7F8D4 /* FileTypeList.swift in Sources */, 587B9E5D29301D8F00AC7927 /* GitLabUserRouter.swift in Sources */, @@ -2834,7 +2794,7 @@ 6C147C4929A32A080089B630 /* EditorView.swift in Sources */, 6C147C4129A328BF0089B630 /* TabGroup.swift in Sources */, B6D7EA592971078500301FAC /* InspectorSection.swift in Sources */, - 58F2EAEF292FB2B0004A9BDE /* ThemePreferencesView.swift in Sources */, + 58F2EAEF292FB2B0004A9BDE /* ThemeSettingsView.swift in Sources */, B6EE989027E8879A00CDD8AB /* InspectorSidebarView.swift in Sources */, 587B9DA229300ABD00AC7927 /* EffectView.swift in Sources */, 6C97EBCC2978760400302F95 /* AcknowledgementsWindowController.swift in Sources */, @@ -2842,6 +2802,7 @@ 58798250292E78D80085B254 /* CodeFile.swift in Sources */, 5878DAA5291AE76700DD95A3 /* QuickOpenView.swift in Sources */, 201169D72837B2E300F92B46 /* SourceControlNavigatorView.swift in Sources */, + B6F0517929D9E3C900D72287 /* SourceControlGitView.swift in Sources */, 587B9E8329301D8F00AC7927 /* GitHubPullRequest.swift in Sources */, 5878DA82291863F900DD95A3 /* AcknowledgementsView.swift in Sources */, 587B9E8529301D8F00AC7927 /* GitHubReview.swift in Sources */, @@ -2850,14 +2811,13 @@ 587B9E5A29301D8F00AC7927 /* GitCloneView.swift in Sources */, 58D01C99293167DC00C5B6B4 /* String+MD5.swift in Sources */, 20EBB505280C329800F3A5DA /* HistoryInspectorItemView.swift in Sources */, - 58F2EAEA292FB2B0004A9BDE /* SourceControlGeneralView.swift in Sources */, 5878DAB2291D627C00DD95A3 /* PathBarView.swift in Sources */, 043C321627E3201F006AE443 /* WorkspaceDocument.swift in Sources */, 58F2EAEC292FB2B0004A9BDE /* IgnoredFiles.swift in Sources */, - 58F2EAEB292FB2B0004A9BDE /* SourceControlGitView.swift in Sources */, 58798236292E30B90085B254 /* FeedbackType.swift in Sources */, 587B9E6D29301D8F00AC7927 /* GitLabEventNote.swift in Sources */, 587B9E9129301D8F00AC7927 /* BitBucketOAuthRouter.swift in Sources */, + B6E41C7429DD40010088F9F4 /* View+HideSidebarToggle.swift in Sources */, 58822532292C280D00E83CDE /* StatusBarViewModel.swift in Sources */, 043BCF03281DA18A000AC47C /* WorkspaceDocument+Search.swift in Sources */, 58822527292C280D00E83CDE /* StatusBarIndentSelector.swift in Sources */, @@ -2870,33 +2830,25 @@ 587B9DA529300ABD00AC7927 /* PressActionsModifier.swift in Sources */, 28B0A19827E385C300B73177 /* NavigatorSidebarToolbarTop.swift in Sources */, 587B9E8629301D8F00AC7927 /* GitHubComment.swift in Sources */, - 58F2EAE9292FB2B0004A9BDE /* SourceControlPreferencesView.swift in Sources */, 6C147C4029A328BC0089B630 /* SplitViewData.swift in Sources */, 587B9E9029301D8F00AC7927 /* BitBucketTokenRouter.swift in Sources */, B6C6A42E29771A8D00A3D28F /* TabBarItemButtonStyle.swift in Sources */, 58822525292C280D00E83CDE /* StatusBarMenuStyle.swift in Sources */, - 58F2EB11292FB2B0004A9BDE /* PreferencesToolbar.swift in Sources */, 6C147C4229A328C10089B630 /* TabGroupData.swift in Sources */, 6C2C155829B4F49100EA60A5 /* SplitViewItem.swift in Sources */, - 58F2EAF0292FB2B0004A9BDE /* PreviewThemeView.swift in Sources */, - 58F2EAFF292FB2B0004A9BDE /* KeybindingsPreferencesView.swift in Sources */, 6CDA84AD284C1BA000C1CC3A /* TabBarContextMenu.swift in Sources */, 6C81916729B3E80700B75C92 /* ModifierKeysObserver.swift in Sources */, 587B9E8129301D8F00AC7927 /* PublicKey.swift in Sources */, - 58F2EB0B292FB2B0004A9BDE /* AccountsPreferences.swift in Sources */, - 58F2EB00292FB2B0004A9BDE /* LocationsPreferencesView.swift in Sources */, + 58F2EB0B292FB2B0004A9BDE /* AccountsSettings.swift in Sources */, 5882252A292C280D00E83CDE /* StatusBarToggleDrawerButton.swift in Sources */, 04C3255B2801F86400C8DA2D /* OutlineViewController.swift in Sources */, 587B9E6029301D8F00AC7927 /* GitLabOAuthRouter.swift in Sources */, 6C05A8AF284D0CA3007F4EAA /* WorkspaceDocument+Listeners.swift in Sources */, 587D9B732933BF5700BF7490 /* FileIcon.swift in Sources */, - 58F2EAF9292FB2B0004A9BDE /* AccountSelectionDialog.swift in Sources */, 6C2C155D29B4F4E500EA60A5 /* SplitViewReader.swift in Sources */, - 58F2EAE8292FB2B0004A9BDE /* TerminalPreferencesView.swift in Sources */, 58AFAA2F2933C69E00482B53 /* TabBarItemID.swift in Sources */, 28FFE1BF27E3A441001939DB /* NavigatorSidebarToolbarBottom.swift in Sources */, 6C4104E3297C87A000F472BA /* BlurButtonStyle.swift in Sources */, - 58F2EAFA292FB2B0004A9BDE /* GitLabHostedLoginView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3828,6 +3780,14 @@ minimumVersion = 1.0.0; }; }; + 6CDEFC9429E22C2700B7C684 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/siteline/SwiftUI-Introspect"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.2.3; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -3876,6 +3836,11 @@ package = 6C147C4329A329350089B630 /* XCRemoteSwiftPackageReference "swift-collections" */; productName = DequeModule; }; + 6CDEFC9529E22C2700B7C684 /* Introspect */ = { + isa = XCSwiftPackageProductDependency; + package = 6CDEFC9429E22C2700B7C684 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */; + productName = Introspect; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = B658FB2427DA9E0F00EA4DBD /* Project object */; diff --git a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 37deb6d4d2..7c51ddd007 100644 --- a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -108,6 +108,15 @@ "version" : "0.7.1" } }, + { + "identity" : "swiftui-introspect", + "kind" : "remoteSourceControl", + "location" : "https://github.com/siteline/SwiftUI-Introspect", + "state" : { + "revision" : "c18951c747ab62af7c15e17a81bd37d4fd5a9979", + "version" : "0.2.3" + } + }, { "identity" : "textformation", "kind" : "remoteSourceControl", diff --git a/CodeEdit/AppDelegate.swift b/CodeEdit/AppDelegate.swift index 8f8603232b..61895e813c 100644 --- a/CodeEdit/AppDelegate.swift +++ b/CodeEdit/AppDelegate.swift @@ -14,7 +14,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { func applicationDidFinishLaunching(_ notification: Notification) { enableWindowSizeSaveOnQuit() - AppPreferencesModel.shared.preferences.general.appAppearance.applyAppearance() + Settings.shared.preferences.general.appAppearance.applyAppearance() checkForFilesToOpen() NSApp.closeWindow(.welcome, .about) @@ -75,7 +75,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { } func handleOpen() { - let behavior = AppPreferencesModel.shared.preferences.general.reopenBehavior + let behavior = Settings.shared.preferences.general.reopenBehavior switch behavior { case .welcome: @@ -137,10 +137,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { // MARK: - Open windows - @IBAction func openPreferences(_ sender: Any) { - preferencesWindowController.show() - } - @IBAction private func openWelcome(_ sender: Any) { NSApp.openWindow(.welcome) } @@ -210,101 +206,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { UserDefaults.standard.setValue(true, forKey: "NSQuitAlwaysKeepsWindows") } - // MARK: - Preferences - private lazy var preferencesWindowController = PreferencesWindowController( - panes: [ - Preferences.Pane( - identifier: Preferences.PaneIdentifier("GeneralSettings"), - title: "General", - toolbarIcon: NSImage(systemSymbolName: "gearshape", accessibilityDescription: nil)! - ) { - GeneralPreferencesView() - .environmentObject(updater) - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Accounts"), - title: "Accounts", - toolbarIcon: NSImage(systemSymbolName: "at", accessibilityDescription: nil)! - ) { - AccountPreferencesView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Behaviors"), - title: "Behaviors", - toolbarIcon: NSImage(systemSymbolName: "flowchart", accessibilityDescription: nil)! - ) { - PreferencesPlaceholderView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Navigation"), - title: "Navigation", - toolbarIcon: NSImage( - systemSymbolName: "arrow.triangle.turn.up.right.diamond", - accessibilityDescription: nil - )! - ) { - PreferencesPlaceholderView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Themes"), - title: "Themes", - toolbarIcon: NSImage(systemSymbolName: "paintbrush", accessibilityDescription: nil)! - ) { - ThemePreferencesView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("TextEditing"), - title: "Text Editing", - toolbarIcon: NSImage(systemSymbolName: "square.and.pencil", accessibilityDescription: nil)! - ) { - TextEditingPreferencesView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Terminal"), - title: "Terminal", - toolbarIcon: NSImage(systemSymbolName: "terminal", accessibilityDescription: nil)! - ) { - TerminalPreferencesView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("KeyBindings"), - title: "Key Bindings", - toolbarIcon: NSImage(systemSymbolName: "keyboard", accessibilityDescription: nil)! - ) { - KeybindingsPreferencesView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("SourceControl"), - title: "Source Control", - toolbarIcon: NSImage.vault - ) { - SourceControlPreferencesView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Components"), - title: "Components", - toolbarIcon: NSImage(systemSymbolName: "puzzlepiece", accessibilityDescription: nil)! - ) { - PreferencesPlaceholderView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Locations"), - title: "Locations", - toolbarIcon: NSImage(systemSymbolName: "externaldrive", accessibilityDescription: nil)! - ) { - LocationsPreferencesView() - }, - Preferences.Pane( - identifier: Preferences.PaneIdentifier("Advanced"), - title: "Advanced", - toolbarIcon: NSImage(systemSymbolName: "gearshape.2", accessibilityDescription: nil)! - ) { - PreferencesPlaceholderView() - } - ], - animated: false - ) - // MARK: NSDocumentController delegate @objc func documentController(_ docController: NSDocumentController, didCloseAll: Bool, contextInfo: Any) { diff --git a/CodeEdit/Assets.xcassets/Icons/BitBucketIcon.imageset/BitBucketIcon.png b/CodeEdit/Assets.xcassets/Icons/BitBucketIcon.imageset/BitBucketIcon.png new file mode 100644 index 0000000000..fe26cbcb71 Binary files /dev/null and b/CodeEdit/Assets.xcassets/Icons/BitBucketIcon.imageset/BitBucketIcon.png differ diff --git a/CodeEdit/Assets.xcassets/Icons/BitBucketIcon.imageset/Contents.json b/CodeEdit/Assets.xcassets/Icons/BitBucketIcon.imageset/Contents.json new file mode 100644 index 0000000000..c623029ab2 --- /dev/null +++ b/CodeEdit/Assets.xcassets/Icons/BitBucketIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "BitBucketIcon.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CodeEdit/Assets.xcassets/Icons/Contents.json b/CodeEdit/Assets.xcassets/Icons/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/CodeEdit/Assets.xcassets/Icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/Contents.json b/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/Contents.json new file mode 100644 index 0000000000..8835ffe82e --- /dev/null +++ b/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "GitHubIcon.png", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "GitHubIcon-Dark.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/GitHubIcon-Dark.png b/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/GitHubIcon-Dark.png new file mode 100644 index 0000000000..c7fb4c8d5f Binary files /dev/null and b/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/GitHubIcon-Dark.png differ diff --git a/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/GitHubIcon.png b/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/GitHubIcon.png new file mode 100644 index 0000000000..d594827582 Binary files /dev/null and b/CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/GitHubIcon.png differ diff --git a/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/Contents.json b/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/Contents.json new file mode 100644 index 0000000000..e4307be646 --- /dev/null +++ b/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "GitLabIcon.png", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "GitLabIcon-Dark.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/GitLabIcon-Dark.png b/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/GitLabIcon-Dark.png new file mode 100644 index 0000000000..119bbe9069 Binary files /dev/null and b/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/GitLabIcon-Dark.png differ diff --git a/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/GitLabIcon.png b/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/GitLabIcon.png new file mode 100644 index 0000000000..2c02bb1b89 Binary files /dev/null and b/CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/GitLabIcon.png differ diff --git a/CodeEdit/CodeEditApp.swift b/CodeEdit/CodeEditApp.swift index e3e3c97c74..8b0b8ea8b1 100644 --- a/CodeEdit/CodeEditApp.swift +++ b/CodeEdit/CodeEditApp.swift @@ -10,25 +10,20 @@ import SwiftUI @main struct CodeEditApp: App { @NSApplicationDelegateAdaptor var appdelegate: AppDelegate + let updater: SoftwareUpdater = SoftwareUpdater() init() { _ = CodeEditDocumentController.shared NSMenuItem.swizzle() + NSSplitViewItem.swizzle() } var body: some Scene { - WelcomeWindow() AboutWindow() - Settings { - VStack { - Text("Hello world!") - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - .defaultSize(width: 500, height: 500) + SettingsWindow() .commands { CodeEditCommands() } diff --git a/CodeEdit/Features/AppPreferences/HelperViews/PreferencesColorPicker.swift b/CodeEdit/Features/AppPreferences/HelperViews/PreferencesColorPicker.swift deleted file mode 100644 index 4853e9ed5b..0000000000 --- a/CodeEdit/Features/AppPreferences/HelperViews/PreferencesColorPicker.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// PreferencesColorPicker.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 31.03.22. -// - -import SwiftUI - -struct PreferencesColorPicker: View { - - @Binding - var color: Color - - private let label: String? - - init(_ color: Binding, label: String? = nil) { - self._color = color - self.label = label - } - - var body: some View { - HStack { - ColorPicker(selection: $color, supportsOpacity: false) { } - .labelsHidden() - if let label { - Text(label) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/HelperViews/PreferencesSection.swift b/CodeEdit/Features/AppPreferences/HelperViews/PreferencesSection.swift deleted file mode 100644 index d4f69e0bfa..0000000000 --- a/CodeEdit/Features/AppPreferences/HelperViews/PreferencesSection.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// PreferencesSection.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 03.04.22. -// - -import SwiftUI - -/// A view that wraps multiple ``PreferencesSection`` views and aligns them correctly. -struct PreferencesContent: View { - - private let width: Double - private let content: Content - - init(width: Double = 844, @ViewBuilder content: () -> Content) { - self.width = width - self.content = content() - } - - var body: some View { - VStack(alignment: .leading) { - content - } - .frame(width: width) - .padding(30) - } -} - -/// A view that wraps controls and more and adds a right aligned label. -struct PreferencesSection: View { - - private let title: String - private let width: Double - private let hideLabels: Bool - private let content: Content - private let align: VerticalAlignment - - init( - _ title: String, - width: Double = 300, - hideLabels: Bool = true, - align: VerticalAlignment = .firstTextBaseline, - @ViewBuilder content: () -> Content - ) { - self.title = title - self.width = width - self.hideLabels = hideLabels - self.align = align - self.content = content() - } - - var body: some View { - HStack(alignment: align) { - /// We keep the ":" since it's being used by all preference views. - Text("\(title):") - .frame(width: width, alignment: .trailing) - if hideLabels { - VStack(alignment: .leading) { - content - .labelsHidden() - .fixedSize() - .frame(maxWidth: .infinity, alignment: .leading) - .frame(minHeight: 20) - } - } else { - VStack(alignment: .leading) { - content - .fixedSize() - .frame(maxWidth: .infinity, alignment: .leading) - .frame(minHeight: 20) - } - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/HelperViews/PreferencesToolbar.swift b/CodeEdit/Features/AppPreferences/HelperViews/PreferencesToolbar.swift deleted file mode 100644 index 6c3ef54efb..0000000000 --- a/CodeEdit/Features/AppPreferences/HelperViews/PreferencesToolbar.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// SwiftUIView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 13.04.22. -// - -import SwiftUI - -struct PreferencesToolbar: View { - - private let height: Double - private let content: () -> T - - init( - height: Double = 27, - bgColor: Color = Color(NSColor.controlBackgroundColor), - @ViewBuilder content: @escaping () -> T - ) { - self.height = height - self.content = content - } - - var body: some View { - ZStack { - EffectView(.contentBackground) - HStack { - content() - .padding(.horizontal, 8) - } - } - .frame(height: height) - } -} diff --git a/CodeEdit/Features/AppPreferences/Model/Accounts/SourceControlAccounts.swift b/CodeEdit/Features/AppPreferences/Model/Accounts/SourceControlAccounts.swift deleted file mode 100644 index 678c1c93e1..0000000000 --- a/CodeEdit/Features/AppPreferences/Model/Accounts/SourceControlAccounts.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// SourceControlAccounts.swift -// CodeEditModules/AppPreferences -// -// Created by Nanashi Li on 2022/04/12. -// - -import Foundation - -struct SourceControlAccounts: Codable, Identifiable, Hashable { - var id: String - var gitProvider: String - var gitProviderLink: String - var gitProviderDescription: String - var gitAccountName: String - // If bool we use the HTTP protocol else if false we use SHH - var gitCloningProtocol: Bool - var gitSSHKey: String - var isTokenValid: Bool -} diff --git a/CodeEdit/Features/AppPreferences/Model/AppPreferencesModel.swift b/CodeEdit/Features/AppPreferences/Model/AppPreferencesModel.swift deleted file mode 100644 index 7d705b2fe7..0000000000 --- a/CodeEdit/Features/AppPreferences/Model/AppPreferencesModel.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// AppPreferencesModel.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 01.04.22. -// - -import Foundation -import SwiftUI - -/// The Preferences View Model. Accessible via the singleton "``AppPreferencesModel/shared``". -/// -/// **Usage:** -/// ```swift -/// @StateObject -/// private var prefs: AppPreferencesModel = .shared -/// ``` -final class AppPreferencesModel: ObservableObject { - - /// The publicly available singleton instance of ``AppPreferencesModel`` - static let shared: AppPreferencesModel = .init() - - private init() { - self.preferences = .init() - self.preferences = loadPreferences() - } - - /// Published instance of the ``AppPreferences`` model. - /// - /// Changes are saved automatically. - @Published - var preferences: AppPreferences { - didSet { - try? savePreferences() - } - } - - /// Load and construct ``AppPreferences`` model from - /// `~/Library/Application Support/CodeEdit/preferences.json` - private func loadPreferences() -> AppPreferences { - if !filemanager.fileExists(atPath: preferencesURL.path) { - try? filemanager.createDirectory(at: baseURL, withIntermediateDirectories: false) - return .init() - } - - guard let json = try? Data(contentsOf: preferencesURL), - let prefs = try? JSONDecoder().decode(AppPreferences.self, from: json) - else { - return .init() - } - return prefs - } - - /// Save``AppPreferences`` model to - /// `~/Library/Application Support/CodeEdit/preferences.json` - private func savePreferences() throws { - let data = try JSONEncoder().encode(preferences) - let json = try JSONSerialization.jsonObject(with: data) - let prettyJSON = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted]) - try prettyJSON.write(to: preferencesURL, options: .atomic) - } - - /// Default instance of the `FileManager` - private let filemanager = FileManager.default - - /// The base URL of preferences. - /// - /// Points to `~/Library/Application Support/CodeEdit/` - internal var baseURL: URL { - filemanager - .homeDirectoryForCurrentUser - .appendingPathComponent("Library/Application Support/CodeEdit", isDirectory: true) - } - - /// The URL of the `preferences.json` settings file. - /// - /// Points to `~/Library/Application Support/CodeEdit/preferences.json` - private var preferencesURL: URL { - baseURL - .appendingPathComponent("preferences") - .appendingPathExtension("json") - } -} diff --git a/CodeEdit/Features/AppPreferences/PreferencesPlaceholderView.swift b/CodeEdit/Features/AppPreferences/PreferencesPlaceholderView.swift deleted file mode 100644 index 65f953bfd0..0000000000 --- a/CodeEdit/Features/AppPreferences/PreferencesPlaceholderView.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// PreferencesPlaceholderView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 30.03.22. -// - -import SwiftUI -import Preferences - -struct PreferencesPlaceholderView: View { - var body: some View { - VStack { - Text("Implementation needed") - } - .frame(width: 844, height: 300) - .padding(30) - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/AccountPreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/AccountPreferencesView.swift deleted file mode 100644 index bff531bb6b..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/AccountPreferencesView.swift +++ /dev/null @@ -1,175 +0,0 @@ -// -// AccountPreferencesView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI - -struct AccountPreferencesView: View { - @StateObject - private var prefs: AppPreferencesModel = .shared - - @State - private var openAccountDialog = false - - @State - private var cloneUsing = false - - @State - var accountSelection: SourceControlAccounts.ID? - - var body: some View { - VStack(alignment: .leading, spacing: 16) { - HStack(spacing: 1) { - accountSelectionView - if prefs.preferences.accounts.sourceControlAccounts.gitAccount.isEmpty { - emptyView - } else if - !prefs.preferences.accounts.sourceControlAccounts.gitAccount.isEmpty && accountSelection == nil { - selectAccount - } else { - accountTypeView - } - } - .padding(1) - .background(Rectangle().foregroundColor(Color(NSColor.separatorColor))) - .frame(width: 872, height: 468) - .padding() - } - } - - private var accountSelectionView: some View { - VStack(alignment: .leading, spacing: 1) { - PreferencesToolbar { - Text("Source Control Accounts") - .foregroundColor(.secondary) - .frame(maxWidth: .infinity, alignment: .leading) - } - List( - $prefs.preferences.accounts.sourceControlAccounts.gitAccount, - selection: $accountSelection - ) { gitAccount in - GitAccountItemView(sourceControlAccount: gitAccount) - } - .background( - EffectView(.contentBackground) - ) - PreferencesToolbar { - sidebarBottomToolbar - } - } - .frame(width: 210) - } - - private var accountTypeView: some View { - VStack(alignment: .leading, spacing: 1) { - PreferencesToolbar { - Text(getSourceControlAccount(selectedAccountId: accountSelection ?? "")?.gitProvider ?? "") - .fontWeight(.medium) - .font(.system(size: 12)) - .frame(maxWidth: .infinity, alignment: .leading) - } - VStack { - PreferencesSection("Account", width: 100) { - Text(getSourceControlAccount(selectedAccountId: accountSelection ?? "")?.gitAccountName ?? "") - .fontWeight(.medium) - } - - PreferencesSection("Description", width: 100) { - let account = getSourceControlAccount(selectedAccountId: accountSelection ?? "") - Text(account?.gitProviderDescription ?? "") - .fontWeight(.medium) - } - - Divider() - .padding(.bottom, 5) - - PreferencesSection("Clone Using", width: 100) { - let account = getSourceControlAccount(selectedAccountId: accountSelection ?? "") - - Picker("", selection: $cloneUsing) { - Text("HTTPS") - .tag(false) // temporary - Text("SSH") - .tag(true) // temporary - } - .pickerStyle(.radioGroup) - - Text("New repositories will be cloned from \(account?.gitProviderDescription ?? "")" - + " using \(cloneUsing ? "SSH" : "HTTPS").") - .lineLimit(2) - .font(.system(size: 11)) - .foregroundColor(Color.secondary) - } - - PreferencesSection("SSH Key", width: 100) { - Picker("", selection: $prefs.preferences.accounts.sourceControlAccounts.sshKey) { - Text("None") - Text("Create New...") - Text("Choose...") - } - } - } - .padding(.top, 10) - .frame(maxHeight: .infinity, alignment: .top) - .background(EffectView(.contentBackground)) - } - } - - private var sidebarBottomToolbar: some View { - HStack { - Button { - openAccountDialog.toggle() - } label: { - Image(systemName: "plus") - } - .sheet(isPresented: $openAccountDialog, content: { - AccountSelectionDialog(openAccountDialog: $openAccountDialog) - }) - .help("Add a Git Account") - .buttonStyle(.plain) - Button { - removeSourceControlAccount(selectedAccountId: accountSelection ?? "") - } label: { - Image(systemName: "minus") - } - .disabled(true) - .help("Delete selected Git Account") - .buttonStyle(.plain) - Spacer() - } - } - - private var emptyView: some View { - VStack { - Text("Click the add (+) button to create a new account") - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(EffectView(.contentBackground)) - } - - private var selectAccount: some View { - VStack { - Text("Select an account from the list in the left panel") - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(EffectView(.contentBackground)) - } - - private func getSourceControlAccount(selectedAccountId: String) -> SourceControlAccounts? { - let gitAccounts = prefs.preferences.accounts.sourceControlAccounts.gitAccount - return gitAccounts.first { $0.id == selectedAccountId } - } - - private func removeSourceControlAccount(selectedAccountId: String) { - var gitAccounts = prefs.preferences.accounts.sourceControlAccounts.gitAccount - - for account in gitAccounts where account.id == selectedAccountId { - let index = gitAccounts.firstIndex(of: account) - gitAccounts.remove(at: index ?? 0) - } - } - -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/AccountListItemView.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/AccountListItemView.swift deleted file mode 100644 index 371f15419f..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/AccountListItemView.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// AccountListItemView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI -import CodeEditSymbols - -struct AccountListItemView: View { - var gitClientName: String - - var body: some View { - HStack { - Image(symbol: "vault.fill") - .resizable() - .frame(width: 28.0, height: 28.0) - Text(gitClientName) - .font(.system(size: 12)) - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/AccountSelectionDialog.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/AccountSelectionDialog.swift deleted file mode 100644 index 0053d6f907..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/AccountSelectionDialog.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// AccountSelectionDialog.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI - -struct AccountSelectionDialog: View { - var gitProviders = [ - SourceControlProvider(name: "Bitbucket Cloud", id: "bitbucketCloud"), - SourceControlProvider(name: "Bitbucket Server", id: "bitbucketServer"), - SourceControlProvider(name: "GitHub", id: "github"), - SourceControlProvider(name: "GitHub Enterprise", id: "githubEnterprise"), - SourceControlProvider(name: "GitLab", id: "gitlab"), - SourceControlProvider(name: "GitLab Self-Hosted", id: "gitlabSelfHosted") - ] - - @State - var providerSelection: SourceControlProvider.ID? = "github" - - @State - var openGitLogin = false - - @Binding - var openAccountDialog: Bool - - var body: some View { - VStack { - Text("Select the type of account you would like to add:") - .font(.system(size: 12)) - - List(gitProviders, selection: $providerSelection) { - AccountListItemView(gitClientName: $0.name) - } - .background(Color(NSColor.controlBackgroundColor)) - .padding(1) - .background(Rectangle().foregroundColor(Color(NSColor.separatorColor))) - - HStack { - Button("Cancel") { - openAccountDialog.toggle() - } - Button("Continue") { - openGitLogin.toggle() - } - .sheet(isPresented: $openGitLogin, content: { - openAccountLoginDialog - }) - .buttonStyle(.borderedProminent) - } - .frame(maxWidth: .infinity, alignment: .trailing) - } - .padding(20) - .frame(width: 400, height: 285) - } - - @ViewBuilder - private var openAccountLoginDialog: some View { - switch providerSelection { - case "bitbucketCloud": - implementationNeeded - case "bitbucketServer": - implementationNeeded - case "github": - GitHubLoginView(dismissDialog: $openGitLogin, dismissParentDialog: $openAccountDialog) - case "githubEnterprise": - GitHubEnterpriseLoginView(dismissDialog: $openGitLogin, dismissParentDialog: $openAccountDialog) - case "gitlab": - GitLabLoginView(dismissDialog: $openGitLogin, dismissParentDialog: $openAccountDialog) - case "gitlabSelfHosted": - GitLabHostedLoginView(dismissDialog: $openGitLogin, dismissParentDialog: $openAccountDialog) - default: - implementationNeeded - } - } - - private var implementationNeeded: some View { - VStack { - Text("This git client is currently not supported yet!") - .font(.system(size: 12)) - HStack { - Button("Close") { - openGitLogin.toggle() - } - .buttonStyle(.borderedProminent) - } - .frame(maxWidth: .infinity, alignment: .trailing) - .padding(.trailing, 20) - } - .padding(20) - .frame(width: 300, height: 120) - } - -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/GitAccountItemView.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/GitAccountItemView.swift deleted file mode 100644 index 354248600a..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/GitAccountItemView.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// GitAccountItemView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI -import CodeEditSymbols - -struct GitAccountItemView: View { - - @Binding - var sourceControlAccount: SourceControlAccounts - - var body: some View { - HStack { - Image(symbol: "vault.fill") - .resizable() - .frame(width: 24.0, height: 24.0) - - VStack(alignment: .leading) { - Text(sourceControlAccount.gitProvider) - .font(.system(size: 12)) - Text(sourceControlAccount.gitProviderLink) - .font(.system(size: 10)) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Github/GitHubEnterpriseLoginView.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Github/GitHubEnterpriseLoginView.swift deleted file mode 100644 index 876e6f3a26..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Github/GitHubEnterpriseLoginView.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// GitHubEnterpriseLoginView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanashi Li on 2022/04/12. -// - -import SwiftUI - -struct GitHubEnterpriseLoginView: View { - - @State var eneterpriseLink = "" - @State var accountName = "" - @State var accountToken = "" - - @Environment(\.openURL) var createToken - - @Binding var dismissDialog: Bool - @Binding var dismissParentDialog: Bool - - @StateObject - private var prefs: AppPreferencesModel = .shared - - private let keychain = CodeEditKeychain() - - var body: some View { - VStack { - Text("Sign in to your GitHub account") - - VStack(alignment: .trailing) { - HStack { - Text("Server:") - TextField("https://example.com", text: $eneterpriseLink) - .frame(width: 300) - } - HStack { - Text("Account:") - TextField("", text: $accountName) - .frame(width: 300) - } - HStack { - Text("Token:") - SecureField( - "Enter your Personal Access Token", - text: $accountToken - ) - .frame(width: 300) - } - } - - HStack { - HStack { - Button("Create a Token on GitHub Enterprise") { - createToken(URL(string: "https://github.com/settings/tokens/new")!) - } - } - .frame(maxWidth: .infinity, alignment: .leading) - HStack { - Button("Cancel") { - dismissDialog = false - } - if accountToken.isEmpty { - Button("Sign In") {} - .disabled(true) - } else { - Button("Sign In") { - loginGitHubEnterprise(gitAccountName: accountName) - } - .buttonStyle(.borderedProminent) - } - } - .frame(maxWidth: .infinity, alignment: .trailing) - }.padding(.top, 10) - } - .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) - .frame(width: 485, height: 190) - } - - private func loginGitHubEnterprise(gitAccountName: String) { - let gitAccounts = prefs.preferences.accounts.sourceControlAccounts.gitAccount - - let config = GitHubTokenConfiguration( - accountToken, - url: eneterpriseLink - ) - GitHubAccount(config).me { response in - switch response { - case .success(let user): - if gitAccounts.contains( - where: { - $0.gitProviderLink == eneterpriseLink && - $0.gitAccountName.lowercased() == gitAccountName.lowercased() - } - ) { - print("Account with the username and provider already exists!") - } else { - print(user) - prefs.preferences.accounts.sourceControlAccounts.gitAccount.append( - SourceControlAccounts( - id: "\(eneterpriseLink)_\(gitAccountName.lowercased())", - gitProvider: "GitHub", - gitProviderLink: eneterpriseLink, - gitProviderDescription: "GitHub", - gitAccountName: gitAccountName, - gitCloningProtocol: true, - gitSSHKey: "", - isTokenValid: true - ) - ) - keychain.set(accountToken, forKey: "github_\(accountName)_enterprise") - dismissDialog = false - dismissParentDialog = false - } - case .failure(let error): - print(error) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Github/GitHubLoginView.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Github/GitHubLoginView.swift deleted file mode 100644 index b16c1628a6..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Github/GitHubLoginView.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// GitHubLoginView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI - -struct GitHubLoginView: View { - - @State var accountName = "" - @State var accountToken = "" - - @Environment(\.openURL) var createToken - - private let keychain = CodeEditKeychain() - - @Binding var dismissDialog: Bool - @Binding var dismissParentDialog: Bool - - @StateObject - private var prefs: AppPreferencesModel = .shared - - var body: some View { - VStack { - Text("Sign in to your GitHub account") - - VStack(alignment: .trailing) { - HStack { - Text("Account:") - TextField("Enter your username", text: $accountName) - .frame(width: 300) - } - HStack { - Text("Token:") - SecureField( - "Enter your Personal Access Token", - text: $accountToken - ) - .frame(width: 300) - } - } - - VStack { - Text("GitHub personal access tokens must have these scopes set:") - .fontWeight(.bold) - .font(.system(size: 11)) - - VStack(alignment: .leading) { - HStack { - Image(systemName: "checkmark") - Text("admin:public _key") - .font(.system(size: 10)) - } - HStack { - Image(systemName: "checkmark") - Text("write:discussion") - .font(.system(size: 10)) - } - HStack { - Image(systemName: "checkmark") - Text("repo") - .font(.system(size: 10)) - } - HStack { - Image(systemName: "checkmark") - Text("user") - .font(.system(size: 10)) - } - }.padding(.top, 2) - } - .frame(maxWidth: .infinity) - .padding(.bottom, 10) - .padding(.top, 10) - - HStack { - HStack { - Button("Create a Token on GitHub") { - createToken(URL(string: "https://github.com/settings/tokens/new")!) - } - } - .frame(maxWidth: .infinity, alignment: .leading) - HStack { - Button("Cancel") { - dismissDialog.toggle() - } - if accountToken.isEmpty { - Button("Sign In") {} - .disabled(true) - } else { - Button("Sign In") { - loginGitHub(gitAccountName: accountName) - } - .buttonStyle(.borderedProminent) - } - } - .frame(maxWidth: .infinity, alignment: .trailing) - }.padding(.top, 10) - } - .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) - .frame(width: 485, height: 280) - } - - private func loginGitHub(gitAccountName: String) { - let gitAccounts = prefs.preferences.accounts.sourceControlAccounts.gitAccount - - let config = GitHubTokenConfiguration(accountToken) - - let providerLink = "https://github.com" - GitHubAccount(config).me { response in - switch response { - case .success(let user): - if gitAccounts.contains( - where: { - $0.gitProviderLink == providerLink && - $0.gitAccountName.lowercased() == gitAccountName.lowercased() - } - ) { - print("Account with the username and provider already exists!") - } else { - print(user) - prefs.preferences.accounts.sourceControlAccounts.gitAccount.append( - SourceControlAccounts( - id: "\(providerLink)_\(gitAccountName.lowercased())", - gitProvider: "GitHub", - gitProviderLink: providerLink, - gitProviderDescription: "GitHub", - gitAccountName: gitAccountName, - gitCloningProtocol: true, - gitSSHKey: "", - isTokenValid: true - ) - ) - keychain.set(accountToken, forKey: "github_\(accountName)") - dismissDialog.toggle() - dismissParentDialog.toggle() - } - case .failure(let error): - print(error) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Gitlab/GitLabHostedLoginView.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Gitlab/GitLabHostedLoginView.swift deleted file mode 100644 index 9f1bad8a3e..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Gitlab/GitLabHostedLoginView.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// GitLabHostedLoginView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanashi Li on 2022/04/21. -// - -import SwiftUI - -struct GitLabHostedLoginView: View { - @State var eneterpriseLink = "" - @State var accountName = "" - @State var accountToken = "" - - @Environment(\.openURL) var createToken - - @Binding var dismissDialog: Bool - @Binding var dismissParentDialog: Bool - - @StateObject - private var prefs: AppPreferencesModel = .shared - - private let keychain = CodeEditKeychain() - - var body: some View { - VStack { - Text("Sign in to your GitHub account") - - VStack(alignment: .trailing) { - HStack { - Text("Server:") - TextField("https://example.com", text: $eneterpriseLink) - .frame(width: 300) - } - HStack { - Text("Account:") - TextField("", text: $accountName) - .frame(width: 300) - } - HStack { - Text("Token:") - SecureField( - "Enter your Personal Access Token", - text: $accountToken - ) - .frame(width: 300) - } - } - - HStack { - HStack { - Button("Create a Token on GitLab Self-Hosted") { - createToken(URL(string: "https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html")!) - } - } - .frame(maxWidth: .infinity, alignment: .leading) - HStack { - Button("Cancel") { - dismissDialog = false - } - if accountToken.isEmpty { - Button("Sign In") {} - .disabled(true) - } else { - Button("Sign In") { - loginGitLabSelfHosted(gitAccountName: accountName) - } - .buttonStyle(.borderedProminent) - } - } - .frame(maxWidth: .infinity, alignment: .trailing) - }.padding(.top, 10) - } - .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) - .frame(width: 485, height: 190) - } - - private func loginGitLabSelfHosted(gitAccountName: String) { - let gitAccounts = prefs.preferences.accounts.sourceControlAccounts.gitAccount - - let config = GitLabTokenConfiguration(accountToken, url: eneterpriseLink) - GitLabAccount(config).me { response in - switch response { - case .success(let user): - if gitAccounts.contains( - where: { - $0.gitProviderLink == eneterpriseLink && - $0.gitAccountName.lowercased() == gitAccountName.lowercased() - } - ) { - print("Account with the username and provider already exists!") - } else { - print(user) - prefs.preferences.accounts.sourceControlAccounts.gitAccount.append( - SourceControlAccounts( - id: "\(eneterpriseLink)_\(gitAccountName.lowercased())", - gitProvider: "GitLab", - gitProviderLink: eneterpriseLink, - gitProviderDescription: "GitLab", - gitAccountName: gitAccountName, - gitCloningProtocol: true, - gitSSHKey: "", - isTokenValid: true - ) - ) - keychain.set(accountToken, forKey: "gitlab_\(gitAccountName)_hosted") - dismissDialog = false - dismissParentDialog = false - } - case .failure(let error): - print(error) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Gitlab/GitLabLoginView.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Gitlab/GitLabLoginView.swift deleted file mode 100644 index ed8cf502b6..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Accounts/Login/Gitlab/GitLabLoginView.swift +++ /dev/null @@ -1,112 +0,0 @@ -// -// GitLabLoginView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanashi Li on 2022/04/21. -// - -import SwiftUI - -struct GitLabLoginView: View { - - @State var accountName = "" - @State var accountToken = "" - - @Environment(\.openURL) var createToken - - private let keychain = CodeEditKeychain() - - @Binding var dismissDialog: Bool - @Binding var dismissParentDialog: Bool - - @StateObject - private var prefs: AppPreferencesModel = .shared - - var body: some View { - VStack { - Text("Sign in to your GitLab account") - - VStack(alignment: .trailing) { - HStack { - Text("Account:") - TextField("Enter your username", text: $accountName) - .frame(width: 300) - } - HStack { - Text("Token:") - SecureField( - "Enter your Personal Access Token", - text: $accountToken - ) - .frame(width: 300) - } - } - - HStack { - HStack { - Button("Create a Token on GitLab") { - createToken(URL(string: "https://gitlab.com/-/profile/personal_access_tokens")!) - } - } - .frame(maxWidth: .infinity, alignment: .leading) - HStack { - Button("Cancel") { - dismissDialog.toggle() - } - if accountToken.isEmpty { - Button("Sign In") {} - .disabled(true) - } else { - Button("Sign In") { - loginGitLab(gitAccountName: accountName) - } - .buttonStyle(.borderedProminent) - } - } - .frame(maxWidth: .infinity, alignment: .trailing) - }.padding(.top, 10) - } - .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) - .frame(width: 485, height: 160) - } - - private func loginGitLab(gitAccountName: String) { - let gitAccounts = prefs.preferences.accounts.sourceControlAccounts.gitAccount - - let config = GitLabTokenConfiguration(accountToken) - - let providerLink = "https://gitlab.com" - GitLabAccount(config).me { response in - switch response { - case .success(let user): - if gitAccounts.contains( - where: { - $0.gitProviderLink == providerLink && - $0.gitAccountName.lowercased() == gitAccountName.lowercased() - } - ) { - print("Account with the username and provider already exists!") - } else { - print(user) - prefs.preferences.accounts.sourceControlAccounts.gitAccount.append( - SourceControlAccounts( - id: "\(providerLink)_\(gitAccountName.lowercased())", - gitProvider: "GitLab", - gitProviderLink: providerLink, - gitProviderDescription: "GitLab", - gitAccountName: gitAccountName, - gitCloningProtocol: true, - gitSSHKey: "", - isTokenValid: true - ) - ) - keychain.set(accountToken, forKey: "gitlab_\(accountName)") - dismissDialog = false - dismissParentDialog = false - } - case .failure(let error): - print(error) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Models/SourceControlProvider.swift b/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Models/SourceControlProvider.swift deleted file mode 100644 index c48da3023e..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/AccountsPreferences/Models/SourceControlProvider.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// SourceControlProvider.swift -// CodeEditModules/AppPreferences -// -// Created by Nanashi Li on 2022/04/08. -// - -import Foundation - -struct SourceControlProvider: Identifiable, Hashable { - let name: String - let id: String -} diff --git a/CodeEdit/Features/AppPreferences/Sections/GeneralPreferences/GeneralPreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/GeneralPreferences/GeneralPreferencesView.swift deleted file mode 100644 index 9221beae2b..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/GeneralPreferences/GeneralPreferencesView.swift +++ /dev/null @@ -1,375 +0,0 @@ -// -// GeneralPreferencesView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 30.03.22. -// - -import SwiftUI - -/// A view that implements the `General` preference section -struct GeneralPreferencesView: View { - private let inputWidth: Double = 160 - private let textEditorWidth: Double = 220 - private let textEditorHeight: Double = 30 - - @EnvironmentObject - var updater: SoftwareUpdater - - @StateObject - private var prefs: AppPreferencesModel = .shared - - @State - private var openInCodeEdit: Bool = true - - init() { - guard let defaults = UserDefaults.init( - suiteName: "austincondiff.CodeEdit.shared" - ) else { - print("Failed to get/init shared defaults") - return - } - - self.openInCodeEdit = defaults.bool(forKey: "enableOpenInCE") - } - - var body: some View { - PreferencesContent { - appearanceSection - showIssuesSection - fileExtensionsSection - fileIconStyleSection - tabBarStyleSection - Group { - reopenBehaviorSection - reopenAfterWindowCloseBehaviourSection - } - projectNavigatorSizeSection - findNavigatorDetailSection - Group { - issueNavigatorDetailSection - dialogWarningsSection - } - Group { - openInCodeEditToggle - revealFileOnFocusChangeToggle - shellCommandSection - autoSaveSection - updaterSection - } - } - .frame(minHeight: 650) - } -} - -private extension GeneralPreferencesView { - var appearanceSection: some View { - PreferencesSection("Appearance") { - Picker("Appearance", selection: $prefs.preferences.general.appAppearance) { - Text("System") - .tag(AppPreferences.Appearances.system) - Divider() - Text("Light") - .tag(AppPreferences.Appearances.light) - Text("Dark") - .tag(AppPreferences.Appearances.dark) - } - .onChange(of: prefs.preferences.general.appAppearance) { tag in - tag.applyAppearance() - } - .frame(width: inputWidth) - } - } - - // TODO: Implement reflecting Show Issues preference and remove disabled modifier - var showIssuesSection: some View { - PreferencesSection("Show Issues", hideLabels: false) { - Picker("Show Issues", selection: $prefs.preferences.general.showIssues) { - Text("Show Inline") - .tag(AppPreferences.Issues.inline) - Text("Show Minimized") - .tag(AppPreferences.Issues.minimized) - } - .labelsHidden() - .frame(width: inputWidth) - - Toggle("Show Live Issues", isOn: $prefs.preferences.general.showLiveIssues) - .toggleStyle(.checkbox) - } - .disabled(true) - } - - var fileExtensionsSection: some View { - PreferencesSection("File Extensions") { - Picker("File Extensions:", selection: $prefs.preferences.general.fileExtensionsVisibility) { - Text("Hide all") - .tag(AppPreferences.FileExtensionsVisibility.hideAll) - Text("Show all") - .tag(AppPreferences.FileExtensionsVisibility.showAll) - Divider() - Text("Show only") - .tag(AppPreferences.FileExtensionsVisibility.showOnly) - Text("Hide only") - .tag(AppPreferences.FileExtensionsVisibility.hideOnly) - } - .frame(width: inputWidth) - if case .showOnly = prefs.preferences.general.fileExtensionsVisibility { - SettingsTextEditor(text: $prefs.preferences.general.shownFileExtensions.string) - .frame(width: textEditorWidth) - .frame(height: textEditorHeight) - } - if case .hideOnly = prefs.preferences.general.fileExtensionsVisibility { - SettingsTextEditor(text: $prefs.preferences.general.hiddenFileExtensions.string) - .frame(width: textEditorWidth) - .frame(height: textEditorHeight) - } - } - } - - var fileIconStyleSection: some View { - PreferencesSection("File Icon Style") { - Picker("File Icon Style:", selection: $prefs.preferences.general.fileIconStyle) { - Text("Color") - .tag(AppPreferences.FileIconStyle.color) - Text("Monochrome") - .tag(AppPreferences.FileIconStyle.monochrome) - } - .pickerStyle(.radioGroup) - } - } - - var tabBarStyleSection: some View { - PreferencesSection("Tab Bar Style") { - Picker("Tab Bar Style:", selection: $prefs.preferences.general.tabBarStyle) { - Text("Xcode") - .tag(AppPreferences.TabBarStyle.xcode) - Text("Native") - .tag(AppPreferences.TabBarStyle.native) - } - .pickerStyle(.radioGroup) - } - } - - var reopenBehaviorSection: some View { - PreferencesSection("Reopen Behavior") { - Picker("Reopen Behavior:", selection: $prefs.preferences.general.reopenBehavior) { - Text("Welcome Screen") - .tag(AppPreferences.ReopenBehavior.welcome) - Divider() - Text("Open Panel") - .tag(AppPreferences.ReopenBehavior.openPanel) - Text("New Document") - .tag(AppPreferences.ReopenBehavior.newDocument) - } - .frame(width: inputWidth) - } - } - - var reopenAfterWindowCloseBehaviourSection: some View { - PreferencesSection("After last window closed") { - Picker( - "After last window closed:", - selection: $prefs.preferences.general.reopenWindowAfterClose - ) { - Text("Do nothing") - .tag(AppPreferences.ReopenWindowBehavior.doNothing) - Divider() - Text("Show Welcome Window") - .tag(AppPreferences.ReopenWindowBehavior.showWelcomeWindow) - Text("Quit") - .tag(AppPreferences.ReopenWindowBehavior.quit) - } - .frame(width: inputWidth) - } - } - - var projectNavigatorSizeSection: some View { - PreferencesSection("Project Navigator Size") { - Picker("Project Navigator Size", selection: $prefs.preferences.general.projectNavigatorSize) { - Text("Small") - .tag(AppPreferences.ProjectNavigatorSize.small) - Text("Medium") - .tag(AppPreferences.ProjectNavigatorSize.medium) - Text("Large") - .tag(AppPreferences.ProjectNavigatorSize.large) - } - .frame(width: inputWidth) - } - } - - var findNavigatorDetailSection: some View { - PreferencesSection("Find Navigator Detail") { - Picker("Find Navigator Detail", selection: $prefs.preferences.general.findNavigatorDetail) { - ForEach(AppPreferences.NavigatorDetail.allCases, id: \.self) { tag in - Text(tag.label).tag(tag) - } - } - .frame(width: inputWidth) - } - } - - // TODO: Implement reflecting Issue Navigator Detail preference and remove disabled modifier - var issueNavigatorDetailSection: some View { - PreferencesSection("Issue Navigator Detail") { - Picker("Issue Navigator Detail", selection: $prefs.preferences.general.issueNavigatorDetail) { - ForEach(AppPreferences.NavigatorDetail.allCases, id: \.self) { tag in - Text(tag.label).tag(tag) - } - } - .frame(width: inputWidth) - } - .disabled(true) - } - - // TODO: Implement reset for Don't Ask Me warnings Button and remove disabled modifier - var dialogWarningsSection: some View { - PreferencesSection("Dialog Warnings", align: .center) { - Button(action: { - }, label: { - Text("Reset \"Don't Ask Me\" Warnings") - .padding(.horizontal, 10) - }) - .buttonStyle(.bordered) - } - .disabled(true) - } - - var shellCommandSection: some View { - PreferencesSection("Shell Command", align: .center) { - Button(action: { - do { - let url = Bundle.main.url(forResource: "codeedit", withExtension: nil, subdirectory: "Resources") - let destination = "/usr/local/bin/codeedit" - - if FileManager.default.fileExists(atPath: destination) { - try FileManager.default.removeItem(atPath: destination) - } - - guard let shellUrl = url?.path else { - print("Failed to get URL to shell command") - return - } - - NSWorkspace.shared.requestAuthorization(to: .createSymbolicLink) { auth, error in - guard let auth, error == nil else { - fallbackShellInstallation(commandPath: shellUrl, destinationPath: destination) - return - } - - do { - try FileManager(authorization: auth).createSymbolicLink( - atPath: destination, withDestinationPath: shellUrl - ) - } catch { - fallbackShellInstallation(commandPath: shellUrl, destinationPath: destination) - } - } - } catch { - print(error) - } - }, label: { - Text("Install 'codeedit' command") - .padding(.horizontal, 10) - }) - .disabled(true) - .buttonStyle(.bordered) - } - } - - var updaterSection: some View { - PreferencesSection("Software Updates", hideLabels: false) { - VStack(alignment: .leading) { - Toggle("Automatically check for app updates", isOn: $updater.automaticallyChecksForUpdates) - - Toggle("Include pre-release versions", isOn: $updater.includePrereleaseVersions) - - Button("Check Now") { - updater.checkForUpdates() - } - - Text("Last checked: \(lastUpdatedString)") - .font(.footnote) - } - .frame(maxWidth: .infinity, alignment: .leading) - } - } - - private var lastUpdatedString: String { - if let lastUpdatedDate = updater.lastUpdateCheckDate { - return Self.formatter.string(from: lastUpdatedDate) - } else { - return "Never" - } - } - - private static func configure(_ subject: Subject, configuration: (inout Subject) -> Void) -> Subject { - var copy = subject - configuration(©) - return copy - } - - private static let formatter = configure(DateFormatter()) { - $0.dateStyle = .medium - $0.timeStyle = .medium - } - - var autoSaveSection: some View { - PreferencesSection("Auto Save Behavior", hideLabels: false) { - Toggle( - "Automatically save changes to disk", - isOn: $prefs.preferences.general.isAutoSaveOn - ) - .toggleStyle(.checkbox) - } - } - - func fallbackShellInstallation(commandPath: String, destinationPath: String) { - let cmd = [ - "osascript", - "-e", - "\"do shell script \\\"mkdir -p /usr/local/bin && ln -sf \'\(commandPath)\' \'\(destinationPath)\'\\\"\"", - "with administrator privileges" - ] - - let cmdStr = cmd.joined(separator: " ") - - let task = Process() - let pipe = Pipe() - - task.standardOutput = pipe - task.standardError = pipe - task.arguments = ["-c", cmdStr] - task.executableURL = URL(fileURLWithPath: "/bin/zsh") - task.standardInput = nil - - do { - try task.run() - } catch { - print(error) - } - } - - var openInCodeEditToggle: some View { - PreferencesSection("Finder Context Menu", hideLabels: false) { - Toggle("Show “Open With CodeEdit” option", isOn: $openInCodeEdit) - .toggleStyle(.checkbox) - .onChange(of: openInCodeEdit) { newValue in - guard let defaults = UserDefaults.init( - suiteName: "austincondiff.CodeEdit.shared" - ) else { - print("Failed to get/init shared defaults") - return - } - - defaults.set(newValue, forKey: "enableOpenInCE") - } - } - } - - var revealFileOnFocusChangeToggle: some View { - PreferencesSection("Project Navigator Behavior", hideLabels: false) { - Toggle("Automatically Show Active File", isOn: $prefs.preferences.general.revealFileOnFocusChange) - .toggleStyle(.checkbox) - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/KeybindingsPreferences/KeybindingsPreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/KeybindingsPreferences/KeybindingsPreferencesView.swift deleted file mode 100644 index 88cb9ef917..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/KeybindingsPreferences/KeybindingsPreferencesView.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// KeybindingsPreferencesView.swift -// -// -// Created by Alex on 19.05.2022. -// - -import SwiftUI - -struct KeybindingsPreferencesView: View { - var body: some View { - PreferencesContent { - Text("Implementation needed") - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/LocationsPreferences/LocationsPreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/LocationsPreferences/LocationsPreferencesView.swift deleted file mode 100644 index a9cd5e0499..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/LocationsPreferences/LocationsPreferencesView.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// LocationsPreferencesView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 03.04.22. -// - -import SwiftUI -import Preferences - -/// A view that implements the `Locations` preference section -struct LocationsPreferencesView: View { - var body: some View { - PreferencesContent { - PreferencesSection("Preferences Location") { - HStack { - Text(AppPreferencesModel.shared.baseURL.path) - .foregroundColor(.secondary) - Button { - NSWorkspace.shared.selectFile( - nil, - inFileViewerRootedAtPath: AppPreferencesModel.shared.baseURL.path - ) - } label: { - Image(systemName: "arrow.right.circle.fill") - } - .buttonStyle(.plain) - .foregroundColor(.secondary) - } - } - PreferencesSection("Themes Location") { - HStack { - Text(ThemeModel.shared.themesURL.path) - .foregroundColor(.secondary) - Button { - NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: ThemeModel.shared.themesURL.path) - } label: { - Image(systemName: "arrow.right.circle.fill") - } - .buttonStyle(.plain) - .foregroundColor(.secondary) - } - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/IgnoredFileView.swift b/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/IgnoredFileView.swift deleted file mode 100644 index 43e5684dbb..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/IgnoredFileView.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// IgnoredFileView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanashi Li on 2022/04/13. -// - -import SwiftUI - -struct IgnoredFileView: View { - @Binding - var ignoredFile: IgnoredFiles - - var body: some View { - Text(ignoredFile.name) - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlGeneralView.swift b/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlGeneralView.swift deleted file mode 100644 index 81c6966ce8..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlGeneralView.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// SourceControlGeneralView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI - -struct SourceControlGeneralView: View { - private let inputWidth: Double = 200 - - @StateObject - private var prefs: AppPreferencesModel = .shared - - @State - var isChecked: Bool - - @State - var branchName: String - - var body: some View { - VStack { - - PreferencesSection("Source Control", hideLabels: false) { - Toggle("Enable Source Control", isOn: $prefs.preferences.sourceControl.general.enableSourceControl) - .toggleStyle(.checkbox) - - VStack(alignment: .leading) { - Toggle( - "Refresh local status automatically", - isOn: $prefs.preferences.sourceControl.general.refreshStatusLocally - ) - .toggleStyle(.checkbox) - Toggle( - "Fetch and refresh server status automatically", - isOn: $prefs.preferences.sourceControl.general.fetchRefreshServerStatus - ) - .toggleStyle(.checkbox) - Toggle( - "Add and remove files automatically", - isOn: $prefs.preferences.sourceControl.general.addRemoveAutomatically - ) - .toggleStyle(.checkbox) - Toggle( - "Select files to commit automatically", - isOn: $prefs.preferences.sourceControl.general.selectFilesToCommit - ) - .toggleStyle(.checkbox) - } - .padding(.leading, 20) - } - - PreferencesSection("Text Editing", hideLabels: false) { - Toggle( - "Show Source Control changes", - isOn: $prefs.preferences.sourceControl.general.showSourceControlChanges - ) - .toggleStyle(.checkbox) - - Toggle( - "Include upstream changes", - isOn: $prefs.preferences.sourceControl.general.includeUpstreamChanges - ) - .toggleStyle(.checkbox) - .padding(.leading, 20) - } - - PreferencesSection("Reporting", hideLabels: false) { - Toggle( - "Open created issue in the browser", - isOn: $prefs.preferences.sourceControl.general.openFeedbackInBrowser - ) - .toggleStyle(.checkbox) - } - - PreferencesSection("Comparison View", hideLabels: true) { - Picker( - "Comparison View", - selection: $prefs.preferences.sourceControl.general.revisionComparisonLayout - ) { - Text("Local Revision on Left Side") - .tag(AppPreferences.RevisionComparisonLayout.localLeft) - Text("Local Revision on Right Side") - .tag(AppPreferences.RevisionComparisonLayout.localRight) - } - .frame(width: inputWidth) - } - - PreferencesSection("Source Control Navigator", hideLabels: true) { - Picker( - "Source Control Navigator", - selection: $prefs.preferences.sourceControl.general.controlNavigatorOrder - ) { - Text("Sort by Name") - .tag(AppPreferences.ControlNavigatorOrder.sortByName) - Text("Sort by Date") - .tag(AppPreferences.ControlNavigatorOrder.sortByDate) - } - .frame(width: inputWidth) - } - - PreferencesSection("Default Branch Name", hideLabels: false) { - TextField("main", text: $branchName) - .frame(width: inputWidth) - Text("Branch names cannot contain spaces, backslashes, or other symbols") - .font(.system(size: 12)) - .foregroundColor(.secondary) - } - } - .frame(height: 350) - .background(EffectView(.contentBackground)) - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlGitView.swift b/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlGitView.swift deleted file mode 100644 index 11cc88c0da..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlGitView.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// SourceControlGitView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI - -struct SourceControlGitView: View { - private let inputWidth: Double = 280 - - @StateObject - private var prefs: AppPreferencesModel = .shared - - @State - var ignoredFileSelection: IgnoredFiles.ID? - - var body: some View { - VStack { - PreferencesSection("Author Name", hideLabels: false) { - TextField("Git Author Name", text: $prefs.preferences.sourceControl.git.authorName) - .frame(width: inputWidth) - } - - PreferencesSection("Author Email", hideLabels: false) { - TextField("Git Email", text: $prefs.preferences.sourceControl.git.authorEmail) - .frame(width: inputWidth) - } - - PreferencesSection("Ignored Files", hideLabels: false, align: .top) { - VStack(spacing: 1) { - List( - $prefs.preferences.sourceControl.git.ignoredFiles, - selection: $ignoredFileSelection - ) { ignoredFile in - IgnoredFileView(ignoredFile: ignoredFile) - } - .overlay(Group { - if prefs.preferences.sourceControl.git.ignoredFiles.isEmpty { - Text("No Ignored Files") - .foregroundColor(.secondary) - .font(.system(size: 11)) - } - }) - .frame(height: 150) - PreferencesToolbar(height: 22) { - bottomToolbar - } - } - .frame(width: inputWidth) - .padding(1) - .background(Rectangle().foregroundColor(Color(NSColor.separatorColor))) - } - - PreferencesSection("Options", hideLabels: false) { - Toggle( - "Prefer to rebase when pulling", - isOn: $prefs.preferences.sourceControl.git.preferRebaseWhenPulling - ) - .toggleStyle(.checkbox) - .frame(width: inputWidth, alignment: .leading) - Toggle( - "Show merge commits in per-file log", - isOn: $prefs.preferences.sourceControl.git.showMergeCommitsPerFileLog - ) - .toggleStyle(.checkbox) - .frame(width: inputWidth, alignment: .leading) - } - } - .frame(height: 350) - .background(EffectView(.contentBackground)) - } - - private var bottomToolbar: some View { - HStack(spacing: 12) { - Button {} label: { - Image(systemName: "plus") - .foregroundColor(Color.secondary) - } - .buttonStyle(.plain) - Button {} label: { - Image(systemName: "minus") - } - .disabled(true) - .buttonStyle(.plain) - Spacer() - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlPreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlPreferencesView.swift deleted file mode 100644 index 44e97ea59e..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/SourceControlPreferencesView.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// SourceControlPreferencesView.swift -// CodeEditModules/AppPreferences -// -// Created by Nanshi Li on 2022/04/01. -// - -import SwiftUI - -struct SourceControlPreferencesView: View { - @State - private var selectedSection: Int = 0 - - var body: some View { - VStack(alignment: .leading, spacing: 16) { - VStack(spacing: 1) { - PreferencesToolbar { - SegmentedControl($selectedSection, options: ["General", "Git"]) - } - if selectedSection == 0 { - SourceControlGeneralView(isChecked: true, branchName: "main") - } - if selectedSection == 1 { - SourceControlGitView() - } - } - .padding(1) - .background(Rectangle().foregroundColor(Color(NSColor.separatorColor))) - .frame(width: 872) - .padding() - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/TerminalPreferences/TerminalPreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/TerminalPreferences/TerminalPreferencesView.swift deleted file mode 100644 index 2c81484c8f..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/TerminalPreferences/TerminalPreferencesView.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// TerminalPreferencesView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 02.04.22. -// - -import SwiftUI -import Preferences - -/// A view that implements the `Terminal` preference section -struct TerminalPreferencesView: View { - private let inputWidth: Double = 150 - - @StateObject - private var prefs: AppPreferencesModel = .shared - - init() {} - - var body: some View { - PreferencesContent { - PreferencesSection("Shell") { - shellSelector - optionAsMetaToggle - } - PreferencesSection("Font") { - fontSelector - } - PreferencesSection("Cursor") { - cursorStyle - cursorBlink - } - } - } - - private var shellSelector: some View { - Picker("Shell:", selection: $prefs.preferences.terminal.shell) { - Text("System Default") - .tag(AppPreferences.TerminalShell.system) - Divider() - Text("ZSH") - .tag(AppPreferences.TerminalShell.zsh) - Text("Bash") - .tag(AppPreferences.TerminalShell.bash) - } - .frame(width: inputWidth) - } - private var cursorStyle: some View { - Picker("Terminal Cursor Style: ", selection: $prefs.preferences.terminal.cursorStyle) { - Text("Block") - .tag(AppPreferences.TerminalCursorStyle.block) - Text("Underline") - .tag(AppPreferences.TerminalCursorStyle.underline) - Text("Bar") - .tag(AppPreferences.TerminalCursorStyle.bar) - } - .frame(width: inputWidth) - } - - private var cursorBlink: some View { - HStack { - Toggle("Cursor Blink", isOn: $prefs.preferences.terminal.cursorBlink) - Text("Blink cursor") - } - } - - private var optionAsMetaToggle: some View { - HStack { - Toggle("Option as Meta", isOn: $prefs.preferences.terminal.optionAsMeta) - Text("Use \"Option\" key as \"Meta\"") - } - } - - @ViewBuilder - private var fontSelector: some View { - Picker("Font:", selection: $prefs.preferences.terminal.font.customFont) { - Text("System Font") - .tag(false) - Text("Custom") - .tag(true) - } - .frame(width: inputWidth) - if prefs.preferences.terminal.font.customFont { - FontPicker( - "\(prefs.preferences.terminal.font.name) \(prefs.preferences.terminal.font.size)", - name: $prefs.preferences.terminal.font.name, size: $prefs.preferences.terminal.font.size - ) - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/TextEditingPreferences/TextEditingPreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/TextEditingPreferences/TextEditingPreferencesView.swift deleted file mode 100644 index 2034f6ee42..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/TextEditingPreferences/TextEditingPreferencesView.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// TextEditingPreferencesView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 30.03.22. -// - -import SwiftUI - -/// A view that implements the `Text Editing` preference section -struct TextEditingPreferencesView: View { - @StateObject - private var prefs: AppPreferencesModel = .shared - - /// only allows integer values in the range of `[1...8]` - private var tabWidthFormatter: NumberFormatter { - let formatter = NumberFormatter() - formatter.allowsFloats = false - formatter.minimum = 1 - formatter.maximum = 8 - - return formatter - } - - /// only allows float values in the range of `[0.75...2.00]` - /// and formats to 2 decimal places. - private var lineHeightFormatter: NumberFormatter { - let formatter = NumberFormatter() - formatter.allowsFloats = true - formatter.maximumFractionDigits = 2 - formatter.minimumFractionDigits = 2 - formatter.minimum = 0.75 - formatter.maximum = 2.0 - - return formatter - } - - private var fontSizeFormatter: NumberFormatter { - let formatter = NumberFormatter() - formatter.allowsFloats = false - formatter.minimum = 1 - formatter.maximum = 288 - - return formatter - } - - var body: some View { - PreferencesContent { - PreferencesSection("Default Tab Width") { - HStack(spacing: 5) { - TextField("", value: $prefs.preferences.textEditing.defaultTabWidth, formatter: tabWidthFormatter) - .multilineTextAlignment(.trailing) - .frame(width: 40) - Stepper( - "Default Tab Width:", - value: $prefs.preferences.textEditing.defaultTabWidth, - in: 1...8 - ) - Text("spaces") - } - } - PreferencesSection("Font") { - fontSelector - } - PreferencesSection("Font Size") { - fontSizeSelector - } - PreferencesSection("Line Height") { - lineHeight - } - PreferencesSection("Code completion") { - autocompleteBraces - enableTypeOverCompletion - } - PreferencesSection("Line Wrapping") { - wrapLinesToEditorWidth - } - } - } - - @ViewBuilder - private var fontSelector: some View { - Picker("Font:", selection: $prefs.preferences.textEditing.font.customFont) { - Text("System Font") - .tag(false) - Text("Custom") - .tag(true) - } - .fixedSize() - if prefs.preferences.textEditing.font.customFont { - FontPicker( - "\(prefs.preferences.textEditing.font.name) \(prefs.preferences.textEditing.font.size)", - name: $prefs.preferences.textEditing.font.name, size: $prefs.preferences.textEditing.font.size - ) - } - } - - private var fontSizeSelector: some View { - HStack(spacing: 5) { - TextField("", value: $prefs.preferences.textEditing.font.size, formatter: fontSizeFormatter) - .multilineTextAlignment(.trailing) - .frame(width: 40) - Stepper( - "Font Size:", - value: $prefs.preferences.textEditing.font.size, - in: 1...288, - step: 1 - ) - } - } - - private var autocompleteBraces: some View { - HStack { - Toggle("Autocomplete braces", isOn: $prefs.preferences.textEditing.autocompleteBraces) - Text("Automatically insert closing braces (\"}\")") - } - } - - private var enableTypeOverCompletion: some View { - HStack { - Toggle("Enable type-over completion", isOn: $prefs.preferences.textEditing.enableTypeOverCompletion) - Text("Enable type-over completion") - } - } - - private var wrapLinesToEditorWidth: some View { - HStack { - Toggle("Wrap lines to editor width", isOn: $prefs.preferences.textEditing.wrapLinesToEditorWidth) - Text("Wrap lines to editor width") - } - } - - private var lineHeight: some View { - HStack(spacing: 5) { - TextField("", value: $prefs.preferences.textEditing.lineHeightMultiple, formatter: lineHeightFormatter) - .multilineTextAlignment(.trailing) - .frame(width: 40) - Stepper( - "Line Height:", - value: $prefs.preferences.textEditing.lineHeightMultiple, - in: 0.75...2.0, - step: 0.05 - ) - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/EditorThemeView.swift b/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/EditorThemeView.swift deleted file mode 100644 index 4f455f1987..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/EditorThemeView.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// EditorThemeView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 31.03.22. -// - -import SwiftUI - -struct EditorThemeView: View { - @StateObject - private var themeModel: ThemeModel = .shared - - @StateObject - private var prefs: AppPreferencesModel = .shared - - var body: some View { - ZStack(alignment: .topLeading) { - EffectView(.contentBackground) - if let selectedTheme = themeModel.selectedTheme, - let index = themeModel.themes.firstIndex(of: selectedTheme) { - VStack(alignment: .leading, spacing: 0) { - Toggle("Use theme background ", isOn: $prefs.preferences.theme.useThemeBackground) - .padding(.bottom, 20) - HStack(spacing: 0) { - VStack(alignment: .leading, spacing: 10) { - PreferencesColorPicker( - $themeModel.themes[index].editor.text.swiftColor, - label: "Text" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.insertionPoint.swiftColor, - label: "Cursor" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.invisibles.swiftColor, - label: "Invisibles" - ) - } - .frame(maxWidth: .infinity, alignment: .leading) - VStack(alignment: .leading, spacing: 10) { - PreferencesColorPicker( - $themeModel.themes[index].editor.background.swiftColor, - label: "Background" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.lineHighlight.swiftColor, - label: "Current Line" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.selection.swiftColor, - label: "Selection" - ) - } - .frame(maxWidth: .infinity, alignment: .leading) - } - .padding(.bottom, 20) - Text("Syntax") - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(.secondary) - .padding(.bottom, 10) - HStack(spacing: 0) { - VStack(alignment: .leading, spacing: 10) { - PreferencesColorPicker( - $themeModel.themes[index].editor.keywords.swiftColor, - label: "Keywords" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.commands.swiftColor, - label: "Commands" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.types.swiftColor, - label: "Types" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.attributes.swiftColor, - label: "Attributes" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.variables.swiftColor, - label: "Variables" - ) - } - .frame(maxWidth: .infinity, alignment: .leading) - VStack(alignment: .leading, spacing: 10) { - PreferencesColorPicker( - $themeModel.themes[index].editor.values.swiftColor, - label: "Values" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.numbers.swiftColor, - label: "Numbers" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.strings.swiftColor, - label: "Strings" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.characters.swiftColor, - label: "Characters" - ) - PreferencesColorPicker( - $themeModel.themes[index].editor.comments.swiftColor, - label: "Comments" - ) - } - .frame(maxWidth: .infinity, alignment: .leading) - } - } - .padding(20) - } else { - Text("Select a Theme") - .foregroundColor(.secondary) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/PreviewThemeView.swift b/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/PreviewThemeView.swift deleted file mode 100644 index 38486229fb..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/PreviewThemeView.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// PreviewThemeView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 31.03.22. -// - -import SwiftUI - -struct PreviewThemeView: View { - @StateObject - private var themeModel: ThemeModel = .shared - - var body: some View { - ZStack(alignment: .topLeading) { - EffectView(.contentBackground) - if themeModel.selectedTheme == nil { - Text("Select a Theme") - .foregroundColor(.secondary) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - } else { - Text("Preview is not yet implemented") - .foregroundColor(.secondary) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/TerminalThemeView.swift b/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/TerminalThemeView.swift deleted file mode 100644 index 6d481bb4f2..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/TerminalThemeView.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// TerminalThemeView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 31.03.22. -// - -import SwiftUI - -struct TerminalThemeView: View { - @StateObject - private var prefs: AppPreferencesModel = .shared - - @StateObject - private var themeModel: ThemeModel = .shared - - var body: some View { - ZStack(alignment: .topLeading) { - EffectView(.contentBackground) - if themeModel.selectedTheme == nil { - Text("Select a Theme") - .foregroundColor(.secondary) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - } else { - VStack(alignment: .leading, spacing: 15) { - topToggles - colorSelector - ansiColorSelector - } - .padding(20) - } - } - } - - private var topToggles: some View { - VStack(alignment: .leading) { - Toggle("Always use dark terminal appearance", isOn: $prefs.preferences.terminal.darkAppearance) - Toggle("Use theme background ", isOn: $prefs.preferences.terminal.useThemeBackground) - } - } - - private var colorSelector: some View { - VStack(alignment: .leading, spacing: 0) { - Text("Background & Text") - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(.secondary) - .padding(.bottom, 10) - HStack(alignment: .top, spacing: 0) { - if let selectedTheme = themeModel.selectedTheme, - let index = themeModel.themes.firstIndex(of: selectedTheme) { - VStack(alignment: .leading, spacing: 10) { - PreferencesColorPicker( - $themeModel.themes[index].terminal.text.swiftColor, - label: "Text" - ) - PreferencesColorPicker( - $themeModel.themes[index].terminal.boldText.swiftColor, - label: "Bold Text" - ) - PreferencesColorPicker( - $themeModel.themes[index].terminal.cursor.swiftColor, - label: "Cursor" - ) - } - .frame(maxWidth: .infinity, alignment: .leading) - VStack(alignment: .leading, spacing: 10) { - PreferencesColorPicker( - $themeModel.themes[index].terminal.background.swiftColor, - label: "Background" - ) - PreferencesColorPicker( - $themeModel.themes[index].terminal.selection.swiftColor, - label: "Selection" - ) - } - .frame(maxWidth: .infinity, alignment: .leading) - } - } - } - } - - private var ansiColorSelector: some View { - VStack(alignment: .leading, spacing: 5) { - if let selectedTheme = themeModel.selectedTheme, - let index = themeModel.themes.firstIndex(of: selectedTheme) { - Text("ANSI Colors") - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(.secondary) - .padding(.bottom, 5) - HStack(spacing: 5) { - PreferencesColorPicker($themeModel.themes[index].terminal.black.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.red.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.green.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.yellow.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.blue.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.magenta.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.cyan.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.white.swiftColor) - Text("Normal").padding(.leading, 4) - } - HStack(spacing: 5) { - PreferencesColorPicker($themeModel.themes[index].terminal.brightBlack.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.brightRed.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.brightGreen.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.brightYellow.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.brightBlue.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.brightMagenta.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.brightCyan.swiftColor) - PreferencesColorPicker($themeModel.themes[index].terminal.brightWhite.swiftColor) - Text("Bright").padding(.leading, 4) - } - .padding(.top, 5) - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/ThemePreferencesView.swift b/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/ThemePreferencesView.swift deleted file mode 100644 index 1e57d7bed6..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/ThemePreferencesView.swift +++ /dev/null @@ -1,216 +0,0 @@ -// -// ThemePreferencesView.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 30.03.22. -// - -import SwiftUI -import Preferences - -/// A view that implements the `Theme` preference section -struct ThemePreferencesView: View { - @Environment(\.colorScheme) - var colorScheme - - @ObservedObject - private var themeModel: ThemeModel = .shared - - @ObservedObject - private var prefs: AppPreferencesModel = .shared - - @State - private var listView: Bool = false - - var body: some View { - VStack(spacing: 20) { - frame - HStack(alignment: .center) { - Toggle( - "Automatically change theme based on system appearance", - isOn: $prefs.preferences.theme.mirrorSystemAppearance - ) - .onChange(of: prefs.preferences.theme.mirrorSystemAppearance) { value in - if value { - if colorScheme == .dark { - themeModel.selectedTheme = themeModel.selectedDarkTheme - } else { - themeModel.selectedTheme = themeModel.selectedLightTheme - } - } else { - themeModel.selectedTheme = themeModel.themes.first { - $0.name == prefs.preferences.theme.selectedTheme - } - } - } - - Spacer() - Button("Get More Themes...") {} - .disabled(true) - .help("Not yet implemented") - HelpButton {} - .disabled(true) - .help("Not yet implemented") - } - } - .frame(width: 872) - .padding() - .onAppear { - try? themeModel.loadThemes() - } - } - - private var frame: some View { - VStack(alignment: .leading, spacing: 16) { - HStack(spacing: 1) { - sidebar - settingsContent - } - .padding(1) - .background(Rectangle().foregroundColor(Color(NSColor.separatorColor))) - .frame(height: 468) - } - } - - private var sidebar: some View { - VStack(spacing: 1) { - PreferencesToolbar { - let options = [ - "Dark Mode", - "Light Mode" - ] - SegmentedControl($themeModel.selectedAppearance, options: options) - } - if listView { - sidebarListView - } else { - sidebarScrollView - } - PreferencesToolbar { - sidebarBottomToolbar - } - } - .frame(width: 320) - } - - private var sidebarListView: some View { - List(selection: $themeModel.selectedTheme) { - ForEach(themeModel.selectedAppearance == 0 ? themeModel.darkThemes : themeModel.lightThemes) { theme in - Button(theme.displayName) { themeModel.selectedTheme = theme } - .buttonStyle(.plain) - .tag(theme) - .contextMenu { - Button("Reset Theme") { - themeModel.reset(theme) - } - Divider() - Button("Delete Theme", role: .destructive) { - themeModel.delete(theme) - } - .disabled(themeModel.themes.count <= 1) - } - } - } - } - - private var sidebarScrollView: some View { - ScrollView { - let grid: [GridItem] = .init( - repeating: .init(.fixed(130), spacing: 20, alignment: .center), - count: 2 - ) - LazyVGrid(columns: grid, alignment: .center, spacing: 20) { - ForEach(themeModel.selectedAppearance == 0 ? themeModel.darkThemes : themeModel.lightThemes) { theme in - ThemePreviewIcon( - theme, - selection: $themeModel.selectedTheme, - colorScheme: themeModel.selectedAppearance == 0 ? .dark : .light - ) - .transition(.opacity) - .contextMenu { - Button("Reset Theme") { - themeModel.reset(theme) - } - Divider() - Button("Delete Theme", role: .destructive) { - themeModel.delete(theme) - } - .disabled(themeModel.themes.count <= 1) - } - } - } - .padding(.vertical, 20) - } - .background(EffectView(.contentBackground)) - } - - private var sidebarBottomToolbar: some View { - HStack { - Button {} label: { - Image(systemName: "plus") - } - .disabled(true) - .help("Not yet implemented") - .buttonStyle(.plain) - Button { - themeModel.delete(themeModel.selectedTheme!) - } label: { - Image(systemName: "minus") - } - .disabled(themeModel.selectedTheme == nil || themeModel.themes.count <= 1) - .help("Delete selected theme") - .buttonStyle(.plain) - Divider() - Button { try? themeModel.loadThemes() } label: { - Image(systemName: "arrow.clockwise") - } - .buttonStyle(.plain) - Spacer() - Button { - listView = true - } label: { - Image(systemName: "list.dash") - .foregroundColor(listView ? .accentColor : .primary) - } - .buttonStyle(.plain) - Button { - listView = false - } label: { - Image(systemName: "square.grid.2x2") - .symbolVariant(listView ? .none : .fill) - .foregroundColor(listView ? .primary : .accentColor) - } - .buttonStyle(.plain) - } - } - - private var settingsContent: some View { - VStack(spacing: 1) { - let options = [ - "Preview", - "Editor", - "Terminal" - ] - PreferencesToolbar { - SegmentedControl($themeModel.selectedTab, options: options) - } - switch themeModel.selectedTab { - case 1: - EditorThemeView() - case 2: - TerminalThemeView() - default: - PreviewThemeView() - } - PreferencesToolbar { - HStack { - Spacer() - Button {} label: { - Image(systemName: "info.circle") - } - .buttonStyle(.plain) - } - } - } - } -} diff --git a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/ThemePreviewIcon.swift b/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/ThemePreviewIcon.swift deleted file mode 100644 index b0ce9219ef..0000000000 --- a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/ThemePreviewIcon.swift +++ /dev/null @@ -1,260 +0,0 @@ -// -// ThemePreviewIcon.swift -// CodeEditModules/AppPreferences -// -// Created by Lukas Pistrol on 30.03.22. -// - -import SwiftUI - -struct ThemePreviewIcon: View { - var theme: Theme - var colorScheme: ColorScheme - - @Binding - var selection: Theme? - - init(_ theme: Theme, selection: Binding, colorScheme: ColorScheme) { - self.theme = theme - self._selection = selection - self.colorScheme = colorScheme - } - - var body: some View { - VStack { - ZStack(alignment: .topLeading) { - RoundedRectangle(cornerRadius: 3) - .foregroundColor(Color(hex: colorScheme == .dark ? 0x4c4c4c : 0xbbbbbb)) - - HStack(spacing: 1) { - sidebar - content - } - .clipShape(RoundedRectangle(cornerRadius: 2)) - .padding(1) - } - .padding(1) - .frame(width: 130, height: 88) - .shadow(color: Color(NSColor.shadowColor).opacity(0.1), radius: 8, x: 0, y: 2) - .overlay { - RoundedRectangle(cornerRadius: 4) - .strokeBorder(lineWidth: 2) - .foregroundColor(selection == theme ? .accentColor : .clear) - } - Text(theme.displayName) - .font(.subheadline) - .padding(.horizontal, 7) - .padding(.vertical, 2) - .foregroundColor(selection == theme ? .white : .primary) - .background(Capsule().foregroundColor(selection == theme ? .accentColor : .clear)) - } - .help(theme.metadataDescription) - .onTapGesture { - withAnimation(.interactiveSpring()) { - self.selection = theme - } - } - } - - private var sidebar: some View { - ZStack(alignment: .topLeading) { - Rectangle() - .foregroundColor(Color(hex: colorScheme == .dark ? 0x383838 : 0xd0d0d0)) - .frame(width: 36) - - HStack(spacing: 1.5) { - Circle().foregroundColor(.red) - Circle().foregroundColor(Color(hex: 0xf9b82d)) - Circle().foregroundColor(.green) - } - .frame(width: 12, height: 3) - .padding(4) - } - } - - private var content: some View { - VStack(spacing: 0) { - Rectangle() - .foregroundColor(Color(hex: colorScheme == .dark ? 0x2b2b2b : 0xe0e0e0)) - .frame(height: 10) - Rectangle() - .foregroundColor(theme.editor.background.swiftColor) - .overlay(alignment: .topLeading) { - codeWindow - } - } - } - - private var codeWindow: some View { - VStack(alignment: .leading, spacing: 4) { - block1 - block2 - block3 - block4 - block5 - } - .padding(.top, 6) - .padding(.leading, 6) - } - - private var block1: some View { - codeStatement(theme.editor.comments.color, length: 25) - } - - private var block2: some View { - VStack(alignment: .leading, spacing: 1) { - HStack(spacing: 1) { - codeStatement(theme.editor.keywords.color, length: 6) - codeStatement(theme.editor.variables.color, length: 6) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.values.color, length: 8) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.values.color, length: 8) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.keywords.color, length: 6) - codeStatement(theme.editor.strings.color, length: 7) - } - HStack(spacing: 1) { - codeStatement(theme.editor.keywords.color, length: 6) - codeStatement(theme.editor.variables.color, length: 8) - codeStatement(theme.editor.keywords.color, length: 6) - codeStatement(theme.editor.strings.color, length: 12) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeStatement(theme.editor.keywords.color, length: 6) - codeStatement(theme.editor.strings.color, length: 14) - codeStatement(theme.editor.text.color, length: 1) - } - } - } - - private var block3: some View { - VStack(alignment: .leading, spacing: 1) { - HStack(spacing: 1) { - codeStatement(theme.editor.keywords.color, length: 4) - codeStatement(theme.editor.variables.color, length: 8) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(3) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(5) - codeStatement(theme.editor.text.color, length: 3) - codeStatement(theme.editor.numbers.color, length: 1) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(5) - codeStatement(theme.editor.text.color, length: 6) - codeStatement(theme.editor.strings.color, length: 7) - codeStatement(theme.editor.strings.color, length: 5) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(5) - codeStatement(theme.editor.text.color, length: 5) - codeStatement(theme.editor.keywords.color, length: 5) - } - HStack(spacing: 1) { - codeSpace(3) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeStatement(theme.editor.text.color, length: 2) - } - } - } - - private var block4: some View { - VStack(alignment: .leading, spacing: 1) { - HStack(spacing: 1) { - codeStatement(theme.editor.keywords.color, length: 6) - codeStatement(theme.editor.keywords.color, length: 7) - codeStatement(theme.editor.commands.color, length: 8) - codeStatement(theme.editor.values.color, length: 3) - codeStatement(theme.editor.text.color, length: 2) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(3) - codeStatement(theme.editor.keywords.color, length: 4) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.variables.color, length: 5) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.values.color, length: 8) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.types.color, length: 8) - codeStatement(theme.editor.text.color, length: 2) - } - } - } - - private var block5: some View { - VStack(alignment: .leading, spacing: 1) { - HStack(spacing: 1) { - codeSpace(3) - codeStatement(theme.editor.keywords.color, length: 4) - codeStatement(theme.editor.variables.color, length: 10) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.types.color, length: 11) - codeStatement(theme.editor.text.color, length: 3) - codeStatement(theme.editor.keywords.color, length: 2) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(5) - codeStatement(theme.editor.attributes.color, length: 8) - codeStatement(theme.editor.text.color, length: 2) - codeStatement(theme.editor.variables.color, length: 5) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.keywords.color, length: 2) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(7) - codeStatement(theme.editor.keywords.color, length: 3) - codeStatement(theme.editor.variables.color, length: 12) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(9) - codeStatement(theme.editor.text.color, length: 3) - codeStatement(theme.editor.characters.color, length: 1) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(9) - codeStatement(theme.editor.text.color, length: 1) - } - HStack(spacing: 1) { - codeSpace(9) - codeStatement(theme.editor.text.color, length: 3) - codeStatement(theme.editor.attributes.color, length: 5) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.types.color, length: 6) - codeStatement(theme.editor.text.color, length: 1) - codeStatement(theme.editor.numbers.color, length: 1) - codeStatement(theme.editor.text.color, length: 1) - } - } - } - - private func codeStatement(_ color: String, length: Double) -> some View { - Rectangle() - .foregroundColor(Color(hex: color)) - .frame(width: length, height: 2) - } - - private func codeSpace(_ length: Double) -> some View { - Rectangle() - .foregroundColor(.clear) - .frame(width: length-1, height: 2) - } -} diff --git a/CodeEdit/Features/CodeEditUI/Views/FontPickerView.swift b/CodeEdit/Features/CodeEditUI/Views/FontPickerView.swift deleted file mode 100644 index 99795c715e..0000000000 --- a/CodeEdit/Features/CodeEditUI/Views/FontPickerView.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// FontPickerView.swift -// CodeEditModules/CodeEditUI -// -// Created by Lukas Pistrol on 23.03.22. -// - -import SwiftUI - -final class FontPickerDelegate { - var parent: FontPicker - - init(_ parent: FontPicker) { - self.parent = parent - } - - @objc - func changeFont(_ id: Any) { - parent.fontSelected() - } - -} - -/// A view that opens a `NSFontPanel` in order to choose a font installed on the system. -struct FontPicker: View { - private let labelString: String - - @State - private var fontPickerDelegate: FontPickerDelegate? - - @Binding - private var fontName: String - - @Binding - private var fontSize: Int - - private var font: NSFont { - get { - NSFont(name: fontName, size: CGFloat(fontSize)) ?? .systemFont(ofSize: CGFloat(fontSize)) - } - set { - self.fontName = newValue.fontName - self.fontSize = Int(newValue.pointSize) - } - } - - init(_ label: String, name: Binding, size: Binding) { - self.labelString = label - self._fontName = name - self._fontSize = size - } - - var body: some View { - HStack { - Text(labelString) - .lineLimit(1) - .truncationMode(.middle) - - Button { - if NSFontPanel.shared.isVisible { - NSFontPanel.shared.orderOut(nil) - return - } - - self.fontPickerDelegate = FontPickerDelegate(self) - NSFontManager.shared.target = self.fontPickerDelegate - NSFontPanel.shared.setPanelFont(self.font, isMultiple: false) - NSFontPanel.shared.orderBack(nil) - } label: { - Text("Select...") - } - .fixedSize() - } - } - - mutating - func fontSelected() { - self.font = NSFontPanel.shared.convert(self.font) - } -} diff --git a/CodeEdit/Features/CodeFile/CodeFile.swift b/CodeEdit/Features/CodeFile/CodeFile.swift index 31a4ea1811..72ca1fbe96 100644 --- a/CodeEdit/Features/CodeFile/CodeFile.swift +++ b/CodeEdit/Features/CodeFile/CodeFile.swift @@ -62,11 +62,11 @@ final class CodeFileDocument: NSDocument, ObservableObject, QLPreviewItem { // MARK: - NSDocument override class var autosavesInPlace: Bool { - AppPreferencesModel.shared.preferences.general.isAutoSaveOn + Settings.shared.preferences.general.isAutoSaveOn } override var autosavingFileType: String? { - AppPreferencesModel.shared.preferences.general.isAutoSaveOn + Settings.shared.preferences.general.isAutoSaveOn ? fileType : nil } diff --git a/CodeEdit/Features/CodeFile/CodeFileView.swift b/CodeEdit/Features/CodeFile/CodeFileView.swift index 743bd4409b..8de951edd7 100644 --- a/CodeEdit/Features/CodeFile/CodeFileView.swift +++ b/CodeEdit/Features/CodeFile/CodeFileView.swift @@ -16,8 +16,7 @@ struct CodeFileView: View { @ObservedObject private var codeFile: CodeFileDocument - @ObservedObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings @Environment(\.colorScheme) private var colorScheme @@ -57,7 +56,7 @@ struct CodeFileView: View { @State private var font: NSFont = { - return AppPreferencesModel.shared.preferences.textEditing.font.current() + return Settings.shared.preferences.textEditing.font.current() }() @Environment(\.edgeInsets) @@ -72,31 +71,27 @@ struct CodeFileView: View { language: getLanguage(), theme: $selectedTheme.editor.editorTheme, font: $font, - tabWidth: $prefs.preferences.textEditing.defaultTabWidth, - lineHeight: $prefs.preferences.textEditing.lineHeightMultiple, - wrapLines: $prefs.preferences.textEditing.wrapLinesToEditorWidth, + tabWidth: $settings.textEditing.defaultTabWidth, + lineHeight: $settings.textEditing.lineHeightMultiple, + wrapLines: $settings.textEditing.wrapLinesToEditorWidth, cursorPosition: $codeFile.cursorPosition, - useThemeBackground: prefs.preferences.theme.useThemeBackground, + useThemeBackground: settings.theme.useThemeBackground, contentInsets: edgeInsets.nsEdgeInsets, isEditable: isEditable ) .id(codeFile.fileURL) .background { if colorScheme == .dark { - if prefs.preferences.theme.selectedTheme == prefs.preferences.theme.selectedLightTheme { - Color.white - } else { - EffectView(.underPageBackground) - } + EffectView(.underPageBackground) } else { - if prefs.preferences.theme.selectedTheme == prefs.preferences.theme.selectedDarkTheme { - Color.black - } else { - EffectView(.contentBackground) - } - + EffectView(.contentBackground) } } + .colorScheme( + selectedTheme.appearance == .dark + ? .dark + : .light + ) // minHeight zero fixes a bug where the app would freeze if the contents of the file are empty. .frame(minHeight: .zero, maxHeight: .infinity) .onChange(of: ThemeModel.shared.selectedTheme) { newValue in @@ -104,14 +99,14 @@ struct CodeFileView: View { self.selectedTheme = theme } .onChange(of: colorScheme) { newValue in - if prefs.preferences.theme.mirrorSystemAppearance { + if settings.theme.matchAppearance { ThemeModel.shared.selectedTheme = newValue == .dark ? ThemeModel.shared.selectedDarkTheme! : ThemeModel.shared.selectedLightTheme! } } - .onChange(of: prefs.preferences.textEditing.font) { _ in - font = AppPreferencesModel.shared.preferences.textEditing.font.current() + .onChange(of: settings.textEditing.font) { _ in + font = Settings.shared.preferences.textEditing.font.current() } } diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift index 64602c3ac8..ab3459b4f4 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift @@ -12,8 +12,6 @@ final class CodeEditDocumentController: NSDocumentController { FileManager.default }() - private var prefs: AppPreferencesModel = .shared - override func newDocument(_ sender: Any?) { guard let newDocumentUrl = self.newDocumentUrl else { return } @@ -80,7 +78,7 @@ final class CodeEditDocumentController: NSDocumentController { super.removeDocument(document) if CodeEditDocumentController.shared.documents.isEmpty { - switch prefs.preferences.general.reopenWindowAfterClose { + switch Settings[\.general].reopenWindowAfterClose { case .showWelcomeWindow: // Opens the welcome window NSApp.openWindow(.welcome) diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift index 696631917d..21183acfaa 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift @@ -16,8 +16,6 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs var observers: [NSKeyValueObservation] = [] - private var prefs: AppPreferencesModel = .shared - var workspace: WorkspaceDocument? var quickOpenPanel: OverlayPanel? var commandPalettePanel: OverlayPanel? @@ -138,7 +136,7 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs toolbar.showsBaselineSeparator = false self.window?.titleVisibility = .hidden self.window?.toolbarStyle = .unifiedCompact - if prefs.preferences.general.tabBarStyle == .native { + if Settings[\.general].tabBarStyle == .native { // Set titlebar background as transparent by default in order to // style the toolbar background in native tab bar style. self.window?.titlebarSeparatorStyle = .none diff --git a/CodeEdit/Features/Documents/Views/WorkspaceCodeFileView.swift b/CodeEdit/Features/Documents/Views/WorkspaceCodeFileView.swift index 0c8323f6cc..789cfa85a8 100644 --- a/CodeEdit/Features/Documents/Views/WorkspaceCodeFileView.swift +++ b/CodeEdit/Features/Documents/Views/WorkspaceCodeFileView.swift @@ -12,7 +12,7 @@ struct WorkspaceCodeFileView: View { var file: WorkspaceClient.FileItem @StateObject - private var prefs: AppPreferencesModel = .shared + private var prefs: Settings = .shared @ViewBuilder var codeView: some View { diff --git a/CodeEdit/Features/Feedback/FeedbackView.swift b/CodeEdit/Features/Feedback/FeedbackView.swift index 1c4d7af8cc..c49f2cda93 100644 --- a/CodeEdit/Features/Feedback/FeedbackView.swift +++ b/CodeEdit/Features/Feedback/FeedbackView.swift @@ -12,7 +12,7 @@ struct FeedbackView: View { private var feedbackModel: FeedbackModel = .shared @StateObject - var prefs: AppPreferencesModel = .shared + var prefs: Settings = .shared @State var showsAlert: Bool = false diff --git a/CodeEdit/Features/Feedback/Model/FeedbackModel.swift b/CodeEdit/Features/Feedback/Model/FeedbackModel.swift index 6b44e8d348..998b717815 100644 --- a/CodeEdit/Features/Feedback/Model/FeedbackModel.swift +++ b/CodeEdit/Features/Feedback/Model/FeedbackModel.swift @@ -11,7 +11,6 @@ public class FeedbackModel: ObservableObject { public static let shared: FeedbackModel = .init() - private var prefs: AppPreferencesModel = .shared private let keychain = CodeEditKeychain() @Environment(\.openURL) var openIssueURL @@ -145,10 +144,10 @@ public class FeedbackModel: ObservableObject { expectation: String?, actuallyHappened: String? ) { - let gitAccounts = prefs.preferences.accounts.sourceControlAccounts.gitAccount + let gitAccounts = Settings[\.accounts].sourceControlAccounts.gitAccounts let firstGitAccount = gitAccounts.first - let config = GitHubTokenConfiguration(keychain.get(firstGitAccount!.gitAccountName)) + let config = GitHubTokenConfiguration(keychain.get(firstGitAccount!.name)) GitHubAccount(config).postIssue( owner: "CodeEditApp", repository: "CodeEdit", @@ -164,7 +163,7 @@ public class FeedbackModel: ObservableObject { ) { response in switch response { case .success(let issue): - if self.prefs.preferences.sourceControl.general.openFeedbackInBrowser { + if Settings[\.sourceControl].general.openFeedbackInBrowser { self.openIssueURL(issue.htmlURL ?? URL(string: "https://github.com/CodeEditApp/CodeEdit/issues")!) } self.isSubmitted.toggle() diff --git a/CodeEdit/Features/Git/Accounts/GitHub/GitHubConfiguration.swift b/CodeEdit/Features/Git/Accounts/GitHub/GitHubConfiguration.swift index 2454e97cba..b62fcb8754 100644 --- a/CodeEdit/Features/Git/Accounts/GitHub/GitHubConfiguration.swift +++ b/CodeEdit/Features/Git/Accounts/GitHub/GitHubConfiguration.swift @@ -27,7 +27,7 @@ struct GitHubTokenConfiguration: GitRouterConfiguration { return previewCustomHeaders } - init(_ token: String? = nil, url: String = GitURL.githubBaseURL, previewHeaders: [GitHubPreviewHeader] = []) { + init(_ token: String? = nil, url: String? = GitURL.githubBaseURL, previewHeaders: [GitHubPreviewHeader] = []) { apiEndpoint = url accessToken = token?.data(using: .utf8)!.base64EncodedString() previewCustomHeaders = previewHeaders.map { $0.header } diff --git a/CodeEdit/Features/Git/Accounts/GitLab/GitLabConfiguration.swift b/CodeEdit/Features/Git/Accounts/GitLab/GitLabConfiguration.swift index 0871e701ee..cf706c7a8f 100644 --- a/CodeEdit/Features/Git/Accounts/GitLab/GitLabConfiguration.swift +++ b/CodeEdit/Features/Git/Accounts/GitLab/GitLabConfiguration.swift @@ -13,7 +13,7 @@ struct GitLabTokenConfiguration: GitRouterConfiguration { var accessToken: String? let errorDomain: String? = "com.codeedit.models.accounts.gitlab" - init(_ token: String? = nil, url: String = GitURL.gitlabBaseURL) { + init(_ token: String? = nil, url: String? = GitURL.gitlabBaseURL) { apiEndpoint = url accessToken = token } @@ -24,7 +24,7 @@ struct GitLabPrivateTokenConfiguration: GitRouterConfiguration { var accessToken: String? let errorDomain: String? = "com.codeedit.models.accounts.gitlab" - init(_ token: String? = nil, url: String = GitURL.gitlabBaseURL) { + init(_ token: String? = nil, url: String? = GitURL.gitlabBaseURL) { apiEndpoint = url accessToken = token } diff --git a/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorListViewController.swift b/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorListViewController.swift index 27b80b5c2c..5c3a0caefb 100644 --- a/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorListViewController.swift +++ b/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorListViewController.swift @@ -17,7 +17,7 @@ final class FindNavigatorListViewController: NSViewController { private var searchItems: [SearchResultModel] = [] private var scrollView: NSScrollView! private var outlineView: NSOutlineView! - private let prefs = AppPreferencesModel.shared.preferences + private let prefs = Settings.shared.preferences private var collapsedRows: Set = [] var rowHeight: Double = 22 { diff --git a/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorResultList.swift b/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorResultList.swift index c60a6c8fe1..5108146667 100644 --- a/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorResultList.swift +++ b/CodeEdit/Features/NavigatorSidebar/FindNavigator/FindNavigatorResultList/FindNavigatorResultList.swift @@ -13,15 +13,14 @@ struct FindNavigatorResultList: NSViewControllerRepresentable { @EnvironmentObject var workspace: WorkspaceDocument - @StateObject - var prefs: AppPreferencesModel = .shared + @AppSettings var settings typealias NSViewControllerType = FindNavigatorListViewController func makeNSViewController(context: Context) -> FindNavigatorListViewController { let controller = FindNavigatorListViewController(workspace: workspace) controller.setSearchResults(workspace.searchState?.searchResult ?? []) - controller.rowHeight = prefs.preferences.general.projectNavigatorSize.rowHeight + controller.rowHeight = settings.general.projectNavigatorSize.rowHeight context.coordinator.controller = controller return controller } @@ -31,8 +30,8 @@ struct FindNavigatorResultList: NSViewControllerRepresentable { workspace.searchState?.searchResult ?? [], searchId: workspace.searchState?.searchId ) - if nsViewController.rowHeight != prefs.preferences.general.projectNavigatorSize.rowHeight { - nsViewController.rowHeight = prefs.preferences.general.projectNavigatorSize.rowHeight + if nsViewController.rowHeight != settings.general.projectNavigatorSize.rowHeight { + nsViewController.rowHeight = settings.general.projectNavigatorSize.rowHeight } return } diff --git a/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineTableViewCell.swift b/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineTableViewCell.swift index 7c037e5fe1..866a044284 100644 --- a/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineTableViewCell.swift +++ b/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineTableViewCell.swift @@ -15,13 +15,13 @@ protocol OutlineTableViewCellDelegate: AnyObject { /// A `NSTableCellView` showing an ``icon`` and a ``label`` final class OutlineTableViewCell: NSTableCellView { + @AppSettings var settings + var label: NSTextField! var icon: NSImageView! private var fileItem: WorkspaceClient.FileItem! private var delegate: OutlineTableViewCellDelegate? - private let prefs = AppPreferencesModel.shared.preferences.general - /// Initializes the `OutlineTableViewCell` with an `icon` and `label` /// Both the icon and label will be colored, and sized based on the user's preferences. /// - Parameters: @@ -116,6 +116,7 @@ final class OutlineTableViewCell: NSTableCellView { /// - Parameter item: The FileItem to generate the name for. /// - Returns: A `String` with the name to display. private func label(for item: WorkspaceClient.FileItem) -> String { + let prefs = Settings[\.general] switch prefs.fileExtensionsVisibility { case .hideAll: return item.fileName(typeHidden: true) @@ -132,7 +133,7 @@ final class OutlineTableViewCell: NSTableCellView { /// - Parameter item: The `FileItem` to get the color for /// - Returns: A `NSColor` for the given `FileItem`. private func color(for item: WorkspaceClient.FileItem) -> NSColor { - return prefs.fileIconStyle == .color + return Settings[\.general.fileIconStyle] == .color ? item.children == nil ? NSColor(item.iconColor) : NSColor(named: "FolderBlue")! : .secondaryLabelColor } diff --git a/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineView.swift b/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineView.swift index d8389ad508..3a7d890a6f 100644 --- a/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineView.swift +++ b/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineView.swift @@ -14,8 +14,7 @@ struct OutlineView: NSViewControllerRepresentable { @EnvironmentObject var workspace: WorkspaceDocument - @StateObject - var prefs: AppPreferencesModel = .shared + @AppSettings var settings // This is mainly just used to trigger a view update. @Binding @@ -26,7 +25,7 @@ struct OutlineView: NSViewControllerRepresentable { func makeNSViewController(context: Context) -> OutlineViewController { let controller = OutlineViewController() controller.workspace = workspace - controller.iconColor = prefs.preferences.general.fileIconStyle + controller.iconColor = settings.general.fileIconStyle context.coordinator.controller = controller @@ -34,11 +33,11 @@ struct OutlineView: NSViewControllerRepresentable { } func updateNSViewController(_ nsViewController: OutlineViewController, context: Context) { - nsViewController.iconColor = prefs.preferences.general.fileIconStyle - nsViewController.rowHeight = prefs.preferences.general.projectNavigatorSize.rowHeight - nsViewController.fileExtensionsVisibility = prefs.preferences.general.fileExtensionsVisibility - nsViewController.shownFileExtensions = prefs.preferences.general.shownFileExtensions - nsViewController.hiddenFileExtensions = prefs.preferences.general.hiddenFileExtensions + nsViewController.iconColor = settings.general.fileIconStyle + nsViewController.rowHeight = settings.general.projectNavigatorSize.rowHeight + nsViewController.fileExtensionsVisibility = settings.general.fileExtensionsVisibility + nsViewController.shownFileExtensions = settings.general.shownFileExtensions + nsViewController.hiddenFileExtensions = settings.general.hiddenFileExtensions nsViewController.updateSelection() return } diff --git a/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineViewController.swift b/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineViewController.swift index 05e5f1e74b..b0b5601977 100644 --- a/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineViewController.swift +++ b/CodeEdit/Features/NavigatorSidebar/ProjectNavigator/OutlineView/OutlineViewController.swift @@ -31,10 +31,10 @@ final class OutlineViewController: NSViewController { var workspace: WorkspaceDocument? - var iconColor: AppPreferences.FileIconStyle = .color - var fileExtensionsVisibility: AppPreferences.FileExtensionsVisibility = .showAll - var shownFileExtensions: AppPreferences.FileExtensions = .default - var hiddenFileExtensions: AppPreferences.FileExtensions = .default + var iconColor: SettingsData.FileIconStyle = .color + var fileExtensionsVisibility: SettingsData.FileExtensionsVisibility = .showAll + var shownFileExtensions: SettingsData.FileExtensions = .default + var hiddenFileExtensions: SettingsData.FileExtensions = .default var rowHeight: Double = 22 { didSet { @@ -304,7 +304,7 @@ extension OutlineViewController: NSOutlineViewDelegate { private func select(by id: TabBarItemID, from collection: [Item]) { // If the user has set "Reveal file on selection change" to on, we need to reveal the item before // selecting the row. - if AppPreferencesModel.shared.preferences.general.revealFileOnFocusChange, + if Settings.shared.preferences.general.revealFileOnFocusChange, case let .codeEditor(id) = id, let fileItem = try? workspace?.workspaceClient?.getFileItem(id as Item.ID) as? Item { reveal(fileItem) diff --git a/CodeEdit/Features/PathBar/Views/PathBarComponent.swift b/CodeEdit/Features/PathBar/Views/PathBarComponent.swift index bbe3cd0afa..f0f77b2687 100644 --- a/CodeEdit/Features/PathBar/Views/PathBarComponent.swift +++ b/CodeEdit/Features/PathBar/Views/PathBarComponent.swift @@ -20,7 +20,7 @@ struct PathBarComponent: View { private var activeState @StateObject - private var prefs: AppPreferencesModel = .shared + private var prefs: Settings = .shared @State var position: NSPoint? diff --git a/CodeEdit/Features/Settings/Models/AppSettings.swift b/CodeEdit/Features/Settings/Models/AppSettings.swift new file mode 100644 index 0000000000..fff7dd850f --- /dev/null +++ b/CodeEdit/Features/Settings/Models/AppSettings.swift @@ -0,0 +1,32 @@ +// +// AppSettings.swift +// CodeEdit +// +// Created by Wouter Hennen on 12/04/2023. +// + +import Foundation +import SwiftUI + +@propertyWrapper +struct AppSettings: DynamicProperty { + @ObservedObject + private var prefs: Settings + + init(_ prefs: Settings = .shared) { + self.prefs = prefs + } + + var wrappedValue: SettingsData { + get { + prefs.preferences + } + nonmutating set { + prefs.preferences = newValue + } + } + + var projectedValue: Binding { + $prefs.preferences + } +} diff --git a/CodeEdit/Features/Settings/Models/Settings.swift b/CodeEdit/Features/Settings/Models/Settings.swift new file mode 100644 index 0000000000..35039c5071 --- /dev/null +++ b/CodeEdit/Features/Settings/Models/Settings.swift @@ -0,0 +1,92 @@ +// +// SettingsModel.swift +// CodeEditModules/Settings +// +// Created by Lukas Pistrol on 01.04.22. +// + +import Foundation +import SwiftUI + +/// The Preferences View Model. Accessible via the singleton "``SettingsModel/shared``". +/// +/// **Usage:** +/// ```swift +/// @StateObject +/// private var prefs: SettingsModel = .shared +/// ``` +final class Settings: ObservableObject { + + /// The publicly available singleton instance of ``SettingsModel`` + static let shared: Settings = .init() + + private init() { + self.preferences = .init() + self.preferences = loadSettings() + } + + static subscript(_ path: WritableKeyPath, suite: Settings = .shared) -> T { + get { + suite.preferences[keyPath: path] + } + set { + suite.preferences[keyPath: path] = newValue + } + } + + /// Published instance of the ``Settings`` model. + /// + /// Changes are saved automatically. + @Published + var preferences: SettingsData { + didSet { + try? savePreferences() + } + } + + /// Load and construct ``Settings`` model from + /// `~/Library/Application Support/CodeEdit/settings.json` + private func loadSettings() -> SettingsData { + if !filemanager.fileExists(atPath: settingsURL.path) { + try? filemanager.createDirectory(at: baseURL, withIntermediateDirectories: false) + return .init() + } + + guard let json = try? Data(contentsOf: settingsURL), + let prefs = try? JSONDecoder().decode(SettingsData.self, from: json) + else { + return .init() + } + return prefs + } + + /// Save``Settings`` model to + /// `~/Library/Application Support/CodeEdit/settings.json` + private func savePreferences() throws { + let data = try JSONEncoder().encode(preferences) + let json = try JSONSerialization.jsonObject(with: data) + let prettyJSON = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted]) + try prettyJSON.write(to: settingsURL, options: .atomic) + } + + /// Default instance of the `FileManager` + private let filemanager = FileManager.default + + /// The base URL of settings. + /// + /// Points to `~/Library/Application Support/CodeEdit/` + internal var baseURL: URL { + filemanager + .homeDirectoryForCurrentUser + .appendingPathComponent("Library/Application Support/CodeEdit", isDirectory: true) + } + + /// The URL of the `settings.json` settings file. + /// + /// Points to `~/Library/Application Support/CodeEdit/settings.json` + private var settingsURL: URL { + baseURL + .appendingPathComponent("settings") + .appendingPathExtension("json") + } +} diff --git a/CodeEdit/Features/AppPreferences/Model/AppPreferences.swift b/CodeEdit/Features/Settings/Models/SettingsData.swift similarity index 57% rename from CodeEdit/Features/AppPreferences/Model/AppPreferences.swift rename to CodeEdit/Features/Settings/Models/SettingsData.swift index ab312acc39..44f38f86f1 100644 --- a/CodeEdit/Features/AppPreferences/Model/AppPreferences.swift +++ b/CodeEdit/Features/Settings/Models/SettingsData.swift @@ -1,47 +1,47 @@ // -// AppPreferences.swift -// CodeEditModules/AppPreferences +// Settings.swift +// CodeEditModules/Settings // // Created by Lukas Pistrol on 01.04.22. // import SwiftUI -/// # AppPreferences +/// # Settings /// /// The model structure of settings for `CodeEdit` /// /// A `JSON` representation is persisted in `~/Library/Application Support/CodeEdit/preference.json`. /// - Attention: Don't use `UserDefaults` for persisting user accessible settings. -/// If a further setting is needed, extend the struct like ``GeneralPreferences``, -/// ``ThemePreferences``, or ``TerminalPreferences`` does. +/// If a further setting is needed, extend the struct like ``GeneralSettings``, +/// ``ThemeSettings``, or ``TerminalSettings`` does. /// /// - Note: Also make sure to implement the ``init(from:)`` initializer, decoding /// all properties with /// [`decodeIfPresent`](https://developer.apple.com/documentation/swift/keyeddecodingcontainer/2921389-decodeifpresent) /// and providing a default value. Otherwise all settings get overridden. -struct AppPreferences: Codable { +struct SettingsData: Codable { /// The general global setting - var general: GeneralPreferences = .init() + var general: GeneralSettings = .init() /// The global settings for text editing - var accounts: AccountsPreferences = .init() + var accounts: AccountsSettings = .init() /// The global settings for themes - var theme: ThemePreferences = .init() + var theme: ThemeSettings = .init() /// The global settings for the terminal emulator - var terminal: TerminalPreferences = .init() + var terminal: TerminalSettings = .init() /// The global settings for text editing - var textEditing: TextEditingPreferences = .init() + var textEditing: TextEditingSettings = .init() /// The global settings for text editing - var sourceControl: SourceControlPreferences = .init() + var sourceControl: SourceControlSettings = .init() /// The global settings for keybindings - var keybindings: KeybindingsPreferences = .init() + var keybindings: KeybindingsSettings = .init() /// Default initializer init() {} @@ -49,15 +49,15 @@ struct AppPreferences: Codable { /// 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.general = try container.decodeIfPresent(GeneralPreferences.self, forKey: .general) ?? .init() - self.accounts = try container.decodeIfPresent(AccountsPreferences.self, forKey: .accounts) ?? .init() - self.theme = try container.decodeIfPresent(ThemePreferences.self, forKey: .theme) ?? .init() - self.terminal = try container.decodeIfPresent(TerminalPreferences.self, forKey: .terminal) ?? .init() - self.textEditing = try container.decodeIfPresent(TextEditingPreferences.self, forKey: .textEditing) ?? .init() + self.general = try container.decodeIfPresent(GeneralSettings.self, forKey: .general) ?? .init() + self.accounts = try container.decodeIfPresent(AccountsSettings.self, forKey: .accounts) ?? .init() + self.theme = try container.decodeIfPresent(ThemeSettings.self, forKey: .theme) ?? .init() + self.terminal = try container.decodeIfPresent(TerminalSettings.self, forKey: .terminal) ?? .init() + self.textEditing = try container.decodeIfPresent(TextEditingSettings.self, forKey: .textEditing) ?? .init() self.sourceControl = try container.decodeIfPresent( - SourceControlPreferences.self, + SourceControlSettings.self, forKey: .sourceControl ) ?? .init() - self.keybindings = try container.decodeIfPresent(KeybindingsPreferences.self, forKey: .keybindings) ?? .init() + self.keybindings = try container.decodeIfPresent(KeybindingsSettings.self, forKey: .keybindings) ?? .init() } } diff --git a/CodeEdit/Features/Settings/Models/SettingsPage.swift b/CodeEdit/Features/Settings/Models/SettingsPage.swift new file mode 100644 index 0000000000..613cb3933e --- /dev/null +++ b/CodeEdit/Features/Settings/Models/SettingsPage.swift @@ -0,0 +1,61 @@ +// +// SettingsPage.swift +// CodeEdit +// +// Created by Raymond Vleeshouwer on 30/03/23. +// + +import Foundation +import SwiftUI + +/// A struct for a preferences tab +struct SettingsPage: Hashable, Identifiable { + /// Default intializer + internal init( + _ name: Name, + baseColor: Color? = nil, + icon: IconResource? = nil, + hideName: Bool? = false, + children: [SettingsPage] = [] + ) { + self.children = children + self.name = name + self.baseColor = baseColor ?? .red + self.icon = icon ?? .system("questionmark.app") + self.hideName = hideName + } + + var id: String { name.rawValue } + + let name: Name + let baseColor: Color + let children: [SettingsPage] + let hideName: Bool? + var nameString: LocalizedStringKey { LocalizedStringKey(name.rawValue) } + let icon: IconResource? + + /// A struct for a 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) + } + + /// An enum of all the preferences tabs + enum Name: String { + // MARK: - App Preferences + case general = "General" + case accounts = "Accounts" + case behavior = "Behaviors" + case navigation = "Navigation" + case theme = "Themes" + case textEditing = "Text Editing" + case terminal = "Terminal" + case keybindings = "Key Bindings" + case sourceControl = "Source Control" + case components = "Components" + case location = "Locations" + case advanced = "Advanced" + } + +} diff --git a/CodeEdit/Features/Settings/Models/SettingsSidebarFix.swift b/CodeEdit/Features/Settings/Models/SettingsSidebarFix.swift new file mode 100644 index 0000000000..3a2bdc4462 --- /dev/null +++ b/CodeEdit/Features/Settings/Models/SettingsSidebarFix.swift @@ -0,0 +1,29 @@ +// +// SettingsSidebarFix.swift +// CodeEdit +// +// Created by Wouter Hennen on 13/04/2023. +// + +import Foundation +import AppKit + +extension NSSplitViewItem { + + @objc + fileprivate var canCollapseSwizzled: Bool { + if let check = self.viewController.view.window?.isSettingsWindow, check { + return false + } + return self.canCollapseSwizzled + } + + static func swizzle() { + let origSelector = #selector(getter: NSSplitViewItem.canCollapse) + let swizzledSelector = #selector(getter: canCollapseSwizzled) + let originalMethodSet = class_getInstanceMethod(self as AnyClass, origSelector) + let swizzledMethodSet = class_getInstanceMethod(self as AnyClass, swizzledSelector) + + method_exchangeImplementations(originalMethodSet!, swizzledMethodSet!) + } +} diff --git a/CodeEdit/Features/Settings/Pages/AccountsSettings/AccoundsSettingsProviderRow.swift b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccoundsSettingsProviderRow.swift new file mode 100644 index 0000000000..154661d78d --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccoundsSettingsProviderRow.swift @@ -0,0 +1,47 @@ +// +// AccoundsSettingsAccountRow.swift +// CodeEdit +// +// Created by Austin Condiff on 4/5/23. +// + +import SwiftUI + +struct AccountsSettingsProviderRow: View { + var name: String + var iconName: String + var action: () -> Void + + @State private var hovering = false + @State private var pressing = false + + var body: some View { + HStack { + Image(iconName) + .resizable() + .aspectRatio(contentMode: .fill) + .cornerRadius(6) + .frame(width: 28, height: 28) + Text(name) + Spacer() + if hovering { + Image(systemName: "plus") + .foregroundColor(Color(.tertiaryLabelColor)) + .padding(.horizontal, 5) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(10) + .background(pressing ? Color(nsColor: .quaternaryLabelColor) : Color(nsColor: .clear)) + .overlay(Color(.black).opacity(0.0001)) + .onHover { hover in + hovering = hover + } + .pressAction { + pressing = true + } onRelease: { + pressing = false + action() + } + } +} diff --git a/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountSelectionView.swift b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountSelectionView.swift new file mode 100644 index 0000000000..1b8fd678aa --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountSelectionView.swift @@ -0,0 +1,55 @@ +// +// AccoundSelectionView.swift +// CodeEdit +// +// Created by Austin Condiff on 4/5/23. +// + +import SwiftUI + +struct AccountSelectionView: View { + @Environment(\.dismiss) var dismiss + + @Binding var selectedProvider: SourceControlAccount.Provider? + + var gitProviders = SourceControlAccount.Provider.allCases + + var body: some View { + VStack(spacing: 0) { + Form { + Section { + VStack(alignment: .leading, spacing: 0) { + ForEach(gitProviders, id: \.self) { provider in + AccountsSettingsProviderRow( + name: provider.name, + iconName: provider.iconName, + action: { + selectedProvider = provider + dismiss() + } + ) + Divider() + } + } + .padding(-10) + } footer: { + HStack { + Spacer() + Button { + dismiss() + } label: { + Text("Cancel") + .padding(.horizontal) + } + .buttonStyle(.borderedProminent) + .controlSize(.large) + } + .padding(.top, 10) + } + } + .formStyle(.grouped) + .scrollDisabled(true) + } + .frame(width: 300) + } +} diff --git a/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsDetailsView.swift b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsDetailsView.swift new file mode 100644 index 0000000000..78d6e35c86 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsDetailsView.swift @@ -0,0 +1,73 @@ +// +// AccountsSettingsDetailView.swift +// CodeEdit +// +// Created by Austin Condiff on 4/6/23. +// + +import SwiftUI + +struct AccountsSettingsDetailsView: View { + @AppSettings var settings + + @Binding var account: SourceControlAccount + + @State var cloneUsing: Bool = false + @State var deleteConfirmationIsPresented: Bool = false + + init(_ account: Binding) { + _account = account + } + + var body: some View { + SettingsDetailsView(title: account.description) { + SettingsForm { + Section { + LabeledContent("Account") { + Text(account.name) + } + TextField("Description", text: $account.description) + if account.provider.baseURL == nil { + TextField("Server", text: $account.serverURL) + } + } + + Section { + Picker(selection: $cloneUsing) { + Text("HTTPS") + .tag(false) // temporary + Text("SSH") + .tag(true) // temporary + } label: { + Text("Clone Using") + Text("New repositories will be cloned from \(account.provider.name)" + + " using \(cloneUsing ? "SSH" : "HTTPS").") + } + .pickerStyle(.radioGroup) + + Picker("SSH Key", selection: $settings.accounts.sourceControlAccounts.sshKey) { + Text("None") + Text("Create New...") + Text("Choose...") + } + } footer: { + HStack { + Button("Delete Account...") { + deleteConfirmationIsPresented.toggle() + } + .alert(isPresented: $deleteConfirmationIsPresented) { + Alert( + title: Text("Are you sure you want to delete the account “\(account.description)”?"), + message: Text("Deleting this account will remove it from CodeEdit."), + primaryButton: .default(Text("OK")), + secondaryButton: .default(Text("Cancel")) + ) + } + Spacer() + } + .padding(.top, 10) + } + } + } + } +} diff --git a/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsSigninView.swift b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsSigninView.swift new file mode 100644 index 0000000000..7369b5d341 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsSigninView.swift @@ -0,0 +1,214 @@ +// +// AccountsSettingsSigninView.swift +// CodeEdit +// +// Created by Austin Condiff on 4/5/23. +// + +import SwiftUI + +struct AccountsSettingsSigninView: View { + @Environment(\.dismiss) var dismiss + @Environment(\.openURL) var createToken + + var provider: SourceControlAccount.Provider + @Binding var addAccountSheetPresented: Bool + + init(_ provider: SourceControlAccount.Provider, addAccountSheetPresented: Binding) { + self.provider = provider + self._addAccountSheetPresented = addAccountSheetPresented + } + + @State var server = "" + @State var username = "" + @State var personalAccessToken = "" + + @AppSettings var settings + + private let keychain = CodeEditKeychain() + + var body: some View { + VStack(spacing: 0) { + Form { + Section( + content: { + if provider.baseURL == nil { + VStack(alignment: .leading, spacing: 5) { + Text("Server") + .font(.caption3) + .foregroundColor(.secondary) + TextField("", text: $server, prompt: Text("https://git.example.com")) + .labelsHidden() + } + } + VStack(alignment: .leading, spacing: 5) { + Text("Username") + .font(.caption3) + .foregroundColor(.secondary) + TextField("", text: $username) + .labelsHidden() + } + VStack(alignment: .leading, spacing: 5) { + Text("Personal Access Token") + .font(.caption3) + .foregroundColor(.secondary) + SecureField("", text: $personalAccessToken) + .labelsHidden() + } + }, + header: { + VStack(alignment: .center, spacing: 10) { + Image(provider.iconName) + .resizable() + .aspectRatio(contentMode: .fill) + .cornerRadius(12) + .frame(width: 52, height: 52) + .padding(.top, 5) + Text("Sign in to \(provider.name)") + .multilineTextAlignment(.center) + } + .frame(maxWidth: .infinity) + }, + footer: { + VStack(alignment: .leading, spacing: 5) { + if provider == .github { + Text("\(provider.name) personal access tokens must have these scopes set:") + .font(.system(size: 10.5)) + .foregroundColor(.secondary) + HStack(alignment: .center) { + Spacer() + VStack(alignment: .leading) { + HStack(spacing: 2.5) { + Image(systemName: "checkmark") + .font(.system(size: 10.5, weight: .semibold)) + Text("admin:public _key") + .font(.system(size: 10.5)) + } + HStack(spacing: 2.5) { + Image(systemName: "checkmark") + .font(.system(size: 10.5, weight: .semibold)) + Text("write:discussion") + .font(.system(size: 10.5)) + } + HStack(spacing: 2.5) { + Image(systemName: "checkmark") + .font(.system(size: 10.5, weight: .semibold)) + Text("repo") + .font(.system(size: 10.5)) + } + HStack(spacing: 2.5) { + Image(systemName: "checkmark") + .font(.system(size: 10.5, weight: .semibold)) + Text("user") + .font(.system(size: 10.5)) + } + } + Spacer() + } + .foregroundColor(.secondary) + } + Button { + createToken(provider.authHelpURL) + } label: { + if provider.authType == .password { + Text("Create a Password on \(provider.name)") + .font(.system(size: 10.5)) + } else { + Text("Create a Token on \(provider.name)") + .font(.system(size: 10.5)) + } + } + .buttonStyle(.link) + .frame(maxWidth: .infinity, alignment: .leading) + } + .frame(maxWidth: .infinity) + } + ) + } + .formStyle(.grouped) + .scrollDisabled(true) + HStack { + Button { + addAccountSheetPresented.toggle() + dismiss() + } label: { + Text("Cancel") + .frame(maxWidth: .infinity) + } + .controlSize(.large) + .frame(maxWidth: .infinity) + + Button { + signin() + } label: { + Text("Sign In") + .frame(maxWidth: .infinity) + } + .disabled(username.isEmpty || personalAccessToken.isEmpty) + .buttonStyle(.borderedProminent) + .controlSize(.large) + } + .padding(.horizontal) + .padding(.bottom) + } + .frame(width: 300) + } + + private func signin() { + let configURL = provider.baseURL == nil ? server : nil + + switch provider { + case .github, .githubEnterprise: + let config = GitHubTokenConfiguration(personalAccessToken, url: configURL) + GitHubAccount(config).me { response in + switch response { + case .success: + handleGitRequestSuccess() + case .failure(let error): + print(error) + } + } + case .gitlab, .gitlabSelfHosted: + let config = GitLabTokenConfiguration(personalAccessToken, url: configURL) + GitLabAccount(config).me { response in + switch response { + case .success: + handleGitRequestSuccess() + case .failure(let error): + print(error) + } + } + default: + print("do nothing") + } + } + + private func handleGitRequestSuccess() { + let gitAccounts = settings.accounts.sourceControlAccounts.gitAccounts + let providerLink = provider.baseURL?.absoluteString ?? server + + if gitAccounts.contains( + where: { + $0.serverURL == providerLink && + $0.name.lowercased() == username.lowercased() + } + ) { + print("Account with the same username and provider already exists!") + } else { + settings.accounts.sourceControlAccounts.gitAccounts.append( + SourceControlAccount( + id: "\(server)_\(username.lowercased())", + name: username, + description: provider.name, + provider: provider, + serverURL: server, + urlProtocol: true, + sshKey: "", + isTokenValid: true + ) + ) + keychain.set(personalAccessToken, forKey: "github_\(username)_enterprise") + dismiss() + } + } +} diff --git a/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsView.swift b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsView.swift new file mode 100644 index 0000000000..8bbdfc9583 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/AccountsSettings/AccountsSettingsView.swift @@ -0,0 +1,92 @@ +// +// AccountSettingsView.swift +// CodeEdit +// +// Created by Austin Condiff on 4/4/23. +// + +import SwiftUI + +struct AccountsSettingsView: View { + @AppSettings var settings + + @State private var addAccountSheetPresented: Bool = false + @State private var selectedProvider: SourceControlAccount.Provider? + + var body: some View { + SettingsForm { + Section { + if $settings.accounts.sourceControlAccounts.gitAccounts.isEmpty { + Text("No accounts") + .foregroundColor(.secondary) + .frame(maxWidth: .infinity, alignment: .center) + } else { + ForEach($settings.accounts.sourceControlAccounts.gitAccounts) { $account in + AccountsSettingsAccountLink($account) + } + } + } footer: { + HStack { + Spacer() + Button("Add Account...") { addAccountSheetPresented.toggle() } + .sheet(isPresented: $addAccountSheetPresented, content: { + AccountSelectionView(selectedProvider: $selectedProvider) + }) + .sheet(item: $selectedProvider, content: { provider in + switch provider { + case .github, .githubEnterprise, .gitlab, .gitlabSelfHosted: + AccountsSettingsSigninView(provider, addAccountSheetPresented: $addAccountSheetPresented) + default: + implementationNeeded + } + }) + } + .padding(.top, 10) + } + } + } + + private var implementationNeeded: some View { + VStack(spacing: 20) { + Text("This git client is currently not supported.") + HStack { + Button("Close") { + addAccountSheetPresented.toggle() + selectedProvider = nil + + } + .buttonStyle(.borderedProminent) + } + .frame(maxWidth: .infinity, alignment: .trailing) + } + .padding(20) + } +} + +struct AccountsSettingsAccountLink: View { + @Binding var account: SourceControlAccount + + init(_ account: Binding) { + _account = account + } + + var body: some View { + NavigationLink(destination: AccountsSettingsDetailsView($account)) { + Label { + Text(account.provider.name) + Text(account.name) + .font(.footnote) + .foregroundColor(.secondary) + } icon: { + Image(account.provider.iconName) + .resizable() + .aspectRatio(contentMode: .fill) + .cornerRadius(6) + .frame(width: 26, height: 26) + .padding(.top, 2) + .padding(.bottom, 2) + .padding(.leading, 2) + } + } + } +} diff --git a/CodeEdit/Features/AppPreferences/Model/Accounts/AccountsPreferences.swift b/CodeEdit/Features/Settings/Pages/AccountsSettings/Models/AccountsSettings.swift similarity index 82% rename from CodeEdit/Features/AppPreferences/Model/Accounts/AccountsPreferences.swift rename to CodeEdit/Features/Settings/Pages/AccountsSettings/Models/AccountsSettings.swift index fb3c28c3f7..cd3ec7710d 100644 --- a/CodeEdit/Features/AppPreferences/Model/Accounts/AccountsPreferences.swift +++ b/CodeEdit/Features/Settings/Pages/AccountsSettings/Models/AccountsSettings.swift @@ -1,16 +1,16 @@ // // AccountsPreferences.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Nanashi Li on 2022/04/08. // import Foundation -extension AppPreferences { +extension SettingsData { /// The global settings for text editing - struct AccountsPreferences: Codable { + struct AccountsSettings: Codable { /// An integer indicating how many spaces a `tab` will generate var sourceControlAccounts: GitAccounts = .init() @@ -29,7 +29,7 @@ extension AppPreferences { struct GitAccounts: Codable { /// This id will store the account name as the identifiable - var gitAccount: [SourceControlAccounts] = [] + var gitAccounts: [SourceControlAccount] = [] var sshKey: String = "" /// Default initializer @@ -37,7 +37,7 @@ extension AppPreferences { /// 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.gitAccount = try container.decodeIfPresent([SourceControlAccounts].self, forKey: .gitAccount) ?? [] + self.gitAccounts = try container.decodeIfPresent([SourceControlAccount].self, forKey: .gitAccounts) ?? [] self.sshKey = try container.decodeIfPresent(String.self, forKey: .sshKey) ?? "" } } diff --git a/CodeEdit/Features/Settings/Pages/AccountsSettings/Models/SourceControlAccount.swift b/CodeEdit/Features/Settings/Pages/AccountsSettings/Models/SourceControlAccount.swift new file mode 100644 index 0000000000..646b63222f --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/AccountsSettings/Models/SourceControlAccount.swift @@ -0,0 +1,158 @@ +// +// SourceControlAccount.swift +// CodeEdit +// +// Created by Austin Condiff on 4/6/23. +// + +import SwiftUI + +struct SourceControlAccount: Codable, Identifiable, Hashable { + internal init( + id: String, + name: String, + description: String, + provider: Provider, + serverURL: String, + urlProtocol: Bool, + sshKey: String, + isTokenValid: Bool + ) { + self.id = id + self.name = name + self.description = description + self.provider = provider + self.serverURL = serverURL + self.urlProtocol = urlProtocol + self.sshKey = sshKey + self.isTokenValid = isTokenValid + } + + var id: String + var name: String + var description: String + var provider: Provider + var serverURL: String + // TODO: Should we use an enum instead of a boolean here: + // If true we use the HTTP protocol else if false we use SSH + var urlProtocol: Bool + var sshKey: String + var isTokenValid: Bool + + enum Provider: Codable, CaseIterable, Identifiable { + case bitbucketCloud + case bitbucketServer + case github + case githubEnterprise + case gitlab + case gitlabSelfHosted + + var id: String { + switch self { + case .bitbucketCloud: + return "bitbucketCloud" + case .bitbucketServer: + return "bitbucketServer" + case .github: + return "github" + case .githubEnterprise: + return "githubEnterprise" + case .gitlab: + return "gitlab" + case .gitlabSelfHosted: + return "gitlabSelfHosted" + } + } + + var name: String { + switch self { + case .bitbucketCloud: + return "BitBucket Cloud" + case .bitbucketServer: + return "BitBucket Server" + case .github: + return "GitHub" + case .githubEnterprise: + return "GitHub Enterprise" + case .gitlab: + return "GitLab" + case .gitlabSelfHosted: + return "GitLab Self-hosted" + } + } + + var baseURL: URL? { + switch self { + case .bitbucketCloud: + return URL(string: "https://www.bitbucket.com/")! + case .bitbucketServer: + return nil + case .github: + return URL(string: "https://www.github.com/")! + case .githubEnterprise: + return nil + case .gitlab: + return URL(string: "https://www.gitlab.com/")! + case .gitlabSelfHosted: + return nil + } + } + + var iconName: String { + switch self { + case .bitbucketCloud: + return "BitBucketIcon" + case .bitbucketServer: + return "BitBucketIcon" + case .github: + return "GitHubIcon" + case .githubEnterprise: + return "GitHubIcon" + case .gitlab: + return "GitLabIcon" + case .gitlabSelfHosted: + return "GitLabIcon" + } + } + + var authHelpURL: URL { + switch self { + case .bitbucketCloud: + return URL(string: "https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/")! + case .bitbucketServer: + return URL(string: + "https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html")! + case .github: + return URL(string: "https://github.com/settings/tokens/new")! + case .githubEnterprise: + return URL(string: "https://github.com/settings/tokens/new")! + case .gitlab: + return URL(string: "https://gitlab.com/-/profile/personal_access_tokens")! + case .gitlabSelfHosted: + return URL(string: "https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html")! + } + } + + var authType: AuthType { + switch self { + case .bitbucketCloud: + return .password + case .bitbucketServer: + return .token + case .github: + return .token + case .githubEnterprise: + return .token + case .gitlab: + return .token + case .gitlabSelfHosted: + return .token + } + } + } + + enum AuthType { + case token + case password + } +} diff --git a/CodeEdit/Features/Settings/Pages/GeneralSettings/GeneralSettingsView.swift b/CodeEdit/Features/Settings/Pages/GeneralSettings/GeneralSettingsView.swift new file mode 100644 index 0000000000..4c0fd112c2 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/GeneralSettings/GeneralSettingsView.swift @@ -0,0 +1,379 @@ +// +// GeneralSettingsView.swift +// CodeEdit +// +// Created by Austin Condiff on 4/1/23. +// + +import SwiftUI + +/// A view that implements the `General` settings page +struct GeneralSettingsView: View { + private let inputWidth: Double = 160 + private let textEditorWidth: Double = 220 + private let textEditorHeight: Double = 30 + + @EnvironmentObject var updater: SoftwareUpdater + @FocusState private var focusedField: UUID? + + @AppSettings var settings + + @State private var openInCodeEdit: Bool = true + + init() { + guard let defaults = UserDefaults.init( + suiteName: "austincondiff.CodeEdit.shared" + ) else { + print("Failed to get/init shared defaults") + return + } + + self.openInCodeEdit = defaults.bool(forKey: "enableOpenInCE") + } + + var body: some View { + SettingsForm { + Section { + appearance + fileIconStyle + tabBarStyle + } + Section { + showIssues + showLiveIssues + } + Section { + autoSave + revealFileOnFocusChangeToggle + reopenBehavior + afterWindowsCloseBehaviour + fileExtensions + } + Section { + projectNavigatorSize + findNavigatorDetail + issueNavigatorDetail + } + Section { + openInCodeEditToggle + shellCommand + dialogWarnings + + } + Section { + updateChecker + autoUpdateToggle + prereleaseToggle + } + } + } +} + +/// The extension of the view with all the preferences +private extension GeneralSettingsView { + var appearance: some View { + Picker("Appearance", selection: $settings.general.appAppearance) { + Text("System") + .tag(SettingsData.Appearances.system) + Divider() + Text("Light") + .tag(SettingsData.Appearances.light) + Text("Dark") + .tag(SettingsData.Appearances.dark) + } + .onChange(of: settings.general.appAppearance) { tag in + tag.applyAppearance() + } + } + + // TODO: Implement reflecting Show Issues preference and remove disabled modifier + var showIssues: some View { + Picker("Show Issues", selection: $settings.general.showIssues) { + Text("Show Inline") + .tag(SettingsData.Issues.inline) + Text("Show Minimized") + .tag(SettingsData.Issues.minimized) + } + } + + var showLiveIssues: some View { + Toggle("Show Live Issues", isOn: $settings.general.showLiveIssues) + } + + var fileExtensions: some View { + Group { + Picker("File Extensions", selection: $settings.general.fileExtensionsVisibility) { + Text("Hide all") + .tag(SettingsData.FileExtensionsVisibility.hideAll) + Text("Show all") + .tag(SettingsData.FileExtensionsVisibility.showAll) + Divider() + Text("Show only") + .tag(SettingsData.FileExtensionsVisibility.showOnly) + Text("Hide only") + .tag(SettingsData.FileExtensionsVisibility.hideOnly) + } + if case .showOnly = settings.general.fileExtensionsVisibility { + TextField("", text: $settings.general.shownFileExtensions.string, axis: .vertical) + .labelsHidden() + .lineLimit(1...3) + } + if case .hideOnly = settings.general.fileExtensionsVisibility { + TextField("", text: $settings.general.hiddenFileExtensions.string, axis: .vertical) + .labelsHidden() + .lineLimit(1...3) + } + } + } + + var fileIconStyle: some View { + Picker("File Icon Style", selection: $settings.general.fileIconStyle) { + Text("Color") + .tag(SettingsData.FileIconStyle.color) + Text("Monochrome") + .tag(SettingsData.FileIconStyle.monochrome) + } + .pickerStyle(.radioGroup) + } + + var tabBarStyle: some View { + Picker("Tab Bar Style", selection: $settings.general.tabBarStyle) { + Text("Xcode") + .tag(SettingsData.TabBarStyle.xcode) + Text("Native") + .tag(SettingsData.TabBarStyle.native) + } + .pickerStyle(.radioGroup) + } + + var reopenBehavior: some View { + Picker("Reopen Behavior", selection: $settings.general.reopenBehavior) { + Text("Welcome Screen") + .tag(SettingsData.ReopenBehavior.welcome) + Divider() + Text("Open Panel") + .tag(SettingsData.ReopenBehavior.openPanel) + Text("New Document") + .tag(SettingsData.ReopenBehavior.newDocument) + } + } + + var afterWindowsCloseBehaviour: some View { + Picker( + "After the last window is closed", + selection: $settings.general.reopenWindowAfterClose + ) { + Text("Do nothing") + .tag(SettingsData.ReopenWindowBehavior.doNothing) + Divider() + Text("Show Welcome Window") + .tag(SettingsData.ReopenWindowBehavior.showWelcomeWindow) + Text("Quit") + .tag(SettingsData.ReopenWindowBehavior.quit) + } + } + + var projectNavigatorSize: some View { + Picker("Project Navigator Size", selection: $settings.general.projectNavigatorSize) { + Text("Small") + .tag(SettingsData.ProjectNavigatorSize.small) + Text("Medium") + .tag(SettingsData.ProjectNavigatorSize.medium) + Text("Large") + .tag(SettingsData.ProjectNavigatorSize.large) + } + } + + var findNavigatorDetail: some View { + Picker("Find Navigator Detail", selection: $settings.general.findNavigatorDetail) { + ForEach(SettingsData.NavigatorDetail.allCases, id: \.self) { tag in + Text(tag.label).tag(tag) + } + } + } + + // TODO: Implement reflecting Issue Navigator Detail preference and remove disabled modifier + var issueNavigatorDetail: some View { + Picker("Issue Navigator Detail", selection: $settings.general.issueNavigatorDetail) { + ForEach(SettingsData.NavigatorDetail.allCases, id: \.self) { tag in + Text(tag.label).tag(tag) + } + } + .disabled(true) + } + + // TODO: Implement reset for Don't Ask Me warnings Button and remove disabled modifier + var dialogWarnings: some View { + LabeledContent("Dialog Warnings") { + Button(action: { + }, label: { + Text("Reset \"Don't Ask Me\" Warnings") + }) + .buttonStyle(.bordered) + } + .disabled(true) + } + + var shellCommand: some View { + LabeledContent("'codeedit' Shell Command") { + Button(action: installShellCommand, label: { + Text("Install") + }) + .disabled(true) + .buttonStyle(.bordered) + } + } + + func installShellCommand() { + do { + let url = Bundle.main.url(forResource: "codeedit", withExtension: nil, subdirectory: "Resources") + let destination = "/usr/local/bin/codeedit" + + if FileManager.default.fileExists(atPath: destination) { + try FileManager.default.removeItem(atPath: destination) + } + + guard let shellUrl = url?.path else { + print("Failed to get URL to shell command") + return + } + + NSWorkspace.shared.requestAuthorization(to: .createSymbolicLink) { auth, error in + guard let auth, error == nil else { + fallbackShellInstallation(commandPath: shellUrl, destinationPath: destination) + return + } + + do { + try FileManager(authorization: auth).createSymbolicLink( + atPath: destination, withDestinationPath: shellUrl + ) + } catch { + fallbackShellInstallation(commandPath: shellUrl, destinationPath: destination) + } + } + } catch { + print(error) + } + } + + var updateChecker: some View { + Section { + LabeledContent { + Button("Check Now") { + updater.checkForUpdates() + } + } label: { + Text("Check for updates") + Text("Last checked: \(lastUpdatedString)") + + } + } + } + + var autoUpdateToggle: some View { + Toggle("Automatically check for app updates", isOn: $updater.automaticallyChecksForUpdates) + } + + var prereleaseToggle: some View { + Toggle("Include pre-release versions", isOn: $updater.includePrereleaseVersions) + } + + var autoSave: some View { + Toggle( + "Automatically save changes to disk", + isOn: $settings.general.isAutoSaveOn + ) + } + + // MARK: - Preference Views + + private var lastUpdatedString: String { + if let lastUpdatedDate = updater.lastUpdateCheckDate { + return Self.formatter.string(from: lastUpdatedDate) + } else { + return "Never" + } + } + + private static func configure(_ subject: Subject, configuration: (inout Subject) -> Void) -> Subject { + var copy = subject + configuration(©) + return copy + } + + func fallbackShellInstallation(commandPath: String, destinationPath: String) { + let cmd = [ + "osascript", + "-e", + "\"do shell script \\\"mkdir -p /usr/local/bin && ln -sf \'\(commandPath)\' \'\(destinationPath)\'\\\"\"", + "with administrator privileges" + ] + + let cmdStr = cmd.joined(separator: " ") + + let task = Process() + let pipe = Pipe() + + task.standardOutput = pipe + task.standardError = pipe + task.arguments = ["-c", cmdStr] + task.executableURL = URL(fileURLWithPath: "/bin/zsh") + task.standardInput = nil + + do { + try task.run() + } catch { + print(error) + } + } + + var openInCodeEditToggle: some View { + Toggle("Show “Open With CodeEdit” option in Finder", isOn: $openInCodeEdit) + .onChange(of: openInCodeEdit) { newValue in + guard let defaults = UserDefaults.init( + suiteName: "austincondiff.CodeEdit.shared" + ) else { + print("Failed to get/init shared defaults") + return + } + + defaults.set(newValue, forKey: "enableOpenInCE") + } + } + + var revealFileOnFocusChangeToggle: some View { + Toggle("Automatically reveal in project navigator", isOn: $settings.general.revealFileOnFocusChange) + } + + private static let formatter = configure(DateFormatter()) { + $0.dateStyle = .medium + $0.timeStyle = .medium + } +} + +extension View { + func actionBar(@ViewBuilder content: () -> Content) -> some View { + self + .padding(.bottom, 24) + .overlay(alignment: .bottom) { + VStack(spacing: -1) { + Divider() + HStack(spacing: 0) { + content() + .buttonStyle( + IconButtonStyle( + font: Font.system(size: 11, weight: .medium), + size: 24 + ) + ) + } + .frame(height: 16) + .padding(.vertical, 4) + .frame(maxWidth: .infinity, alignment: .leading) + } + .frame(height: 24) + .background(.separator) + } + } +} diff --git a/CodeEdit/Features/AppPreferences/Model/General/GeneralPreferences.swift b/CodeEdit/Features/Settings/Pages/GeneralSettings/Models/GeneralSettings.swift similarity index 98% rename from CodeEdit/Features/AppPreferences/Model/General/GeneralPreferences.swift rename to CodeEdit/Features/Settings/Pages/GeneralSettings/Models/GeneralSettings.swift index d01bd8298c..3087154def 100644 --- a/CodeEdit/Features/AppPreferences/Model/General/GeneralPreferences.swift +++ b/CodeEdit/Features/Settings/Pages/GeneralSettings/Models/GeneralSettings.swift @@ -1,16 +1,16 @@ // // GeneralPreferences.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Nanashi Li on 2022/04/08. // import SwiftUI -extension AppPreferences { +extension SettingsData { /// The general global setting - struct GeneralPreferences: Codable { + struct GeneralSettings: Codable { /// The appearance of the app var appAppearance: Appearances = .system @@ -231,7 +231,7 @@ extension AppPreferences { case medium case large - /// Returns the row height depending on the `projectNavigatorSize` in `AppPreferences`. + /// Returns the row height depending on the `projectNavigatorSize` in `Settings`. /// /// * `small`: 20 /// * `medium`: 22 diff --git a/CodeEdit/Features/AppPreferences/Model/Keybindings/KeybindingsPreferences.swift b/CodeEdit/Features/Settings/Pages/Keybindings/Models/KeybindingsSettings.swift similarity index 94% rename from CodeEdit/Features/AppPreferences/Model/Keybindings/KeybindingsPreferences.swift rename to CodeEdit/Features/Settings/Pages/Keybindings/Models/KeybindingsSettings.swift index 5c37dea900..9fed884a41 100644 --- a/CodeEdit/Features/AppPreferences/Model/Keybindings/KeybindingsPreferences.swift +++ b/CodeEdit/Features/Settings/Pages/Keybindings/Models/KeybindingsSettings.swift @@ -1,16 +1,16 @@ // // KeybindingsPreferences.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Alex on 18.05.2022. // import Foundation -extension AppPreferences { +extension SettingsData { /// The global settings for text editing - struct KeybindingsPreferences: Codable { + struct KeybindingsSettings: Codable { /// An integer indicating how many spaces a `tab` will generate var keybindings: [String: KeyboardShortcutWrapper] = .init() diff --git a/CodeEdit/Features/Settings/Pages/LocationsSettings/LocationsSettingsView.swift b/CodeEdit/Features/Settings/Pages/LocationsSettings/LocationsSettingsView.swift new file mode 100644 index 0000000000..f995c94027 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/LocationsSettings/LocationsSettingsView.swift @@ -0,0 +1,61 @@ +// +// LocationSettingsView.swift +// CodeEdit +// +// Created by Raymond Vleeshouwer on 02/04/23. +// + +import SwiftUI + +/// A view that implements the `Locations` preference section +struct LocationsSettingsView: View { + var body: some View { + SettingsForm { + Section { + applicationSupportLocation + settingsLocation + themesLocation + extensionsLocation + } + } + } +} + +private extension LocationsSettingsView { + @ViewBuilder + private var applicationSupportLocation: some View { + ExternalLink(destination: Settings.shared.baseURL) { + Text("Application Support") + Text(Settings.shared.baseURL.path) + .font(.footnote) + .foregroundColor(.secondary) + } + } + + private var settingsLocation: some View { + ExternalLink(destination: ThemeModel.shared.settingsURL) { + Text("Settings") + Text(ThemeModel.shared.settingsURL.path) + .font(.footnote) + .foregroundColor(.secondary) + } + } + + private var themesLocation: some View { + ExternalLink(destination: ThemeModel.shared.themesURL) { + Text("Themes") + Text(ThemeModel.shared.themesURL.path) + .font(.footnote) + .foregroundColor(.secondary) + } + } + + private var extensionsLocation: some View { + ExternalLink(destination: ThemeModel.shared.extensionsURL) { + Text("Extensions") + Text(ThemeModel.shared.extensionsURL.path) + .font(.footnote) + .foregroundColor(.secondary) + } + } +} diff --git a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/Model/IgnoredFiles.swift b/CodeEdit/Features/Settings/Pages/SourceControlSettings/Models/IgnoredFiles.swift similarity index 84% rename from CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/Model/IgnoredFiles.swift rename to CodeEdit/Features/Settings/Pages/SourceControlSettings/Models/IgnoredFiles.swift index 07981360c2..1895b91d15 100644 --- a/CodeEdit/Features/AppPreferences/Sections/SourceControlPreferences/Model/IgnoredFiles.swift +++ b/CodeEdit/Features/Settings/Pages/SourceControlSettings/Models/IgnoredFiles.swift @@ -1,6 +1,6 @@ // // IgnoredFiles.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Nanashi Li on 2022/04/08. // diff --git a/CodeEdit/Features/AppPreferences/Model/Source Control/SourceControlPreferences.swift b/CodeEdit/Features/Settings/Pages/SourceControlSettings/Models/SourceControlSettings.swift similarity index 98% rename from CodeEdit/Features/AppPreferences/Model/Source Control/SourceControlPreferences.swift rename to CodeEdit/Features/Settings/Pages/SourceControlSettings/Models/SourceControlSettings.swift index 13367f3cde..6616b5528d 100644 --- a/CodeEdit/Features/AppPreferences/Model/Source Control/SourceControlPreferences.swift +++ b/CodeEdit/Features/Settings/Pages/SourceControlSettings/Models/SourceControlSettings.swift @@ -1,15 +1,15 @@ // // SourceControlPreferences.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Nanashi Li on 2022/04/08. // import Foundation -extension AppPreferences { +extension SettingsData { /// The global settings for source control - struct SourceControlPreferences: Codable { + struct SourceControlSettings: Codable { /// The general source control settings var general: SourceControlGeneral = .init() /// The source control git settings diff --git a/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlGeneralView.swift b/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlGeneralView.swift new file mode 100644 index 0000000000..3f453e6a02 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlGeneralView.swift @@ -0,0 +1,118 @@ +// +// SourceControlGeneralView.swift +// CodeEdit +// +// Created by Raymond Vleeshouwer on 02/04/23. +// + +import SwiftUI + +struct SourceControlGeneralView: View { + @AppSettings var settings + + @State + private var text: String = "main" + + var body: some View { + SettingsForm { + Section { + enableSourceControl + refreshLocalStatusAuto + fetchRefreshStatusAuto + addRemoveFilesAuto + selectFilesToCommitAuto + } + Section { + showSourceControlChanges + includeUpstreamChanges + } + Section { + comparisonView + sourceControlNavigator + defaultBranchName + } + } + } +} + +private extension SourceControlGeneralView { + private var enableSourceControl: some View { + Toggle( + "Enable source control", + isOn: $settings.sourceControl.general.enableSourceControl + ) + } + + private var refreshLocalStatusAuto: some View { + Toggle( + "Refresh local status automatically", + isOn: $settings.sourceControl.general.refreshStatusLocally + ) + } + + private var fetchRefreshStatusAuto: some View { + Toggle( + "Fetch and refresh server status automatically", + isOn: $settings.sourceControl.general.fetchRefreshServerStatus + ) + } + + private var addRemoveFilesAuto: some View { + Toggle( + "Add and remove files automatically", + isOn: $settings.sourceControl.general.addRemoveAutomatically + ) + } + + private var selectFilesToCommitAuto: some View { + Toggle( + "Select files to commit automatically", + isOn: $settings.sourceControl.general.selectFilesToCommit + ) + } + + private var showSourceControlChanges: some View { + Toggle( + "Show source control changes", + isOn: $settings.sourceControl.general.showSourceControlChanges + ) + } + + private var includeUpstreamChanges: some View { + Toggle( + "Include upstream changes", + isOn: $settings.sourceControl.general.includeUpstreamChanges + ) + } + + private var comparisonView: some View { + Picker( + "Comparison view", + selection: $settings.sourceControl.general.revisionComparisonLayout + ) { + Text("Local Revision on Left Side") + .tag(SettingsData.RevisionComparisonLayout.localLeft) + Text("Local Revision on Right Side") + .tag(SettingsData.RevisionComparisonLayout.localRight) + } + } + + private var sourceControlNavigator: some View { + Picker( + "Source control navigator", + selection: $settings.sourceControl.general.controlNavigatorOrder + ) { + Text("Sort by Name") + .tag(SettingsData.ControlNavigatorOrder.sortByName) + Text("Sort by Date") + .tag(SettingsData.ControlNavigatorOrder.sortByDate) + } + } + + private var defaultBranchName: some View { + TextField(text: $text) { + Text("Default branch name") + Text("Cannot contain spaces, backslashes, or other symbols") + } + } +} diff --git a/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlGitView.swift b/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlGitView.swift new file mode 100644 index 0000000000..84d795da03 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlGitView.swift @@ -0,0 +1,68 @@ +// +// SourceControlGitView.swift +// CodeEdit +// +// Created by Raymond Vleeshouwer on 02/04/23. +// + +import SwiftUI + +struct SourceControlGitView: View { + @AppSettings var settings + + @State + var ignoredFileSelection: IgnoredFiles.ID? + + var body: some View { + SettingsForm { + Section { + gitAuthorName + gitEmail + } + Section { + preferToRebaseWhenPulling + showMergeCommitsInPerFileLog + } + } + } +} + +private extension SourceControlGitView { + private var gitAuthorName: some View { + TextField("Author Name", text: $settings.sourceControl.git.authorName) + } + + private var gitEmail: some View { + TextField("Author Email", text: $settings.sourceControl.git.authorEmail) + } + + private var preferToRebaseWhenPulling: some View { + Toggle( + "Prefer to rebase when pulling", + isOn: $settings.sourceControl.git.preferRebaseWhenPulling + ) + } + + private var showMergeCommitsInPerFileLog: some View { + Toggle( + "Show merge commits in per-file log", + isOn: $settings.sourceControl.git.showMergeCommitsPerFileLog + ) + } + + private var bottomToolbar: some View { + HStack(spacing: 12) { + Button {} label: { + Image(systemName: "plus") + .foregroundColor(Color.secondary) + } + .buttonStyle(.plain) + Button {} label: { + Image(systemName: "minus") + } + .disabled(true) + .buttonStyle(.plain) + Spacer() + } + } +} diff --git a/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlSettingsView.swift b/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlSettingsView.swift new file mode 100644 index 0000000000..120ead4a19 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/SourceControlSettings/SourceControlSettingsView.swift @@ -0,0 +1,36 @@ +// +// SourceControlSettingsView.swift +// CodeEdit +// +// Created by Raymond Vleeshouwer on 02/04/23. +// + +import SwiftUI + +struct SourceControlSettingsView: View { + @State var selectedTab: String = "general" + + var body: some View { + Group { + switch selectedTab { + case "general": + SourceControlGeneralView() + case "git": + SourceControlGitView() + default: + SourceControlGeneralView() + } + } + .safeAreaInset(edge: .top, spacing: 0) { + Picker("", selection: $selectedTab) { + Text("General").tag("general") + Text("Git").tag("git") + } + .pickerStyle(.segmented) + .labelsHidden() + .padding(.horizontal, 20) + .padding(.bottom, 20) + + } + } +} diff --git a/CodeEdit/Features/AppPreferences/Model/Terminal/TerminalPreferences.swift b/CodeEdit/Features/Settings/Pages/TerminalSettings/Models/TerminalSettings.swift similarity index 83% rename from CodeEdit/Features/AppPreferences/Model/Terminal/TerminalPreferences.swift rename to CodeEdit/Features/Settings/Pages/TerminalSettings/Models/TerminalSettings.swift index d8842c47c1..ac1bcf84ee 100644 --- a/CodeEdit/Features/AppPreferences/Model/Terminal/TerminalPreferences.swift +++ b/CodeEdit/Features/Settings/Pages/TerminalSettings/Models/TerminalSettings.swift @@ -1,16 +1,19 @@ // // TerminalPreferences.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Nanashi Li on 2022/04/08. // import Foundation -extension AppPreferences { +extension SettingsData { /// The global settings for the terminal emulator - struct TerminalPreferences: Codable { + struct TerminalSettings: Codable { + + /// If true terminal will use editor theme. + var useEditorTheme: Bool = true /// If true terminal appearance will always be `dark`. Otherwise it adapts to the system setting. var darkAppearance: Bool = false @@ -33,6 +36,9 @@ extension AppPreferences { // Toggle for blinking cursor or not var cursorBlink: Bool = false + // Use font settings from Text Editing + var useTextEditorFont: Bool = true + /// Default initializer init() {} @@ -48,6 +54,7 @@ extension AppPreferences { forKey: .cursorStyle ) ?? .block self.cursorBlink = try container.decodeIfPresent(Bool.self, forKey: .cursorBlink) ?? false + self.useTextEditorFont = try container.decodeIfPresent(Bool.self, forKey: .useTextEditorFont) ?? true } } @@ -72,10 +79,10 @@ extension AppPreferences { var customFont: Bool = false /// The font size for the custom font - var size: Int = 12 + var size: Double = 12 /// The name of the custom font - var name: String = "SFMono-Medium" + var name: String = "SF Mono" /// Default initializer init() {} @@ -84,8 +91,8 @@ extension AppPreferences { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.customFont = try container.decodeIfPresent(Bool.self, forKey: .customFont) ?? false - self.size = try container.decodeIfPresent(Int.self, forKey: .size) ?? 11 - self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "SFMono-Medium" + self.size = try container.decodeIfPresent(Double.self, forKey: .size) ?? 11 + self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "SF Mono" } } } diff --git a/CodeEdit/Features/Settings/Pages/TerminalSettings/TerminalSettingsView.swift b/CodeEdit/Features/Settings/Pages/TerminalSettings/TerminalSettingsView.swift new file mode 100644 index 0000000000..93771d3508 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/TerminalSettings/TerminalSettingsView.swift @@ -0,0 +1,88 @@ +// +// TerminalSettingsView.swift +// CodeEdit +// +// Created by Raymond Vleeshouwer on 02/04/23. +// + +import SwiftUI + +struct TerminalSettingsView: View { + @AppSettings var settings + + var body: some View { + SettingsForm { + Section { + shellSelector + optionAsMetaToggle + } + Section { + useTextEditorFontToggle + if !settings.terminal.useTextEditorFont { + fontSelector + fontSizeSelector + } + } + Section { + cursorStyle + cursorBlink + } + } + } +} + +private extension TerminalSettingsView { + @ViewBuilder + private var shellSelector: some View { + Picker("Shell", selection: $settings.terminal.shell) { + Text("System Default") + .tag(SettingsData.TerminalShell.system) + Divider() + Text("ZSH") + .tag(SettingsData.TerminalShell.zsh) + Text("Bash") + .tag(SettingsData.TerminalShell.bash) + } + } + + private var cursorStyle: some View { + Picker("Terminal Cursor Style", selection: $settings.terminal.cursorStyle) { + Text("Block") + .tag(SettingsData.TerminalCursorStyle.block) + Text("Underline") + .tag(SettingsData.TerminalCursorStyle.underline) + Text("Bar") + .tag(SettingsData.TerminalCursorStyle.bar) + } + } + + private var cursorBlink: some View { + Toggle("Blink Cursor", isOn: $settings.terminal.cursorBlink) + } + + private var optionAsMetaToggle: some View { + Toggle("Use \"Option\" key as \"Meta\"", isOn: $settings.terminal.optionAsMeta) + } + + private var useTextEditorFontToggle: some View { + Toggle("Use text editor font", isOn: $settings.terminal.useTextEditorFont) + } + + @ViewBuilder + private var fontSelector: some View { + MonospacedFontPicker(title: "Font", selectedFontName: $settings.terminal.font.name) + .onChange(of: settings.terminal.font.name) { fontName in + settings.terminal.font.customFont = fontName != "SF Mono" + } + } + + private var fontSizeSelector: some View { + Stepper( + "Font Size", + value: $settings.terminal.font.size, + in: 1...288, + step: 1, + format: .number + ) + } +} diff --git a/CodeEdit/Features/AppPreferences/Model/Text Editing/TextEditingPreferences.swift b/CodeEdit/Features/Settings/Pages/TextEditingSettings/Models/TextEditingSettings.swift similarity index 84% rename from CodeEdit/Features/AppPreferences/Model/Text Editing/TextEditingPreferences.swift rename to CodeEdit/Features/Settings/Pages/TextEditingSettings/Models/TextEditingSettings.swift index 40fc38c34a..3a27717285 100644 --- a/CodeEdit/Features/AppPreferences/Model/Text Editing/TextEditingPreferences.swift +++ b/CodeEdit/Features/Settings/Pages/TextEditingSettings/Models/TextEditingSettings.swift @@ -1,6 +1,6 @@ // // TextEditingPreferences.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Nanashi Li on 2022/04/08. // @@ -8,10 +8,10 @@ import AppKit import Foundation -extension AppPreferences { +extension SettingsData { /// The global settings for text editing - struct TextEditingPreferences: Codable { + struct TextEditingSettings: Codable { /// An integer indicating how many spaces a `tab` will generate var defaultTabWidth: Int = 4 @@ -69,7 +69,7 @@ extension AppPreferences { title: "Toggle Type-Over Completion", id: "prefs.text_editing.type_over_completion", command: CommandClosureWrapper { - AppPreferencesModel.shared.preferences.textEditing.enableTypeOverCompletion.toggle() + Settings.shared.preferences.textEditing.enableTypeOverCompletion.toggle() } ) @@ -78,7 +78,7 @@ extension AppPreferences { title: "Toggle Autocomplete Braces", id: "prefs.text_editing.autocomplete_braces", command: CommandClosureWrapper { - AppPreferencesModel.shared.preferences.textEditing.autocompleteBraces.toggle() + Settings.shared.preferences.textEditing.autocompleteBraces.toggle() } ) @@ -87,7 +87,7 @@ extension AppPreferences { title: "Toggle Word Wrap", id: "prefs.text_editing.wrap_lines_to_editor_width", command: CommandClosureWrapper { - AppPreferencesModel.shared.preferences.textEditing.wrapLinesToEditorWidth.toggle() + Settings[\.textEditing].wrapLinesToEditorWidth.toggle() } ) } @@ -98,10 +98,10 @@ extension AppPreferences { var customFont: Bool = false /// The font size for the font - var size: Int = 12 + var size: Double = 12 /// The name of the custom font - var name: String = "SFMono-Medium" + var name: String = "SF Mono" /// Default initializer init() {} @@ -110,8 +110,8 @@ extension AppPreferences { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.customFont = try container.decodeIfPresent(Bool.self, forKey: .customFont) ?? false - self.size = try container.decodeIfPresent(Int.self, forKey: .size) ?? 11 - self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "SFMono-Medium" + self.size = try container.decodeIfPresent(Double.self, forKey: .size) ?? 11 + self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "SF Mono" } /// Returns an NSFont representation of the current configuration. @@ -120,8 +120,8 @@ extension AppPreferences { /// Otherwise returns a default system font monospaced. func current() -> NSFont { guard customFont, - let customFont = NSFont(name: name, size: Double(size)) else { - return NSFont.monospacedSystemFont(ofSize: Double(size), weight: .regular) + let customFont = NSFont(name: name, size: size) else { + return NSFont.monospacedSystemFont(ofSize: size, weight: .medium) } return customFont } diff --git a/CodeEdit/Features/Settings/Pages/TextEditingSettings/TextEditingSettingsView.swift b/CodeEdit/Features/Settings/Pages/TextEditingSettings/TextEditingSettingsView.swift new file mode 100644 index 0000000000..e26ede5a86 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/TextEditingSettings/TextEditingSettingsView.swift @@ -0,0 +1,93 @@ +// +// TextEditingSettingsView.swift +// CodeEdit +// +// Created by Austin Condiff on 4/2/23. +// + +import SwiftUI + +/// A view that implements the `Text Editing` settings page +struct TextEditingSettingsView: View { + @AppSettings var settings + + var body: some View { + SettingsForm { + Section { + defaultTabWidth + wrapLinesToEditorWidth + } + Section { + fontSelector + fontSizeSelector + lineHeight + } + Section { + autocompleteBraces + enableTypeOverCompletion + } + } + } +} + +private extension TextEditingSettingsView { + @ViewBuilder + private var fontSelector: some View { + MonospacedFontPicker(title: "Font", selectedFontName: $settings.textEditing.font.name) + .onChange(of: settings.textEditing.font.name) { fontName in + settings.textEditing.font.customFont = fontName != "SF Mono" + } + } + + private var fontSizeSelector: some View { + Stepper( + "Font Size", + value: $settings.textEditing.font.size, + in: 1...288, + step: 1, + format: .number + ) + } + + private var autocompleteBraces: some View { + Toggle(isOn: $settings.textEditing.autocompleteBraces) { + Text("Autocomplete braces") + Text("Automatically insert closing braces (\"}\")") + } + } + + private var enableTypeOverCompletion: some View { + Toggle("Enable type-over completion", isOn: $settings.textEditing.enableTypeOverCompletion) + } + + private var wrapLinesToEditorWidth: some View { + Toggle("Wrap lines to editor width", isOn: $settings.textEditing.wrapLinesToEditorWidth) + } + + private var lineHeight: some View { + Stepper( + "Line Height", + value: $settings.textEditing.lineHeightMultiple, + in: 0.75...2.0, + step: 0.05, + format: .number + ) + } + + private var defaultTabWidth: some View { + HStack(alignment: .top) { + Stepper( + "Default Tab Width", + value: Binding( + get: { Double(settings.textEditing.defaultTabWidth) }, + set: { settings.textEditing.defaultTabWidth = Int($0) } + ), + in: 1...8, + step: 1, + format: .number + ) + Text("spaces") + .foregroundColor(.secondary) + } + } +} diff --git a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/Model/Theme.swift b/CodeEdit/Features/Settings/Pages/ThemeSettings/Models/Theme.swift similarity index 99% rename from CodeEdit/Features/AppPreferences/Sections/ThemePreferences/Model/Theme.swift rename to CodeEdit/Features/Settings/Pages/ThemeSettings/Models/Theme.swift index 858e7dc588..1e83f9e5b7 100644 --- a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/Model/Theme.swift +++ b/CodeEdit/Features/Settings/Pages/ThemeSettings/Models/Theme.swift @@ -1,6 +1,6 @@ // // Theme.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Lukas Pistrol on 31.03.22. // @@ -50,7 +50,7 @@ struct Theme: Identifiable, Codable, Equatable, Hashable, Loopable { /// The ``ThemeType`` of the theme /// - /// Appears as `"type"` in the `preferences.json` + /// Appears as `"type"` in the `settings.json` var appearance: ThemeType /// Editor colors of the theme diff --git a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/Model/ThemeModel.swift b/CodeEdit/Features/Settings/Pages/ThemeSettings/Models/ThemeModel.swift similarity index 90% rename from CodeEdit/Features/AppPreferences/Sections/ThemePreferences/Model/ThemeModel.swift rename to CodeEdit/Features/Settings/Pages/ThemeSettings/Models/ThemeModel.swift index 9e5ec1ea8c..ba6b695989 100644 --- a/CodeEdit/Features/AppPreferences/Sections/ThemePreferences/Model/ThemeModel.swift +++ b/CodeEdit/Features/Settings/Pages/ThemeSettings/Models/ThemeModel.swift @@ -1,6 +1,6 @@ // // ThemeModel.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Lukas Pistrol on 31.03.22. // @@ -30,13 +30,23 @@ final class ThemeModel: ObservableObject { baseURL.appendingPathComponent("themes", isDirectory: true) } + /// The URL of the `Extensions` folder + internal var extensionsURL: URL { + baseURL.appendingPathComponent("Extensions", isDirectory: true) + } + + /// The URL of the `settings.json` file + internal var settingsURL: URL { + baseURL.appendingPathComponent("settings.json", isDirectory: true) + } + /// Selected 'light' theme /// Used for auto-switching theme to match macOS system appearance @Published var selectedLightTheme: Theme? { didSet { DispatchQueue.main.async { - AppPreferencesModel.shared + Settings.shared .preferences.theme.selectedLightTheme = self.selectedLightTheme?.name ?? "Broken" } } @@ -48,7 +58,7 @@ final class ThemeModel: ObservableObject { var selectedDarkTheme: Theme? { didSet { DispatchQueue.main.async { - AppPreferencesModel.shared + Settings.shared .preferences.theme.selectedDarkTheme = self.selectedDarkTheme?.name ?? "Broken" } } @@ -80,7 +90,7 @@ final class ThemeModel: ObservableObject { var selectedTheme: Theme? { didSet { DispatchQueue.main.async { - AppPreferencesModel.shared.preferences.theme.selectedTheme = self.selectedTheme?.name + Settings[\.theme].selectedTheme = self.selectedTheme?.name } updateAppearanceTheme() } @@ -125,7 +135,7 @@ final class ThemeModel: ObservableObject { /// If no themes are available, it will create a default theme and save /// it to the location mentioned above. /// - /// When overrides are found in `~/Library/Application Support/CodeEdit/.preferences.json` + /// When overrides are found in `~/Library/Application Support/CodeEdit/.settings.json` /// they are applied to the loaded themes without altering the original /// the files in `~/Library/Application Support/CodeEdit/themes/`. func loadThemes() throws { // swiftlint:disable:this function_body_length @@ -146,7 +156,7 @@ final class ThemeModel: ObservableObject { // get all filenames in themes folder that end with `.json` let content = try filemanager.contentsOfDirectory(atPath: url.path).filter { $0.contains(".json") } - let prefs = AppPreferencesModel.shared.preferences + let prefs = Settings.shared.preferences // load each theme from disk try content.forEach { file in let fileURL = url.appendingPathComponent(file) @@ -161,7 +171,7 @@ final class ThemeModel: ObservableObject { throw NSError() // swiftlint:disable:this discouraged_direct_init } - // check if there are any overrides in `preferences.json` + // check if there are any overrides in `settings.json` if let overrides = prefs.theme.overrides[theme.name]?["terminal"] { terminalColors.forEach { (key, _) in if let attributes = overrides[key] { @@ -180,7 +190,7 @@ final class ThemeModel: ObservableObject { // add the theme to themes array self.themes.append(theme) - // if there already is a selected theme in `preferences.json` select this theme + // if there already is a selected theme in `settings.json` select this theme // otherwise take the first in the list self.selectedDarkTheme = self.darkThemes.first { $0.name == prefs.theme.selectedDarkTheme @@ -241,7 +251,7 @@ final class ThemeModel: ObservableObject { } /// Removes all overrides of the given theme in - /// `~/Library/Application Support/CodeEdit/preferences.json` + /// `~/Library/Application Support/CodeEdit/settings.json` /// /// After removing overrides, themes are reloaded /// from `~/Library/Application Support/CodeEdit/themes`. See ``loadThemes()`` @@ -249,7 +259,7 @@ final class ThemeModel: ObservableObject { /// /// - Parameter theme: The theme to reset func reset(_ theme: Theme) { - AppPreferencesModel.shared.preferences.theme.overrides[theme.name] = [:] + Settings.shared.preferences.theme.overrides[theme.name] = [:] do { try self.loadThemes() } catch { @@ -272,8 +282,8 @@ final class ThemeModel: ObservableObject { // remove the theme from the list try filemanager.removeItem(at: url) - // remove from overrides in `preferences.json` - AppPreferencesModel.shared.preferences.theme.overrides.removeValue(forKey: theme.name) + // remove from overrides in `settings.json` + Settings.shared.preferences.theme.overrides.removeValue(forKey: theme.name) // reload themes try self.loadThemes() @@ -283,7 +293,7 @@ final class ThemeModel: ObservableObject { } /// Saves changes on theme properties to `overrides` - /// in `~/Library/Application Support/CodeEdit/preferences.json`. + /// in `~/Library/Application Support/CodeEdit/settings.json`. private func saveThemes() { let url = themesURL themes.forEach { theme in @@ -304,7 +314,7 @@ final class ThemeModel: ObservableObject { } // compare the properties and if there are differences, save to overrides - // in `preferences.json + // in `settings.json var newAttr: [String: [String: Theme.Attributes]] = ["terminal": [:], "editor": [:]] terminalColors.forEach { (key, value) in if value != oTermColors[key] { @@ -318,7 +328,7 @@ final class ThemeModel: ObservableObject { } } DispatchQueue.main.async { - AppPreferencesModel.shared.preferences.theme.overrides[theme.name] = newAttr + Settings.shared.preferences.theme.overrides[theme.name] = newAttr } } catch { diff --git a/CodeEdit/Features/AppPreferences/Model/Theme/ThemePreferences.swift b/CodeEdit/Features/Settings/Pages/ThemeSettings/Models/ThemeSettings.swift similarity index 90% rename from CodeEdit/Features/AppPreferences/Model/Theme/ThemePreferences.swift rename to CodeEdit/Features/Settings/Pages/ThemeSettings/Models/ThemeSettings.swift index 8d7929b715..3327cecb8d 100644 --- a/CodeEdit/Features/AppPreferences/Model/Theme/ThemePreferences.swift +++ b/CodeEdit/Features/Settings/Pages/ThemeSettings/Models/ThemeSettings.swift @@ -1,13 +1,13 @@ // // ThemePreferences.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Nanashi Li on 2022/04/08. // import Foundation -extension AppPreferences { +extension SettingsData { /// A dictionary containing the keys and associated ``Theme/Attributes`` of overridden properties /// @@ -30,7 +30,7 @@ extension AppPreferences { typealias ThemeOverrides = [String: [String: Theme.Attributes]] /// The global settings for themes - struct ThemePreferences: Codable { + struct ThemeSettings: Codable { /// The name of the currently selected dark theme var selectedDarkTheme: String = "codeedit-xcode-dark" @@ -45,7 +45,7 @@ extension AppPreferences { var useThemeBackground: Bool = true /// Automatically change theme based on system appearance - var mirrorSystemAppearance: Bool = true + var matchAppearance: Bool = true /// Dictionary of themes containing overrides /// @@ -88,8 +88,8 @@ extension AppPreferences { ) ?? selectedLightTheme self.selectedTheme = try container.decodeIfPresent(String.self, forKey: .selectedTheme) self.useThemeBackground = try container.decodeIfPresent(Bool.self, forKey: .useThemeBackground) ?? true - self.mirrorSystemAppearance = try container.decodeIfPresent( - Bool.self, forKey: .mirrorSystemAppearance + self.matchAppearance = try container.decodeIfPresent( + Bool.self, forKey: .matchAppearance ) ?? true self.overrides = try container.decodeIfPresent([String: ThemeOverrides].self, forKey: .overrides) ?? [:] } diff --git a/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingThemeRow.swift b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingThemeRow.swift new file mode 100644 index 0000000000..34bc988edb --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingThemeRow.swift @@ -0,0 +1,51 @@ +// +// ThemeSettingThemeRow.swift +// CodeEdit +// +// Created by Austin Condiff on 4/3/23. +// + +import SwiftUI + +struct ThemeSettingsThemeRow: View { + @Binding var theme: Theme + var active: Bool + var action: (Theme) -> Void + + @State private var presentingDetails: Bool = false + + @State private var isHovering = false + + var body: some View { + HStack { + Image(systemName: "checkmark") + .opacity(active ? 1 : 0) + .font(.system(size: 10.5, weight: .bold)) + VStack(alignment: .leading) { + Text(theme.displayName) + Text("CodeEdit") + .foregroundColor(.secondary) + .font(.footnote) + } + .frame(maxWidth: .infinity, alignment: .leading) + Button { + presentingDetails = true + } label: { + Text("Details...") + } + .buttonStyle(.bordered) + .opacity(isHovering ? 1 : 0) + ThemeSettingsColorPreview(theme) + } + .padding(10) + .onHover { hovering in + isHovering = hovering + } + .onTapGesture { + action(theme) + } + .sheet(isPresented: $presentingDetails) { + ThemeSettingsThemeDetails($theme) + } + } +} diff --git a/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsColorPreview.swift b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsColorPreview.swift new file mode 100644 index 0000000000..c2196de0e8 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsColorPreview.swift @@ -0,0 +1,95 @@ +// +// ThemeSettingsColorPreview.swift +// CodeEdit +// +// Created by Austin Condiff on 4/3/23. +// + +import SwiftUI + +struct ThemeSettingsColorPreview: View { + var theme: Theme + + @StateObject + private var themeModel: ThemeModel = .shared + + @State private var displayName: String + + init(_ theme: Theme) { + self.theme = theme + self.displayName = theme.displayName + } + + var body: some View { + HStack(spacing: 5) { + ThemeSettingsColorPreviewColor( + theme.editor.keywords.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.commands.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.types.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.attributes.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.variables.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.values.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.numbers.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.strings.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.characters.swiftColor + ) + ThemeSettingsColorPreviewColor( + theme.editor.comments.swiftColor + ) + } + .padding(12) + .background(theme.editor.background.swiftColor) + .clipShape(Capsule()) + .overlay { + ZStack { + Capsule() + .stroke(Color(.black).opacity(0.2), lineWidth: 0.5) + .frame(maxWidth: .infinity, maxHeight: .infinity) + Capsule() + .strokeBorder(Color(.white).opacity(0.2), lineWidth: 0.5) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + } +} + +struct ThemeSettingsColorPreviewColor: View { + private var color: Color + + init(_ color: Color) { + self.color = color + } + + var body: some View { + color + .frame(width: 5, height: 5) + .cornerRadius(5) + .overlay { + ZStack { + Circle() + .stroke(Color(.black).opacity(0.2), lineWidth: 0.5) + .frame(width: 5, height: 5) + Circle() + .strokeBorder(Color(.white).opacity(0.2), lineWidth: 0.5) + .frame(width: 5, height: 5) + } + } + + } +} diff --git a/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsThemeDetails.swift b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsThemeDetails.swift new file mode 100644 index 0000000000..b2edb71702 --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsThemeDetails.swift @@ -0,0 +1,123 @@ +// +// ThemeSettingsThemeDetails.swift +// CodeEdit +// +// Created by Austin Condiff on 4/3/23. +// + +import SwiftUI + +struct ThemeSettingsThemeDetails: View { + @Environment(\.dismiss) var dismiss + + @Binding var theme: Theme + + @State private var initialTheme: Theme + + @StateObject + private var themeModel: ThemeModel = .shared + + init(_ theme: Binding) { + _theme = theme + _initialTheme = State(initialValue: theme.wrappedValue) + } + + var body: some View { + VStack(spacing: 0) { + Form { + Section { + TextField("Name", text: $theme.displayName) + } + Section { + SettingsColorPicker( + "Text", + color: $theme.editor.text.swiftColor + ) + SettingsColorPicker( + "Cursor", + color: $theme.editor.insertionPoint.swiftColor + ) + SettingsColorPicker( + "Invisibles", + color: $theme.editor.invisibles.swiftColor + ) + } + Section { + SettingsColorPicker( + "Background", + color: $theme.editor.background.swiftColor + ) + SettingsColorPicker( + "Current Line", + color: $theme.editor.lineHighlight.swiftColor + ) + SettingsColorPicker( + "Selection", + color: $theme.editor.selection.swiftColor + ) + } + Section { + SettingsColorPicker( + "Keywords", + color: $theme.editor.keywords.swiftColor + ) + SettingsColorPicker( + "Commands", + color: $theme.editor.commands.swiftColor + ) + SettingsColorPicker( + "Types", + color: $theme.editor.types.swiftColor + ) + SettingsColorPicker( + "Attributes", + color: $theme.editor.attributes.swiftColor + ) + SettingsColorPicker( + "Variables", + color: $theme.editor.variables.swiftColor + ) + SettingsColorPicker( + "Values", + color: $theme.editor.values.swiftColor + ) + SettingsColorPicker( + "Numbers", + color: $theme.editor.numbers.swiftColor + ) + SettingsColorPicker( + "Strings", + color: $theme.editor.strings.swiftColor + ) + SettingsColorPicker( + "Characters", + color: $theme.editor.characters.swiftColor + ) + SettingsColorPicker( + "Comments", + color: $theme.editor.comments.swiftColor + ) + } + }.formStyle(.grouped) + Divider() + HStack { + Spacer() + Button { + theme = initialTheme + dismiss() + } label: { + Text("Cancel") + } + .buttonStyle(.bordered) + Button { + dismiss() + } label: { + Text("Done") + } + .buttonStyle(.borderedProminent) + } + .padding() + } + .constrainHeightToWindow() + } +} diff --git a/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsView.swift b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsView.swift new file mode 100644 index 0000000000..f1738417aa --- /dev/null +++ b/CodeEdit/Features/Settings/Pages/ThemeSettings/ThemeSettingsView.swift @@ -0,0 +1,133 @@ +// +// ThemePreferencesView.swift +// CodeEdit +// +// Created by Lukas Pistrol on 30.03.22. +// + +import SwiftUI +import Preferences + +/// A view that implements the `Theme` preference section +struct ThemeSettingsView: View { + @Environment(\.colorScheme) var colorScheme + @ObservedObject private var themeModel: ThemeModel = .shared + @AppSettings var settings + + @State private var listView: Bool = false + @State private var selectedAppearance: ThemeSettingsAppearances = .dark + + enum ThemeSettingsAppearances: String, CaseIterable { + case light = "Light Appearance" + case dark = "Dark Appearance" + } + + func getThemeActive (_ theme: Theme) -> Bool { + if settings.theme.matchAppearance { + return selectedAppearance == .dark + ? themeModel.selectedDarkTheme == theme + : selectedAppearance == .light + ? themeModel.selectedLightTheme == theme + : themeModel.selectedTheme == theme + } + return themeModel.selectedTheme == theme + } + + func activateTheme (_ theme: Theme) { + if settings.theme.matchAppearance { + if selectedAppearance == .dark { + themeModel.selectedDarkTheme = theme + } else if selectedAppearance == .light { + themeModel.selectedLightTheme = theme + } + if (selectedAppearance == .dark && colorScheme == .dark) + || (selectedAppearance == .light && colorScheme == .light) { + themeModel.selectedTheme = theme + } + } else { + themeModel.selectedTheme = theme + if colorScheme == .light { + themeModel.selectedLightTheme = theme + } + if colorScheme == .dark { + themeModel.selectedDarkTheme = theme + } + } + } + + var body: some View { + SettingsForm { + Section { + changeThemeOnSystemAppearance + if settings.theme.matchAppearance { + alwaysUseDarkTerminalAppearance + } + useThemeBackground + } + Section { + VStack(spacing: 0) { + if settings.theme.matchAppearance { + Picker("", selection: $selectedAppearance) { + ForEach(ThemeSettingsAppearances.allCases, id: \.self) { tab in + Text(tab.rawValue) + .tag(tab) + } + } + .pickerStyle(.segmented) + .labelsHidden() + .padding(10) + } + VStack(spacing: 0) { + ForEach(selectedAppearance == .dark ? themeModel.darkThemes : themeModel.lightThemes) { theme in + Divider() + ThemeSettingsThemeRow( + theme: $themeModel.themes[themeModel.themes.firstIndex(of: theme)!], + active: getThemeActive(theme), + action: activateTheme + ).id(theme) + } + ForEach(selectedAppearance == .dark ? themeModel.lightThemes : themeModel.darkThemes) { theme in + Divider() + ThemeSettingsThemeRow( + theme: $themeModel.themes[themeModel.themes.firstIndex(of: theme)!], + active: getThemeActive(theme), + action: activateTheme + ).id(theme) + } + } + } + .padding(-10) + } + } + } +} + +private extension ThemeSettingsView { + private var useThemeBackground: some View { + Toggle("Use theme background ", isOn: $settings.theme.useThemeBackground) + } + + private var alwaysUseDarkTerminalAppearance: some View { + Toggle("Always use dark terminal appearance", isOn: $settings.terminal.darkAppearance) + } + + private var changeThemeOnSystemAppearance: some View { + Toggle( + "Automatically change theme based on system appearance", + isOn: $settings.theme.matchAppearance + ) + .onChange(of: settings.theme.matchAppearance) { value in + if value { + if colorScheme == .dark { + themeModel.selectedTheme = themeModel.selectedDarkTheme + } else { + themeModel.selectedTheme = themeModel.selectedLightTheme + } + } else { + themeModel.selectedTheme = themeModel.themes.first { + $0.name == settings.theme.selectedTheme + } + } + } + } +} diff --git a/CodeEdit/Features/Settings/SettingsView.swift b/CodeEdit/Features/Settings/SettingsView.swift new file mode 100644 index 0000000000..d6c5f6b4d6 --- /dev/null +++ b/CodeEdit/Features/Settings/SettingsView.swift @@ -0,0 +1,123 @@ +// +// SettingsView.swift +// CodeEdit +// +// Created by Raymond Vleeshouwer on 26/03/23. +// + +import SwiftUI +import CodeEditSymbols +import AppKit +import Introspect + +/// A struct for settings +struct SettingsView: View { + @StateObject var model = SettingsViewModel() + @Environment(\.colorScheme) private var colorScheme + + /// An array of navigationItem(s) + private static let pages: [SettingsPage] = [ + .init(.general, baseColor: .gray, icon: .system("gear")), + .init(.accounts, baseColor: .blue, icon: .system("at")), + .init(.behavior, baseColor: .red, icon: .system("flowchart.fill")), + .init(.navigation, baseColor: .green, icon: .system("arrow.triangle.turn.up.right.diamond.fill")), + .init(.theme, baseColor: .pink, icon: .system("paintbrush.fill")), + .init(.textEditing, baseColor: .blue, icon: .system("pencil.line")), + .init(.terminal, baseColor: .blue, icon: .system("terminal.fill")), + .init(.keybindings, baseColor: .gray, icon: .system("keyboard.fill")), + .init(.sourceControl, baseColor: .blue, icon: .symbol("vault")), + .init(.components, baseColor: .blue, icon: .system("puzzlepiece.fill")), + .init(.location, baseColor: .green, icon: .system("externaldrive.fill")), + .init(.advanced, baseColor: .gray, icon: .system("gearshape.2.fill")) + ] + + /// Variables for the selected Page, the current search text and software updater + @State private var selectedPage = pages.first! + @State private var searchText: String = "" + + @Environment(\.presentationMode) var presentationMode + + let updater: SoftwareUpdater + + var body: some View { + NavigationSplitView { + List(selection: $selectedPage) { + Section { + ForEach(Self.pages) { item in + if searchText.isEmpty || item.name.rawValue.lowercased().contains(searchText.lowercased()) { + SettingsPageView(item) + } + } + } + } + .navigationSplitViewColumnWidth(215) + .safeAreaInset(edge: .top, spacing: 0) { + List {} + .frame(height: 35) + .searchable(text: $searchText, placement: .sidebar, prompt: "Search") + .scrollDisabled(true) + } +// .safeAreaInset(edge: .top) { +// TextField("Search", text: $searchText, prompt: Text("Search")) +// .textFieldStyle(.roundedBorder) +// .padding(.horizontal, 10) +// .controlSize(.large) +// .introspectTextField { textField in +// let iconImage = NSImage(systemSymbolName: "magnifyingglass", accessibilityDescription: nil) +// +// let attachment = NSTextAttachment() +// attachment.contents = iconImage +// +// let cell = NSTextAttachmentCell(imageCell: iconImage) +// attachment.attachmentCell = cell +// +// textField.setCell_(NSSearchFieldCell.alloc().init()) +// } +// } + } detail: { + Group { + switch selectedPage.name { + case .general: + GeneralSettingsView().environmentObject(updater) + case .accounts: + AccountsSettingsView() + case .theme: + ThemeSettingsView() + case .textEditing: + TextEditingSettingsView() + case .terminal: + TerminalSettingsView() + case .sourceControl: + SourceControlSettingsView() + case .location: + LocationsSettingsView() + default: + Text("Implementation Needed").frame(alignment: .center) + } + } + .navigationSplitViewColumnWidth(500) + .hideSidebarToggle() + .onAppear { + model.showingDetails = false + } + } + .navigationTitle(selectedPage.name.rawValue) + .toolbar { + ToolbarItem(placement: .navigation) { + if !model.showingDetails { + Rectangle() + .frame(width: 10) + .opacity(0) + } else { + EmptyView() + } + } + } + .environmentObject(model) + } +} + +class SettingsViewModel: ObservableObject { + @Published var showingDetails: Bool = false + @Published var scrolledToTop: Bool = false +} diff --git a/CodeEdit/Features/Settings/SettingsWindow.swift b/CodeEdit/Features/Settings/SettingsWindow.swift new file mode 100644 index 0000000000..380bda3dba --- /dev/null +++ b/CodeEdit/Features/Settings/SettingsWindow.swift @@ -0,0 +1,26 @@ +// +// SettingsWindow.swift +// CodeEdit +// +// Created by Austin Condiff on 3/31/23. +// + +import SwiftUI + +struct SettingsWindow: Scene { + private let updater = SoftwareUpdater() + + var body: some Scene { + Window("Settings", id: "settings") { + SettingsView(updater: updater) + .frame(minWidth: 715, maxWidth: 715) + .task { + let window = NSApp.windows.first { $0.identifier?.rawValue == "settings" }! + window.titlebarAppearsTransparent = true + } + } + .windowStyle(.automatic) + .windowToolbarStyle(.unified) + .windowResizability(.contentSize) + } +} diff --git a/CodeEdit/Features/AppPreferences/SoftwareUpdater.swift b/CodeEdit/Features/Settings/SoftwareUpdater.swift similarity index 100% rename from CodeEdit/Features/AppPreferences/SoftwareUpdater.swift rename to CodeEdit/Features/Settings/SoftwareUpdater.swift diff --git a/CodeEdit/Features/Settings/Views/ExternalLink.swift b/CodeEdit/Features/Settings/Views/ExternalLink.swift new file mode 100644 index 0000000000..2218b57834 --- /dev/null +++ b/CodeEdit/Features/Settings/Views/ExternalLink.swift @@ -0,0 +1,96 @@ +// +// ExternalLink.swift +// CodeEdit +// +// Created by Austin Condiff on 4/20/23. +// + +import SwiftUI + +// Usage 1: ExternalLink with title +// ExternalLink("Title", destination: URL(string: "https://apple.com")!) +// +// Usage 2: ExternalLink with title and subtitle but no icon +// ExternalLink(destination: URL(string: "https://apple.com")!) { +// Text("Title") +// Text("Subtitle") +// } icon: { +// Image(systemName: "star") +// } +// +// Usage 3: ExternalLink with title, subtitle and icon +// ExternalLink(destination: URL(string: "https://apple.com")!) { +// Text("Title") +// Text("Subtitle") +// } icon: { +// Image(systemName: "star") +// } + +struct ExternalLink: View { + let title: String? + let subtitle: String? + let destination: URL + let icon: (() -> Icon)? + let content: () -> Content + + init( + _ title: String, + destination: URL, + subtitle: String? = nil, + icon: (() -> Icon)? = nil + ) where Content == EmptyView, Icon == EmptyView { + self.title = title + self.subtitle = subtitle + self.destination = destination + self.icon = icon + self.content = { EmptyView() } + } + + init( + destination: URL, + @ViewBuilder content: @escaping () -> Content, + title: String? = nil, + subtitle: String? = nil, + @ViewBuilder icon: @escaping() -> Icon = { EmptyView() } + ) { + self.title = title + self.subtitle = subtitle + self.destination = destination + self.icon = icon + self.content = content + } + + var body: some View { + Button(action: { NSWorkspace.shared.open(destination) }, label: { + HStack(spacing: 8) { + icon?() + VStack(alignment: .leading, spacing: 2) { + if let title = title { + Text(title) + } + if let subtitle = subtitle { + Text(subtitle) + .font(.caption) + .foregroundColor(Color(.secondaryLabelColor)) + } + content() + } + Spacer() + Image(systemName: "arrow.up.right") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(Color(.tertiaryLabelColor)) + } + }) + .buttonStyle(ExternalLinkButtonStyle()) + } +} + +struct ExternalLinkButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding(10) + .background(configuration.isPressed ? Color(.separatorColor) : Color(.clear)) + .contentShape(Rectangle()) + .padding(-10) + } +} diff --git a/CodeEdit/Features/Settings/Views/MonospacedFontPicker.swift b/CodeEdit/Features/Settings/Views/MonospacedFontPicker.swift new file mode 100644 index 0000000000..c36a4aa4f8 --- /dev/null +++ b/CodeEdit/Features/Settings/Views/MonospacedFontPicker.swift @@ -0,0 +1,93 @@ +// +// MonospacedFontPicker.swift +// CodeEdit +// +// Created by Austin Condiff on 4/22/23. +// + +import SwiftUI + +struct MonospacedFontPicker: View { + var title: String + @Binding var selectedFontName: String + @State var recentFonts: [String] + @State var monospacedFontFamilyNames: [String] = [] + @State var otherFontFamilyNames: [String] = [] + + init(title: String, selectedFontName: Binding) { + self.title = title + self._selectedFontName = selectedFontName + self.recentFonts = UserDefaults.standard.stringArray(forKey: "recentFonts") ?? [] + } + + private func pushIntoRecentFonts(_ newItem: String) { + recentFonts.removeAll(where: { $0 == newItem }) + recentFonts.insert(newItem, at: 0) + if recentFonts.count > 3 { + recentFonts.removeLast() + } + UserDefaults.standard.set(recentFonts, forKey: "recentFonts") + } + + func getFonts() { + DispatchQueue.global(qos: .userInitiated).async { + let availableFontFamilies = NSFontManager.shared.availableFontFamilies + + self.monospacedFontFamilyNames = availableFontFamilies.filter { fontFamilyName in + let fontNames = NSFontManager.shared.availableMembers(ofFontFamily: fontFamilyName) ?? [] + return fontNames.contains { fontName in + guard let font = NSFont(name: "\(fontName[0])", size: 14) else { + return false + } + return font.isFixedPitch && font.numberOfGlyphs > 26 + } + } + .filter { $0 != "SF Mono" } + + self.otherFontFamilyNames = availableFontFamilies.filter { fontFamilyName in + let fontNames = NSFontManager.shared.availableMembers(ofFontFamily: fontFamilyName) ?? [] + return fontNames.contains { fontName in + guard let font = NSFont(name: "\(fontName[0])", size: 14) else { + return false + } + return !font.isFixedPitch && font.numberOfGlyphs > 26 + } + } + } + } + + var body: some View { + return Picker(selection: $selectedFontName, label: Text(title)) { + Text("System Font") + .font(Font(NSFont.monospacedSystemFont(ofSize: 13.5, weight: .medium))) + .tag("SF Mono") + if !recentFonts.isEmpty { + Divider() + ForEach(recentFonts, id: \.self) { fontFamilyName in + Text(fontFamilyName).font(.custom(fontFamilyName, size: 13.5)) + } + } + if !monospacedFontFamilyNames.isEmpty { + Divider() + ForEach(monospacedFontFamilyNames, id: \.self) { fontFamilyName in + Text(fontFamilyName).font(.custom(fontFamilyName, size: 13.5)) + } + } + if !otherFontFamilyNames.isEmpty { + Divider() + Picker(selection: $selectedFontName, label: Text("Other fonts...")) { + ForEach(otherFontFamilyNames, id: \.self) { fontFamilyName in + Text(fontFamilyName) + .font(.custom(fontFamilyName, size: 13.5)) + } + } + } + } + .onChange(of: selectedFontName) { selection in + if selectedFontName != "SF Mono" { + pushIntoRecentFonts(selection) + } + } + .onAppear(perform: getFonts) + } +} diff --git a/CodeEdit/Features/Settings/Views/SettingsColorPicker.swift b/CodeEdit/Features/Settings/Views/SettingsColorPicker.swift new file mode 100644 index 0000000000..0c91629d82 --- /dev/null +++ b/CodeEdit/Features/Settings/Views/SettingsColorPicker.swift @@ -0,0 +1,26 @@ +// +// SettingsColorPicker.swift +// CodeEdit +// +// Created by Austin Condiff on 4/3/23. +// + +import SwiftUI + +struct SettingsColorPicker: View { + @Binding var color: Color + + private let label: String + + init(_ label: String, color: Binding) { + self._color = color + self.label = label + } + + var body: some View { + LabeledContent(label) { + ColorPicker(selection: $color, supportsOpacity: false) { } + .labelsHidden() + } + } +} diff --git a/CodeEdit/Features/Settings/Views/SettingsDetailsView.swift b/CodeEdit/Features/Settings/Views/SettingsDetailsView.swift new file mode 100644 index 0000000000..91f0c921a5 --- /dev/null +++ b/CodeEdit/Features/Settings/Views/SettingsDetailsView.swift @@ -0,0 +1,42 @@ +// +// SettingsDetailsView.swift +// CodeEdit +// +// Created by Austin Condiff on 4/8/23. +// + +import SwiftUI + +struct SettingsDetailsView: View { + @Environment(\.presentationMode) var presentationMode + @EnvironmentObject var model: SettingsViewModel + + let title: String + + @ViewBuilder + var content: Content + + var body: some View { + content + .navigationTitle("") + .toolbar { + ToolbarItem(placement: .navigation) { + Button { + print(self.presentationMode.wrappedValue) + self.presentationMode.wrappedValue.dismiss() + } label: { + Image(systemName: "chevron.left") + } + Text(title) + } + } + .hideSidebarToggle() + .task { + let window = NSApp.windows.first { $0.identifier?.rawValue == "settings" }! + window.title = title + } + .onAppear { + model.showingDetails = true + } + } +} diff --git a/CodeEdit/Features/Settings/Views/SettingsForm.swift b/CodeEdit/Features/Settings/Views/SettingsForm.swift new file mode 100644 index 0000000000..c904f5c924 --- /dev/null +++ b/CodeEdit/Features/Settings/Views/SettingsForm.swift @@ -0,0 +1,82 @@ +// +// SettingsForm.swift +// CodeEdit +// +// Created by Austin Condiff on 4/8/23. +// + +import SwiftUI +import Introspect + +struct SettingsForm: View { + @Environment(\.colorScheme) private var colorScheme + @Environment(\.controlActiveState) private var activeState + @EnvironmentObject var model: SettingsViewModel + @ViewBuilder var content: Content + + var body: some View { + Form { + Section { + EmptyView() + } footer: { + Rectangle() + .frame(height: 0) + .background( + GeometryReader { + Color.clear.preference( + key: ViewOffsetKey.self, + value: -$0.frame(in: .named("scroll")).origin.y + ) + } + ) + .onPreferenceChange(ViewOffsetKey.self) { + if $0 <= -20.0 && !model.scrolledToTop { + withAnimation { + model.scrolledToTop = true + } + } else if $0 > -20.0 && model.scrolledToTop { + withAnimation { + model.scrolledToTop = false + } + } + } + } + content + } + .introspectScrollView { scrollView in + scrollView.scrollerInsets.top = 50 + } + .formStyle(.grouped) + .coordinateSpace(name: "scroll") + .safeAreaInset(edge: .top, spacing: -50) { + EffectView(.menu) + .opacity(!model.scrolledToTop ? 1 : 0) + .transaction { transaction in + transaction.animation = nil + } + .overlay(alignment: .bottom) { + LinearGradient( + gradient: Gradient( + colors: [.black.opacity(colorScheme == .dark ? 1 : 0.17), .black.opacity(0)] + ), + startPoint: .top, + endPoint: .bottom + ) + .frame(height: colorScheme == .dark || activeState == .inactive ? 1 : 2) + .padding(.bottom, colorScheme == .dark || activeState == .inactive ? -1 : -2) + .opacity(!model.scrolledToTop ? 1 : 0) + .transition(.opacity) + } + .ignoresSafeArea() + .frame(height: 0) + } + } +} + +struct ViewOffsetKey: PreferenceKey { + typealias Value = CGFloat + static var defaultValue = CGFloat.zero + static func reduce(value: inout Value, nextValue: () -> Value) { + value += nextValue() + } +} diff --git a/CodeEdit/Features/Settings/Views/SettingsPageView.swift b/CodeEdit/Features/Settings/Views/SettingsPageView.swift new file mode 100644 index 0000000000..846b227391 --- /dev/null +++ b/CodeEdit/Features/Settings/Views/SettingsPageView.swift @@ -0,0 +1,58 @@ +// +// SettingPageView.swift +// CodeEdit +// +// Created by Austin Condiff on 3/31/23. +// + +import SwiftUI + +struct SettingsPageView: View { + var page: SettingsPage + + init(_ page: SettingsPage) { + self.page = page + } + + var body: some View { + NavigationLink(value: page) { + Label { + Text(page.nameString) + .padding(.leading, 2) + } icon: { + if let icon = page.icon { + Group { + switch 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) + } + } + .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.gradient) + .shadow(color: Color(NSColor.black).opacity(0.25), radius: 0.5, y: 0.5) + ) + } else { + EmptyView() + } + } + } + } +} diff --git a/CodeEdit/Features/Settings/Views/View+ConstrainHeightToWindow.swift b/CodeEdit/Features/Settings/Views/View+ConstrainHeightToWindow.swift new file mode 100644 index 0000000000..618dcd1b25 --- /dev/null +++ b/CodeEdit/Features/Settings/Views/View+ConstrainHeightToWindow.swift @@ -0,0 +1,38 @@ +// +// View+ConstrainHeightToWindow.swift +// CodeEdit +// +// Created by Austin Condiff on 4/3/23. +// + +import SwiftUI + +extension NSWindow { + var isSettingsWindow: Bool { + self.identifier?.rawValue == "settings" + } +} + +extension NSApplication { + var settingsWindow: NSWindow? { + NSApp.windows.first { $0.isSettingsWindow } + } +} + +extension View { + func constrainHeightToWindow() -> some View { + modifier(ConstrainHeightToWindowViewModifier()) + } +} + +struct ConstrainHeightToWindowViewModifier: ViewModifier { + @State var height: CGFloat = 100 + + func body(content: Content) -> some View { + content + .frame(height: height-100) + .onReceive(NSApp.settingsWindow!.publisher(for: \.frame)) { newValue in + height = newValue.height + } + } +} diff --git a/CodeEdit/Features/Settings/Views/View+HideSidebarToggle.swift b/CodeEdit/Features/Settings/Views/View+HideSidebarToggle.swift new file mode 100644 index 0000000000..a7cac0d8fa --- /dev/null +++ b/CodeEdit/Features/Settings/Views/View+HideSidebarToggle.swift @@ -0,0 +1,28 @@ +// +// View+HideSidebarToggle.swift +// CodeEdit +// +// Created by Austin Condiff on 4/5/23. +// + +import SwiftUI + +extension View { + func hideSidebarToggle() -> some View { + modifier(HideSidebarToggleViewModifier()) + } +} + +struct HideSidebarToggleViewModifier: ViewModifier { + func body(content: Content) -> some View { + content + .task { + let window = NSApp.windows.first { $0.identifier?.rawValue == "settings" }! + let sidebaritem = "com.apple.SwiftUI.navigationSplitView.toggleSidebar" + let index = window.toolbar?.items.firstIndex { $0.itemIdentifier.rawValue == sidebaritem } + if let index { + window.toolbar?.removeItem(at: index) + } + } + } +} diff --git a/CodeEdit/Features/StatusBar/Views/StatusBarDrawer/StatusBarDrawer.swift b/CodeEdit/Features/StatusBar/Views/StatusBarDrawer/StatusBarDrawer.swift index db7913c824..9476206207 100644 --- a/CodeEdit/Features/StatusBar/Views/StatusBarDrawer/StatusBarDrawer.swift +++ b/CodeEdit/Features/StatusBar/Views/StatusBarDrawer/StatusBarDrawer.swift @@ -17,8 +17,8 @@ struct StatusBarDrawer: View { @EnvironmentObject private var model: StatusBarViewModel - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings + private var settings @StateObject private var themeModel: ThemeModel = .shared @@ -28,7 +28,9 @@ struct StatusBarDrawer: View { /// Returns the `background` color of the selected theme private var backgroundColor: NSColor { - if let selectedTheme = themeModel.selectedTheme, + if let selectedTheme = settings.theme.matchAppearance && settings.terminal.darkAppearance + ? themeModel.selectedDarkTheme + : themeModel.selectedTheme, let index = themeModel.themes.firstIndex(of: selectedTheme) { return NSColor(themeModel.themes[index].terminal.background.swiftColor) } @@ -47,24 +49,48 @@ struct StatusBarDrawer: View { .frame(maxWidth: 175) .padding(.leading, -2) Spacer() - StatusBarIcon(icon: Image(systemName: "trash")) { + Button { // clear logs + } label: { + Image(systemName: "trash") } + .buttonStyle(.icon) Divider() HStack(alignment: .center, spacing: 3.5) { - StatusBarIcon(icon: Image(systemName: "square.split.2x1")) { + Button { // split terminal + } label: { + Image(systemName: "square.split.2x1") } - StatusBarIcon(icon: Image(systemName: "arrowtriangle.up.square"), active: model.isMaximized) { + .buttonStyle(.icon) + Button { model.isMaximized.toggle() + } label: { + Image(systemName: "arrowtriangle.up.square") } + .buttonStyle(.icon(isActive: model.isMaximized)) } } .padding(.horizontal, 7) .padding(.vertical, 8) .frame(maxHeight: 28) } - .background(Color(nsColor: backgroundColor)) + .background { + if settings.theme.useThemeBackground { + Color(nsColor: backgroundColor) + } else { + if colorScheme == .dark { + EffectView(.underPageBackground) + } else { + EffectView(.contentBackground) + } + } + } + .colorScheme( + settings.theme.matchAppearance && settings.terminal.darkAppearance + ? themeModel.selectedDarkTheme?.appearance == .dark ? .dark : .light + : themeModel.selectedTheme?.appearance == .dark ? .dark : .light + ) } } } diff --git a/CodeEdit/Features/StatusBar/Views/StatusBarIcon.swift b/CodeEdit/Features/StatusBar/Views/StatusBarIcon.swift index 04f4c6365e..a619d3e5eb 100644 --- a/CodeEdit/Features/StatusBar/Views/StatusBarIcon.swift +++ b/CodeEdit/Features/StatusBar/Views/StatusBarIcon.swift @@ -10,16 +10,22 @@ import SwiftUI /// Accessory icon view for status bar. struct StatusBarIcon: View { /// Unifies icon font for status bar accessories. - static let iconFont = Font.system(size: 14.5, weight: .regular, design: .default) + private let iconFont: Font + + enum IconSize: CGFloat { + case small = 11 + case medium = 14.5 + } private let icon: Image private let active: Bool private let action: () -> Void - init(icon: Image, active: Bool? = false, action: @escaping () -> Void) { + init(icon: Image, size: IconSize = .medium, active: Bool? = false, action: @escaping () -> Void) { self.icon = icon self.action = action self.active = active ?? false + self.iconFont = Font.system(size: size.rawValue, weight: .regular, design: .default) } var body: some View { @@ -27,13 +33,83 @@ struct StatusBarIcon: View { action: action, label: { icon - .font(StatusBarIcon.iconFont) - .foregroundColor(active ? .accentColor : .secondary) + .font(iconFont) .contentShape(Rectangle()) } ) - .buttonStyle(.plain) + .buttonStyle(StatusBarIconButtonStyle(isActive: active)) + } +} + +struct StatusBarIconButtonStyle: ButtonStyle { + var isActive: Bool = false + func makeBody(configuration: Self.Configuration) -> some View { + configuration.label + .foregroundColor(isActive ? Color.accentColor : Color.secondary) + .brightness(configuration.isPressed ? 0.5 : 0) + } +} + +struct IconButtonStyle: ButtonStyle { + var isActive: Bool? + var font: Font? + var size: CGFloat? + + func makeBody(configuration: ButtonStyle.Configuration) -> some View { + IconButton( + configuration: configuration, + isActive: isActive, + font: font, + size: size + ) + } + + struct IconButton: View { + let configuration: ButtonStyle.Configuration + var isActive: Bool + var font: Font + var size: CGFloat? + @Environment(\.isEnabled) private var isEnabled: Bool + @Environment(\.colorScheme) private var colorScheme + + init(configuration: ButtonStyle.Configuration, isActive: Bool?, font: Font?, size: CGFloat?) { + self.configuration = configuration + self.isActive = isActive ?? false + self.font = font ?? Font.system(size: 14.5, weight: .regular, design: .default) + self.size = size ?? nil + } + + var body: some View { + configuration.label + .font(font) + .foregroundColor( + isActive + ? Color(.controlAccentColor) + : Color(.secondaryLabelColor) + ) + .frame(width: size, height: size) + .contentShape(Rectangle()) + .opacity(isEnabled ? 1 : 0.5) + .brightness( + configuration.isPressed + ? colorScheme == .dark + ? 0.5 + : isActive ? -0.25 : -0.75 + : 0 + ) + } + } +} + +extension ButtonStyle where Self == IconButtonStyle { + static func icon( + isActive: Bool? = false, + font: Font? = Font.system(size: 14.5, weight: .regular, design: .default), + size: CGFloat? = 24 + ) -> IconButtonStyle { + return IconButtonStyle(isActive: isActive, font: font, size: size) } + static var icon: IconButtonStyle { .init() } } struct StatusBarIcon_Previews: PreviewProvider { diff --git a/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarIndentSelector.swift b/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarIndentSelector.swift index e7f5a19b24..0d786968f0 100644 --- a/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarIndentSelector.swift +++ b/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarIndentSelector.swift @@ -8,9 +8,7 @@ import SwiftUI struct StatusBarIndentSelector: View { - - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings var body: some View { Menu { @@ -24,14 +22,14 @@ struct StatusBarIndentSelector: View { Divider() - Picker("Tab Width", selection: $prefs.preferences.textEditing.defaultTabWidth) { + Picker("Tab Width", selection: $settings.textEditing.defaultTabWidth) { ForEach(2..<9) { index in Text("\(index) Spaces") .tag(index) } } } label: { - Text("\(prefs.preferences.textEditing.defaultTabWidth) Spaces") + Text("\(settings.textEditing.defaultTabWidth) Spaces") } .menuStyle(StatusBarMenuStyle()) .onHover { isHovering($0) } diff --git a/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarToggleDrawerButton.swift b/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarToggleDrawerButton.swift index 96a900d578..0c5eb8f3db 100644 --- a/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarToggleDrawerButton.swift +++ b/CodeEdit/Features/StatusBar/Views/StatusBarItems/StatusBarToggleDrawerButton.swift @@ -32,12 +32,13 @@ internal struct StatusBarToggleDrawerButton: View { } internal var body: some View { - StatusBarIcon(icon: Image(systemName: "square.bottomthird.inset.filled")) { + Button { togglePanel() + } label: { + Image(systemName: "square.bottomthird.inset.filled") } - .tint(collapsed ? .primary : .accentColor) + .buttonStyle(.icon) .keyboardShortcut("Y", modifiers: [.command, .shift]) - .buttonStyle(.borderless) .onHover { isHovering($0) } } } diff --git a/CodeEdit/Features/StatusBar/Views/StatusBarView.swift b/CodeEdit/Features/StatusBar/Views/StatusBarView.swift index e60264eb97..435692c12a 100644 --- a/CodeEdit/Features/StatusBar/Views/StatusBarView.swift +++ b/CodeEdit/Features/StatusBar/Views/StatusBarView.swift @@ -24,7 +24,7 @@ struct StatusBarView: View { private var model: StatusBarViewModel @ObservedObject - private var prefs: AppPreferencesModel = .shared + private var prefs: Settings = .shared static let height = 28.0 diff --git a/CodeEdit/Features/Tabs/Views/TabBarAccessory.swift b/CodeEdit/Features/Tabs/Views/TabBarAccessory.swift index d981e44749..6cbf3cbd8a 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarAccessory.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarAccessory.swift @@ -23,14 +23,9 @@ struct TabBarAccessoryIcon: View { var body: some View { Button( action: action, - label: { - icon - .font(TabBarAccessoryIcon.iconFont) - .frame(height: TabBarView.height - 2) - .padding(.horizontal, 4) - .contentShape(Rectangle()) - } + label: { icon } ) + .buttonStyle(.icon(size: 24)) } } diff --git a/CodeEdit/Features/Tabs/Views/TabBarDivider.swift b/CodeEdit/Features/Tabs/Views/TabBarDivider.swift index 3fd8895469..ff1bfd8ec8 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarDivider.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarDivider.swift @@ -12,17 +12,16 @@ struct TabDivider: View { @Environment(\.colorScheme) var colorScheme - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings let width: CGFloat = 1 var body: some View { Rectangle() .frame(width: width) - .padding(.vertical, prefs.preferences.general.tabBarStyle == .xcode ? 8 : 0) + .padding(.vertical, settings.general.tabBarStyle == .xcode ? 8 : 0) .foregroundColor( - prefs.preferences.general.tabBarStyle == .xcode + settings.general.tabBarStyle == .xcode ? Color(nsColor: colorScheme == .dark ? .white : .black) .opacity(0.12) : Color(nsColor: colorScheme == .dark ? .controlColor : .black) @@ -36,16 +35,15 @@ struct TabBarTopDivider: View { @Environment(\.colorScheme) var colorScheme - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings var body: some View { ZStack(alignment: .top) { - if prefs.preferences.general.tabBarStyle == .native { + if settings.general.tabBarStyle == .native { // Color background overlay in native style. Color(nsColor: .black) .opacity(colorScheme == .dark ? 0.80 : 0.02) - .frame(height: prefs.preferences.general.tabBarStyle == .xcode ? 1.0 : 0.8) + .frame(height: settings.general.tabBarStyle == .xcode ? 1.0 : 0.8) // Shadow of top divider in native style. TabBarNativeShadow() } @@ -58,20 +56,19 @@ struct TabBarBottomDivider: View { @Environment(\.colorScheme) var colorScheme - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings var body: some View { Rectangle() .foregroundColor( - prefs.preferences.general.tabBarStyle == .xcode + settings.general.tabBarStyle == .xcode ? Color(nsColor: .separatorColor) .opacity(colorScheme == .dark ? 0.80 : 1) : Color(nsColor: .black) .opacity(colorScheme == .dark ? 0.65 : 0.13) ) - .frame(height: prefs.preferences.general.tabBarStyle == .xcode ? 1.0 : 0.8) + .frame(height: settings.general.tabBarStyle == .xcode ? 1.0 : 0.8) } } diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemButtonStyle.swift b/CodeEdit/Features/Tabs/Views/TabBarItemButtonStyle.swift index ba098b6fe3..7fc9d162d5 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemButtonStyle.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemButtonStyle.swift @@ -12,7 +12,7 @@ struct TabBarItemButtonStyle: ButtonStyle { var colorScheme @StateObject - private var prefs: AppPreferencesModel = .shared + private var prefs: Settings = .shared @Binding private var isPressing: Bool diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift b/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift index 969161239f..c80e545ce6 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemCloseButton.swift @@ -19,8 +19,7 @@ struct TabBarItemCloseButton: View { @Environment(\.colorScheme) var colorScheme - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings @State private var isPressingClose: Bool = false @@ -32,7 +31,7 @@ struct TabBarItemCloseButton: View { var body: some View { HStack { - if prefs.preferences.general.tabBarStyle == .xcode { + if settings.general.tabBarStyle == .xcode { Image(systemName: "xmark") .font(.system(size: 11.5, weight: .regular, design: .rounded)) .foregroundColor( @@ -52,7 +51,7 @@ struct TabBarItemCloseButton: View { ? Color(nsColor: .white) .opacity(isPressingClose ? 0.10 : isHoveringClose ? 0.05 : 0) : ( - prefs.preferences.general.tabBarStyle == .xcode + settings.general.tabBarStyle == .xcode ? Color(nsColor: isActive ? .controlAccentColor : .black) .opacity( isPressingClose diff --git a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift index 30bdc1bca7..da89956708 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarItemView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarItemView.swift @@ -26,9 +26,7 @@ struct TabBarItemView: View { @EnvironmentObject private var tabManager: TabManager - /// User preferences. - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings /// Is cursor hovering over the entire tab. @State @@ -112,7 +110,7 @@ struct TabBarItemView: View { func closeAction() { isAppeared = false withAnimation( - .easeOut(duration: prefs.preferences.general.tabBarStyle == .native ? 0.15 : 0.20) + .easeOut(duration: settings.general.tabBarStyle == .native ? 0.15 : 0.20) ) { tabgroup.closeTab(item: item) } @@ -140,16 +138,16 @@ struct TabBarItemView: View { TabDivider() .opacity( (isActive || inHoldingState) - && prefs.preferences.general.tabBarStyle == .xcode ? 0.0 : 1.0 + && settings.general.tabBarStyle == .xcode ? 0.0 : 1.0 ) - .padding(.top, isActive && prefs.preferences.general.tabBarStyle == .native ? 1.22 : 0) + .padding(.top, isActive && settings.general.tabBarStyle == .native ? 1.22 : 0) // Tab content (icon and text). HStack(alignment: .center, spacing: 5) { Image(systemName: item.systemImage) .resizable() .aspectRatio(contentMode: .fit) .foregroundColor( - prefs.preferences.general.fileIconStyle == .color + settings.general.fileIconStyle == .color && activeState != .inactive && isActiveTabGroup ? item.iconColor : .secondary @@ -165,11 +163,11 @@ struct TabBarItemView: View { } .frame( // To horizontally max-out the given width area ONLY in native tab bar style. - maxWidth: prefs.preferences.general.tabBarStyle == .native ? .infinity : nil, + maxWidth: settings.general.tabBarStyle == .native ? .infinity : nil, // To max-out the parent (tab bar) area. maxHeight: .infinity ) - .padding(.horizontal, prefs.preferences.general.tabBarStyle == .native ? 28 : 23) + .padding(.horizontal, settings.general.tabBarStyle == .native ? 28 : 23) .overlay { ZStack { if isActive { @@ -218,31 +216,31 @@ struct TabBarItemView: View { ? 1.0 : ( isActive - ? (prefs.preferences.general.tabBarStyle == .xcode ? 0.6 : 0.35) - : (prefs.preferences.general.tabBarStyle == .xcode ? 0.4 : 0.55) + ? (settings.general.tabBarStyle == .xcode ? 0.6 : 0.35) + : (settings.general.tabBarStyle == .xcode ? 0.4 : 0.55) ) ) TabDivider() .opacity( (isActive || inHoldingState) - && prefs.preferences.general.tabBarStyle == .xcode ? 0.0 : 1.0 + && settings.general.tabBarStyle == .xcode ? 0.0 : 1.0 ) - .padding(.top, isActive && prefs.preferences.general.tabBarStyle == .native ? 1.22 : 0) + .padding(.top, isActive && settings.general.tabBarStyle == .native ? 1.22 : 0) } .overlay(alignment: .top) { // Only show NativeTabShadow when `tabBarStyle` is native and this tab is not active. TabBarTopDivider() - .opacity(prefs.preferences.general.tabBarStyle == .native && !isActive ? 1 : 0) + .opacity(settings.general.tabBarStyle == .native && !isActive ? 1 : 0) } .foregroundColor( isActive && isActiveTabGroup ? ( - prefs.preferences.general.tabBarStyle == .xcode && colorScheme != .dark + settings.general.tabBarStyle == .xcode && colorScheme != .dark ? Color(nsColor: .controlAccentColor) : .primary ) : ( - prefs.preferences.general.tabBarStyle == .xcode + settings.general.tabBarStyle == .xcode ? .primary : .secondary ) @@ -267,7 +265,7 @@ struct TabBarItemView: View { content } .background { - if prefs.preferences.general.tabBarStyle == .xcode { + if settings.general.tabBarStyle == .xcode { TabBarItemBackground(isActive: isActive, isPressing: isPressing, isDragging: isDragging) .animation(.easeInOut(duration: 0.08), value: isHovering) } else { @@ -306,29 +304,29 @@ struct TabBarItemView: View { ) .padding( // This padding is to avoid background color overlapping with top divider. - .top, prefs.preferences.general.tabBarStyle == .xcode ? 1 : 0 + .top, settings.general.tabBarStyle == .xcode ? 1 : 0 ) // .offset( -// x: isAppeared || prefs.preferences.general.tabBarStyle == .native ? 0 : -14, +// x: isAppeared || settings.general.tabBarStyle == .native ? 0 : -14, // y: 0 // ) // .opacity(isAppeared && onDragTabId != item.id ? 1.0 : 0.0) .zIndex( isActive - ? (prefs.preferences.general.tabBarStyle == .native ? -1 : 2) + ? (settings.general.tabBarStyle == .native ? -1 : 2) : (isDragging ? 3 : (isPressing ? 1 : 0)) ) .frame( width: ( // Constrain the width of tab bar item for native tab style only. - prefs.preferences.general.tabBarStyle == .native + settings.general.tabBarStyle == .native ? max(expectedWidth.isFinite ? expectedWidth : 0, 0) : nil ) ) .onAppear { withAnimation( - .easeOut(duration: prefs.preferences.general.tabBarStyle == .native ? 0.15 : 0.20) + .easeOut(duration: settings.general.tabBarStyle == .native ? 0.15 : 0.20) ) { // isAppeared = true } diff --git a/CodeEdit/Features/Tabs/Views/TabBarView.swift b/CodeEdit/Features/Tabs/Views/TabBarView.swift index 26333ff263..bdb79dca5d 100644 --- a/CodeEdit/Features/Tabs/Views/TabBarView.swift +++ b/CodeEdit/Features/Tabs/Views/TabBarView.swift @@ -40,9 +40,7 @@ struct TabBarView: View { @Environment(\.splitEditor) var splitEditor - /// The app preference. - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings /// The tab id of current dragging tab. /// @@ -352,7 +350,7 @@ struct TabBarView: View { } } // This padding is to hide dividers at two ends under the accessory view divider. - .padding(.horizontal, prefs.preferences.general.tabBarStyle == .native ? -1 : 0) + .padding(.horizontal, settings.general.tabBarStyle == .native ? -1 : 0) .onAppear { openedTabs = tabgroup.tabs.map(\.id) // On view appeared, compute the initial expected width for tabs. @@ -365,7 +363,7 @@ struct TabBarView: View { updateForTabCountChange(geometryProxy: geometryProxy) } else { withAnimation( - .easeOut(duration: prefs.preferences.general.tabBarStyle == .native ? 0.15 : 0.20) + .easeOut(duration: settings.general.tabBarStyle == .native ? 0.15 : 0.20) ) { updateForTabCountChange(geometryProxy: geometryProxy) } @@ -406,13 +404,13 @@ struct TabBarView: View { // To fill up the parent space of tab bar. .frame(maxWidth: .infinity) .background { - if prefs.preferences.general.tabBarStyle == .native { + if settings.general.tabBarStyle == .native { TabBarNativeInactiveBackground() } } } .background { - if prefs.preferences.general.tabBarStyle == .native { + if settings.general.tabBarStyle == .native { TabBarAccessoryNativeBackground(dividerAt: .none) } } @@ -423,12 +421,12 @@ struct TabBarView: View { .frame(height: TabBarView.height) .overlay(alignment: .top) { // When tab bar style is `xcode`, we put the top divider as an overlay. - if prefs.preferences.general.tabBarStyle == .xcode { + if settings.general.tabBarStyle == .xcode { TabBarTopDivider() } } .background { - if prefs.preferences.general.tabBarStyle == .native { + if settings.general.tabBarStyle == .native { TabBarNativeMaterial() .edgesIgnoringSafeArea(.top) } else { @@ -529,7 +527,7 @@ struct TabBarView: View { .opacity(activeState != .inactive ? 1.0 : 0.5) .frame(maxHeight: .infinity) // Fill out vertical spaces. .background { - if prefs.preferences.general.tabBarStyle == .native { + if settings.general.tabBarStyle == .native { TabBarAccessoryNativeBackground(dividerAt: .trailing) } } @@ -553,11 +551,11 @@ struct TabBarView: View { .help("Enable Code Review") splitviewButton } - .padding(.horizontal, 7) + .padding(.horizontal, 5) .opacity(activeState != .inactive ? 1.0 : 0.5) .frame(maxHeight: .infinity) // Fill out vertical spaces. .background { - if prefs.preferences.general.tabBarStyle == .native { + if settings.general.tabBarStyle == .native { TabBarAccessoryNativeBackground(dividerAt: .leading) } } diff --git a/CodeEdit/Features/TerminalEmulator/TerminalEmulatorView.swift b/CodeEdit/Features/TerminalEmulator/TerminalEmulatorView.swift index 3ee5b3f7a8..b1a637478a 100644 --- a/CodeEdit/Features/TerminalEmulator/TerminalEmulatorView.swift +++ b/CodeEdit/Features/TerminalEmulator/TerminalEmulatorView.swift @@ -16,8 +16,7 @@ import SwiftTerm /// for use in SwiftUI. /// struct TerminalEmulatorView: NSViewRepresentable { - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings @StateObject private var themeModel: ThemeModel = .shared @@ -30,12 +29,22 @@ struct TerminalEmulatorView: NSViewRepresentable { private let systemFont: NSFont = .monospacedSystemFont(ofSize: 11, weight: .medium) private var font: NSFont { - if !prefs.preferences.terminal.font.customFont { - return systemFont.withSize(CGFloat(prefs.preferences.terminal.font.size)) + if settings.terminal.useTextEditorFont { + if !settings.textEditing.font.customFont { + return systemFont.withSize(CGFloat(settings.textEditing.font.size)) + } + return NSFont( + name: settings.textEditing.font.name, + size: CGFloat(settings.textEditing.font.size) + ) ?? systemFont + } + + if !settings.terminal.font.customFont { + return systemFont.withSize(CGFloat(settings.terminal.font.size)) } return NSFont( - name: prefs.preferences.terminal.font.name, - size: CGFloat(prefs.preferences.terminal.font.size) + name: settings.terminal.font.name, + size: CGFloat(settings.terminal.font.size) ) ?? systemFont } @@ -63,7 +72,7 @@ struct TerminalEmulatorView: NSViewRepresentable { /// return String(cString: pwd.pw_shell) /// ``` private func getShell() -> String { - switch prefs.preferences.terminal.shell { + switch settings.terminal.shell { case .system: return autoDetectDefaultShell() case .bash: @@ -74,8 +83,8 @@ struct TerminalEmulatorView: NSViewRepresentable { } private func getTerminalCursor() -> CursorStyle { - let blink = prefs.preferences.terminal.cursorBlink - switch prefs.preferences.terminal.cursorStyle { + let blink = settings.terminal.cursorBlink + switch settings.terminal.cursorStyle { case .block: return blink ? .blinkBlock : .steadyBlock case .underline: @@ -102,12 +111,14 @@ struct TerminalEmulatorView: NSViewRepresentable { /// Returns true if the `option` key should be treated as the `meta` key. private var optionAsMeta: Bool { - prefs.preferences.terminal.optionAsMeta + settings.terminal.optionAsMeta } /// Returns the mapped array of `SwiftTerm.Color` objects of ANSI Colors private var colors: [SwiftTerm.Color] { - if let selectedTheme = themeModel.selectedTheme, + if let selectedTheme = Settings[\.theme].matchAppearance && Settings[\.terminal].darkAppearance + ? themeModel.selectedDarkTheme + : themeModel.selectedTheme, let index = themeModel.themes.firstIndex(of: selectedTheme) { return themeModel.themes[index].terminal.ansiColors.map { color in SwiftTerm.Color(hex: color) @@ -118,7 +129,9 @@ struct TerminalEmulatorView: NSViewRepresentable { /// Returns the `cursor` color of the selected theme private var cursorColor: NSColor { - if let selectedTheme = themeModel.selectedTheme, + if let selectedTheme = Settings[\.theme].matchAppearance && Settings[\.terminal].darkAppearance + ? themeModel.selectedDarkTheme + : themeModel.selectedTheme, let index = themeModel.themes.firstIndex(of: selectedTheme) { return NSColor(themeModel.themes[index].terminal.cursor.swiftColor) } @@ -127,7 +140,9 @@ struct TerminalEmulatorView: NSViewRepresentable { /// Returns the `selection` color of the selected theme private var selectionColor: NSColor { - if let selectedTheme = themeModel.selectedTheme, + if let selectedTheme = Settings[\.theme].matchAppearance && Settings[\.terminal].darkAppearance + ? themeModel.selectedDarkTheme + : themeModel.selectedTheme, let index = themeModel.themes.firstIndex(of: selectedTheme) { return NSColor(themeModel.themes[index].terminal.selection.swiftColor) } @@ -136,7 +151,9 @@ struct TerminalEmulatorView: NSViewRepresentable { /// Returns the `text` color of the selected theme private var textColor: NSColor { - if let selectedTheme = themeModel.selectedTheme, + if let selectedTheme = Settings[\.theme].matchAppearance && Settings[\.terminal].darkAppearance + ? themeModel.selectedDarkTheme + : themeModel.selectedTheme, let index = themeModel.themes.firstIndex(of: selectedTheme) { return NSColor(themeModel.themes[index].terminal.text.swiftColor) } @@ -145,17 +162,13 @@ struct TerminalEmulatorView: NSViewRepresentable { /// Returns the `background` color of the selected theme private var backgroundColor: NSColor { - if let selectedTheme = themeModel.selectedTheme, - let index = themeModel.themes.firstIndex(of: selectedTheme) { - return NSColor(themeModel.themes[index].terminal.background.swiftColor) - } - return .windowBackgroundColor + return .clear } /// returns a `NSAppearance` based on the user setting of the terminal appearance, /// `nil` if app default is not overridden private var colorAppearance: NSAppearance? { - if prefs.preferences.terminal.darkAppearance { + if settings.terminal.darkAppearance { return .init(named: .darkAqua) } return nil @@ -187,7 +200,7 @@ struct TerminalEmulatorView: NSViewRepresentable { terminal.caretColor = cursorColor terminal.selectedTextBackgroundColor = selectionColor terminal.nativeForegroundColor = textColor - terminal.nativeBackgroundColor = prefs.preferences.terminal.useThemeBackground ? backgroundColor : .clear + terminal.nativeBackgroundColor = settings.terminal.useThemeBackground ? backgroundColor : .clear terminal.cursorStyleChanged(source: terminal.getTerminal(), newStyle: getTerminalCursor()) terminal.layer?.backgroundColor = .clear terminal.optionAsMetaKey = optionAsMeta @@ -215,7 +228,7 @@ struct TerminalEmulatorView: NSViewRepresentable { view.caretColor = cursorColor view.selectedTextBackgroundColor = selectionColor view.nativeForegroundColor = textColor - view.nativeBackgroundColor = prefs.preferences.terminal.useThemeBackground ? backgroundColor : .clear + view.nativeBackgroundColor = settings.terminal.useThemeBackground ? backgroundColor : .clear view.layer?.backgroundColor = .clear view.optionAsMetaKey = optionAsMeta view.cursorStyleChanged(source: view.getTerminal(), newStyle: getTerminalCursor()) diff --git a/CodeEdit/Features/Welcome/Views/WelcomeView.swift b/CodeEdit/Features/Welcome/Views/WelcomeView.swift index cd9e82ce14..847755c8f8 100644 --- a/CodeEdit/Features/Welcome/Views/WelcomeView.swift +++ b/CodeEdit/Features/Welcome/Views/WelcomeView.swift @@ -13,8 +13,7 @@ struct WelcomeView: View { @Environment(\.colorScheme) var colorScheme - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings @State private var repoPath = "~/" @@ -50,9 +49,9 @@ struct WelcomeView: View { private var showWhenLaunchedBinding: Binding { Binding { - prefs.preferences.general.reopenBehavior == .welcome + settings.general.reopenBehavior == .welcome } set: { new in - prefs.preferences.general.reopenBehavior = new ? .welcome : .openPanel + settings.general.reopenBehavior = new ? .welcome : .openPanel } } diff --git a/CodeEdit/Features/WindowCommands/MainCommands.swift b/CodeEdit/Features/WindowCommands/MainCommands.swift index 4f7d902923..d93346c67e 100644 --- a/CodeEdit/Features/WindowCommands/MainCommands.swift +++ b/CodeEdit/Features/WindowCommands/MainCommands.swift @@ -25,14 +25,9 @@ struct MainCommands: Commands { CommandGroup(replacing: .appSettings) { Button("Settings...") { - NSApp.sendAction(#selector(AppDelegate.openPreferences(_:)), to: nil, from: nil) + openWindow(id: "settings") } .keyboardShortcut(",") - - Button("New Settings...") { - NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) - } - .keyboardShortcut(",", modifiers: [.command, .option, .hidden]) } } } diff --git a/CodeEdit/Features/WindowCommands/ViewCommands.swift b/CodeEdit/Features/WindowCommands/ViewCommands.swift index df6ddeb988..29f18d082e 100644 --- a/CodeEdit/Features/WindowCommands/ViewCommands.swift +++ b/CodeEdit/Features/WindowCommands/ViewCommands.swift @@ -8,8 +8,7 @@ import SwiftUI struct ViewCommands: Commands { - @ObservedObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings @State var windowController: CodeEditWindowController? @@ -33,20 +32,20 @@ struct ViewCommands: Commands { Button("Increase font size") { if CodeEditDocumentController.shared.documents.count > 1 { - prefs.preferences.textEditing.font.size += 1 + settings.textEditing.font.size += 1 } - prefs.preferences.terminal.font.size += 1 + settings.terminal.font.size += 1 } .keyboardShortcut("+") Button("Decrease font size") { if CodeEditDocumentController.shared.documents.count > 1 { - if !(prefs.preferences.textEditing.font.size <= 1) { - prefs.preferences.textEditing.font.size -= 1 + if !(settings.textEditing.font.size <= 1) { + settings.textEditing.font.size -= 1 } } - if !(prefs.preferences.terminal.font.size <= 1) { - prefs.preferences.terminal.font.size -= 1 + if !(settings.terminal.font.size <= 1) { + settings.terminal.font.size -= 1 } } .keyboardShortcut("-") diff --git a/CodeEdit/Utils/Protocols/Loopable.swift b/CodeEdit/Utils/Protocols/Loopable.swift index 256c846d99..85a8af3e5b 100644 --- a/CodeEdit/Utils/Protocols/Loopable.swift +++ b/CodeEdit/Utils/Protocols/Loopable.swift @@ -1,6 +1,6 @@ // // Loopable.swift -// CodeEditModules/AppPreferences +// CodeEditModules/Settings // // Created by Lukas Pistrol on 03.04.22. // diff --git a/CodeEdit/WindowObserver.swift b/CodeEdit/WindowObserver.swift index 7f7f5e6d12..9129615c17 100644 --- a/CodeEdit/WindowObserver.swift +++ b/CodeEdit/WindowObserver.swift @@ -19,8 +19,7 @@ struct WindowObserver: View { @State private var isFullscreen = false - @StateObject - private var prefs: AppPreferencesModel = .shared + @AppSettings var settings @State var modifierFlags: NSEvent.ModifierFlags = [] @@ -39,7 +38,7 @@ struct WindowObserver: View { self.isFullscreen = false } // When tab bar style is changed, update NSWindow configuration as follows. - .onChange(of: prefs.preferences.general.tabBarStyle) { newStyle in + .onChange(of: settings.general.tabBarStyle) { newStyle in DispatchQueue.main.async { if newStyle == .native { window.titlebarSeparatorStyle = .none diff --git a/CodeEdit/WorkspaceView.swift b/CodeEdit/WorkspaceView.swift index bd6a7c79f0..5ac27b6729 100644 --- a/CodeEdit/WorkspaceView.swift +++ b/CodeEdit/WorkspaceView.swift @@ -20,7 +20,7 @@ struct WorkspaceView: View { private var tabManager: TabManager @StateObject - private var prefs: AppPreferencesModel = .shared + private var prefs: Settings = .shared @Environment(\.window) private var window diff --git a/CodeEditTests/Features/CodeEditUI/CodeEditUITests.swift b/CodeEditTests/Features/CodeEditUI/CodeEditUITests.swift index bae0d502f5..d72e55795a 100644 --- a/CodeEditTests/Features/CodeEditUI/CodeEditUITests.swift +++ b/CodeEditTests/Features/CodeEditUI/CodeEditUITests.swift @@ -65,24 +65,6 @@ final class CodeEditUIUnitTests: XCTestCase { assertSnapshot(matching: hosting, as: .image) } - // MARK: FontPickerView - - func testFontPickerViewLight() throws { - let view = FontPicker("Font", name: .constant("SF-Mono"), size: .constant(13)) - let hosting = NSHostingView(rootView: view) - hosting.appearance = .init(named: .aqua) - hosting.frame = CGRect(origin: .zero, size: .init(width: 120, height: 30)) - assertSnapshot(matching: hosting, as: .image) - } - - func testFontPickerViewDark() throws { - let view = FontPicker("Font", name: .constant("SF-Mono"), size: .constant(13)) - let hosting = NSHostingView(rootView: view) - hosting.appearance = .init(named: .darkAqua) - hosting.frame = CGRect(origin: .zero, size: .init(width: 120, height: 30)) - assertSnapshot(matching: hosting, as: .image) - } - // MARK: EffectView func testEffectViewLight() throws { diff --git a/Documentation.docc/AppPreferences/AppPreferences.md b/Documentation.docc/AppPreferences/AppPreferences.md index 68c21c6f86..0acaafea7a 100644 --- a/Documentation.docc/AppPreferences/AppPreferences.md +++ b/Documentation.docc/AppPreferences/AppPreferences.md @@ -1,4 +1,4 @@ -# ``CodeEdit/AppPreferences`` +# ``CodeEdit/Settings`` ## Topics @@ -9,7 +9,7 @@ ### Preferences Model -- ``AppPreferencesModel`` +- ``SettingsModel`` - ``SoftwareUpdater`` ### Preferences Section Views diff --git a/Documentation.docc/AppPreferences/Create a View.md b/Documentation.docc/AppPreferences/Create a View.md index 1b571006c7..a7ef618908 100644 --- a/Documentation.docc/AppPreferences/Create a View.md +++ b/Documentation.docc/AppPreferences/Create a View.md @@ -4,45 +4,72 @@ Now that you followed the guide it's time to create a view ## Add Option to existing Section -In our example we added `ourNewOption` in ``AppPreferences/GeneralPreferences``. +In our example we added `ourNewOption` in ``Settings/GeneralPreferences``. Now let's take a look at the ``GeneralPreferencesView``. ```swift import SwiftUI -public struct PreferencesGeneralView: View { +struct GeneralPreferencesView: View { - @StateObject - private var prefs: AppPreferencesModel = .shared + // MARK: - View - public init() {} + init() {} - public var body: some View { + var body: some View { PreferencesContent { - PreferencesSection("Appearance") { - // ... - } - PreferencesSection("File Icon Style") { - // ... - } - PreferencesSection("Reopen Behavior") { - // ... - } + appearanceSection + showIssuesSection + fileExtensionsSection } } + + @AppSettings var settings } ``` -As you can see ``AppPreferencesModel`` is already setup and ready to use. -Note that in order to align all the options in a nice and uniform way we wrap them into a -``PreferencesContent`` and individual ``PreferencesSection`` views. +As you can see ``SettingsModel`` is already setup and ready to use. To add your option toggle below the other options just add something like this: ```swift -PreferencesSection("Reopen Behavior") { - Toggle("Your new Option", value: $prefs.preferences.general.yourNewOption) +private extension GeneralPreferencesView { + + // MARK: - Sections + + private var yourOptionSection: some View { + PreferencesSection("Your Option") { + yourOption + } + } + + // MARK: - Preferences View + + private var yourOption: some View { + Toggle("Your text", isOn: $settings.general.yourNewOption) + } +} +``` + +Then add it to `var body: some View` + +```swift +struct GeneralPreferencesView: View { + + // MARK: - View + + init() {} + + var body: some View { + PreferencesContent { + appearanceSection + showIssuesSection + fileExtensionsSection + // REMOVEME: et cetera + yourOptionSection + } + } } ``` @@ -50,33 +77,96 @@ And now you're done! ## Implement new Section +> Tip: Rename YourSection to the section name that you want + To implement a new section first create a new folder inside the `Sections` folder and name it accordingly. -In this example we might want to name it "AdvancedPreferences". +Inside the folder create a new SwiftUI view and name it "PreferencesYourSectionView.swift". -Inside the folder create a new SwiftUI view and name it "PreferencesAdvancedView.swift". +Then find `VenturaPreferences.swift` by searching in the filter field at the bottom of the file explorer, then create a new page for your view like so: -Then go to the main `CodeEdit` target and open the "AppDelegate.swift" and scroll down to find the `private lazy var preferencesWindowController` instance. Search for the `Pane` titled "Advanced" and replace `PreferencesPlaceholderView()` with your newly created `PreferencesAdvancedView()`. +> Tip: The order that pages are arranged in the array is the same as in the settings window, the first array member will be the top item -Back in "PreferencesAdvancedView.swift" implement your option like that: +```swift +private static let pages: [Page] = [ + .init(.appPreferencesSection, children: [ + .init( + .generalPreferences, + icon: .init( + baseColor: Colors().gray, + systemName: "gear", + icon: .system("gear") + ) + ), + .init( + .yourSection, + icon: .init( + baseColor: Colors().yourColor + systemName: "// REMOVEME: Find an SF Symbol that is similar to the icon you imagined" + icon: .system("// REMOVEME: Find an SF Symbol that is similar to the icon you imagined") + ) + ) + ] +] +``` + +Then find the file `Page.swift` and add `YourSection` to the `enum Name` like this: + +```swift +enum Name: String { + case appPreferencesSection = "App Preferences" + + case generalPreferences = "General" + case advancedPreferences = "Advanced" + // REMOVEME: et cetera + case yourSection = "YourSection" +} +``` + +Back in `YourSectionView.swift` implement your option like this: ```swift import SwiftUI -public struct PreferencesAdvancedView: View { +struct YourSectionPreferencesView: View { + var body: some View { + yourToggleSection + } @StateObject - private var prefs: AppPreferencesModel = .shared + private var prefs: SettingsModel = .shared public init() {} +} - public var body: some View { - PreferencesContent { - PreferencesSection("Reopen Behavior") { - Toggle("Your new Option", value: $prefs.preferences.general.yourNewOption) - } - } +private extension YourSectionPreferencesView { + + // MARK: - Sections + + private var yourToggleSection: some View { + yourToggle + } + + // MARK: - Preferences Views + + private var yourToggle: some View { + Toggle("Your option", isOn: $settings.general.yourNewOption) } } ``` +When you are done, add `YourSectionView` to `VenturaPreferences.swift`: + +```swift +if selectedPage?.name != nil { + // Can force un-wrap because we just checked if it was nil + switch selectedPage!.name { + case .generalPreferences: + GeneralPreferencesView() + .environmentObject(updater) + case .themePreferences: + ThemePreferencesView() + // REMOVEME: et cetera + case .yourSection: + YourSectionPreferencesView() +``` diff --git a/Documentation.docc/AppPreferences/Getting Started.md b/Documentation.docc/AppPreferences/Getting Started.md index a376896c75..5ec56e8dce 100644 --- a/Documentation.docc/AppPreferences/Getting Started.md +++ b/Documentation.docc/AppPreferences/Getting Started.md @@ -1,22 +1,21 @@ # Getting Started -There are a few things to consider when using the ``AppPreferences``. +There are a few things to consider when using the ``Settings``. ## Reading/Writing Values The Preferences can be accessed from everywhere in the app like this: ```swift -import AppPreferences +import Settings -@StateObject -private var prefs: AppPreferencesModel = .shared +@AppSettings var settings ``` Since it is a `@StateObject` we can be sure to always get up-to-date information and we can easily bind to the individual properties like this: ```swift -Toggle("Enable some Feature", value: $prefs.preferences.someFeature.isEnabled) +Toggle("Enable some Feature", value: $settings.someFeature.isEnabled) ``` ## Creating a New Preference @@ -27,7 +26,7 @@ When implementing a new feature, we might have some options in regards to this n The preferences window is structured in different sections. Figure out in which section your new option should appear in. -If the section is already populated with other options (e.g. ``AppPreferences/GeneralPreferences``), just add your new option like this: +If the section is already populated with other options (e.g. ``Settings/GeneralPreferences``), just add your new option like this: ```swift struct GeneralPreferences: Codable { @@ -55,7 +54,7 @@ struct GeneralPreferences: Codable { ### Create a Section In some cases in early development the section you decided on where to put your option in might not yet have been implemented. In this -case you can create a new `struct` inside ``AppPreferences`` like this: +case you can create a new `struct` inside ``Settings`` like this: ```swift public extension YourNewSection: Codable { @@ -77,10 +76,10 @@ public extension YourNewSection: Codable { } ``` -Now let's add the new section to ``AppPreferences`` like this: +Now let's add the new section to ``Settings`` like this: ```swift -public struct AppPreferences: Codable { +public struct Settings: Codable { // ... // Add your new section above the `public init() {}` @@ -98,5 +97,5 @@ public struct AppPreferences: Codable { ### Main Components -- ``AppPreferences`` -- ``AppPreferencesModel`` +- ``Settings`` +- ``SettingsModel`` diff --git a/Documentation.docc/AppPreferences/Sections/AccountPreferencesView.md b/Documentation.docc/AppPreferences/Sections/AccountPreferencesView.md index 8e55ebcab1..492ea7b897 100644 --- a/Documentation.docc/AppPreferences/Sections/AccountPreferencesView.md +++ b/Documentation.docc/AppPreferences/Sections/AccountPreferencesView.md @@ -4,7 +4,7 @@ ### Model -- ``AppPreferences/AccountsPreferences`` +- ``Settings/AccountsPreferences`` - ``SourceControlAccounts`` - ``SourceControlProvider`` diff --git a/Documentation.docc/AppPreferences/Sections/GeneralPreferencesView.md b/Documentation.docc/AppPreferences/Sections/GeneralPreferencesView.md index 454b97049b..e0e6e56d91 100644 --- a/Documentation.docc/AppPreferences/Sections/GeneralPreferencesView.md +++ b/Documentation.docc/AppPreferences/Sections/GeneralPreferencesView.md @@ -4,4 +4,4 @@ ### Model -- ``AppPreferences/GeneralPreferences`` +- ``Settings/GeneralPreferences`` diff --git a/Documentation.docc/AppPreferences/Sections/KeybindingsPreferencesView.md b/Documentation.docc/AppPreferences/Sections/KeybindingsPreferencesView.md index e770594fe3..e50a4dac0c 100644 --- a/Documentation.docc/AppPreferences/Sections/KeybindingsPreferencesView.md +++ b/Documentation.docc/AppPreferences/Sections/KeybindingsPreferencesView.md @@ -4,4 +4,4 @@ ### Model -- ``AppPreferences/KeybindingsPreferences`` +- ``Settings/KeybindingsPreferences`` diff --git a/Documentation.docc/AppPreferences/Sections/SourceControlPreferencesView.md b/Documentation.docc/AppPreferences/Sections/SourceControlPreferencesView.md index aa16c1588c..1b42648819 100644 --- a/Documentation.docc/AppPreferences/Sections/SourceControlPreferencesView.md +++ b/Documentation.docc/AppPreferences/Sections/SourceControlPreferencesView.md @@ -4,7 +4,7 @@ ### Model -- ``AppPreferences/SourceControlPreferences`` +- ``Settings/SourceControlPreferences`` - ``IgnoredFiles`` ### Views diff --git a/Documentation.docc/AppPreferences/Sections/TerminalPreferencesView.md b/Documentation.docc/AppPreferences/Sections/TerminalPreferencesView.md index 9ce992fc87..2a10ec3d55 100644 --- a/Documentation.docc/AppPreferences/Sections/TerminalPreferencesView.md +++ b/Documentation.docc/AppPreferences/Sections/TerminalPreferencesView.md @@ -4,4 +4,4 @@ ### Model -- ``AppPreferences/TerminalPreferences`` +- ``Settings/TerminalPreferences`` diff --git a/Documentation.docc/AppPreferences/Sections/TextEditingPreferencesView.md b/Documentation.docc/AppPreferences/Sections/TextEditingPreferencesView.md index 6f9e5bd066..f44302e503 100644 --- a/Documentation.docc/AppPreferences/Sections/TextEditingPreferencesView.md +++ b/Documentation.docc/AppPreferences/Sections/TextEditingPreferencesView.md @@ -4,4 +4,4 @@ ### Model -- ``AppPreferences/TextEditingPreferences`` +- ``Settings/TextEditingPreferences`` diff --git a/Documentation.docc/AppPreferences/Sections/ThemePreferencesView.md b/Documentation.docc/AppPreferences/Sections/ThemePreferencesView.md index bf9a7091af..6587e91b35 100644 --- a/Documentation.docc/AppPreferences/Sections/ThemePreferencesView.md +++ b/Documentation.docc/AppPreferences/Sections/ThemePreferencesView.md @@ -4,7 +4,7 @@ ### Model -- ``AppPreferences/ThemePreferences`` +- ``Settings/ThemePreferences`` - ``ThemeModel`` ### Views diff --git a/Documentation.docc/CodeEditUI/CodeEditUI.md b/Documentation.docc/CodeEditUI/CodeEditUI.md index d1f844f31e..71f8b89de6 100644 --- a/Documentation.docc/CodeEditUI/CodeEditUI.md +++ b/Documentation.docc/CodeEditUI/CodeEditUI.md @@ -11,7 +11,6 @@ This module contains UI elements that can be reused throughout the `CodeEdit` ap ### Controls - ``ToolbarBranchPicker`` -- ``FontPicker`` - ``HelpButton`` - ``SegmentedControl`` - ``SettingsTextEditor`` diff --git a/Documentation.docc/CodeEditUI/FontPicker.md b/Documentation.docc/CodeEditUI/FontPicker.md deleted file mode 100644 index 3029df3f9b..0000000000 --- a/Documentation.docc/CodeEditUI/FontPicker.md +++ /dev/null @@ -1,24 +0,0 @@ -# ``CodeEdit/FontPicker`` - -## Overview - -Opens a [`NSFontPanel`](https://developer.apple.com/documentation/appkit/nsfontpanel) and binds the `fontSize` and `fontName` parameters to the provided variables. - -## Usage - -```swift -@State var fontName: String = "SF-Mono" -@State var fontSize: Int = 13 - -FontPicker("Font Picker", name: $fontName, size: $fontSize) -``` - -## Preview - -![FontPicker](FontPicker_View.png) - -## Topics - -### Delegate - -- ``FontPickerDelegate`` diff --git a/Documentation.docc/Documentation.md b/Documentation.docc/Documentation.md index 79d390ce39..904eb07206 100644 --- a/Documentation.docc/Documentation.md +++ b/Documentation.docc/Documentation.md @@ -6,9 +6,9 @@ - -### AppPreferences +### Settings -- ``AppPreferences`` +- ``Settings`` ### App Window