// Copyright (c) 2014-2020 Sebastien Rombauts (sebastien.rombauts@gmail.com) // // Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt // or copy at http://opensource.org/licenses/MIT) #include "SGitSourceControlSettings.h" #include "Fonts/SlateFontInfo.h" #include "Misc/App.h" #include "Misc/FileHelper.h" #include "Misc/Paths.h" #include "Modules/ModuleManager.h" #include "Styling/SlateTypes.h" #include "Widgets/SBoxPanel.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SEditableTextBox.h" #include "Widgets/Input/SFilePathPicker.h" #include "Widgets/Input/SMultiLineEditableTextBox.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SSeparator.h" #include "Widgets/Notifications/SNotificationList.h" #include "Framework/Notifications/NotificationManager.h" #include "EditorDirectories.h" #include "EditorStyleSet.h" #include "SourceControlOperations.h" #include "GitSourceControlModule.h" #include "GitSourceControlUtils.h" #define LOCTEXT_NAMESPACE "SGitSourceControlSettings" void SGitSourceControlSettings::Construct(const FArguments& InArgs) { const FSlateFontInfo Font = FEditorStyle::GetFontStyle(TEXT("SourceControl.LoginWindow.Font")); bAutoCreateGitIgnore = true; bAutoCreateReadme = true; bAutoCreateGitAttributes = false; bAutoInitialCommit = true; InitialCommitMessage = LOCTEXT("InitialCommitMessage", "Initial commit"); const FText FileFilterType = NSLOCTEXT("GitSourceControl", "Executables", "Executables"); #if PLATFORM_WINDOWS const FString FileFilterText = FString::Printf(TEXT("%s (*.exe)|*.exe"), *FileFilterType.ToString()); #else const FString FileFilterText = FString::Printf(TEXT("%s"), *FileFilterType.ToString()); #endif ReadmeContent = FText::FromString(FString(TEXT("# ")) + FApp::GetProjectName() + "\n\nDeveloped with Unreal Engine 4\n"); ChildSlot [ SNew(SBorder) .BorderImage( FEditorStyle::GetBrush("DetailsView.CategoryBottom")) .Padding(FMargin(0.0f, 3.0f, 0.0f, 0.0f)) [ SNew(SVerticalBox) // Path to the Git command line executable +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .ToolTipText(LOCTEXT("BinaryPathLabel_Tooltip", "Path to Git binary")) +SHorizontalBox::Slot() .FillWidth(1.0f) [ SNew(STextBlock) .Text(LOCTEXT("BinaryPathLabel", "Git Path")) .Font(Font) ] +SHorizontalBox::Slot() .FillWidth(2.0f) [ SNew(SFilePathPicker) .BrowseButtonImage(FEditorStyle::GetBrush("PropertyWindow.Button_Ellipsis")) .BrowseButtonStyle(FEditorStyle::Get(), "HoverHintOnly") .BrowseDirectory(FEditorDirectories::Get().GetLastDirectory(ELastDirectory::GENERIC_OPEN)) .BrowseTitle(LOCTEXT("BinaryPathBrowseTitle", "File picker...")) .FilePath(this, &SGitSourceControlSettings::GetBinaryPathString) .FileTypeFilter(FileFilterText) .OnPathPicked(this, &SGitSourceControlSettings::OnBinaryPathPicked) ] ] // Root of the local repository +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .ToolTipText(LOCTEXT("RepositoryRootLabel_Tooltip", "Path to the root of the Git repository")) +SHorizontalBox::Slot() .FillWidth(1.0f) [ SNew(STextBlock) .Text(LOCTEXT("RepositoryRootLabel", "Root of the repository")) .Font(Font) ] +SHorizontalBox::Slot() .FillWidth(2.0f) [ SNew(STextBlock) .Text(this, &SGitSourceControlSettings::GetPathToRepositoryRoot) .Font(Font) ] ] // User Name +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .ToolTipText(LOCTEXT("GitUserName_Tooltip", "User name configured for the Git repository")) +SHorizontalBox::Slot() .FillWidth(1.0f) [ SNew(STextBlock) .Text(LOCTEXT("GitUserName", "User Name")) .Font(Font) ] +SHorizontalBox::Slot() .FillWidth(2.0f) [ SNew(STextBlock) .Text(this, &SGitSourceControlSettings::GetUserName) .Font(Font) ] ] // User e-mail +SVerticalBox::Slot() .FillHeight(1.0f) .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .ToolTipText(LOCTEXT("GitUserEmail_Tooltip", "User e-mail configured for the Git repository")) +SHorizontalBox::Slot() .FillWidth(1.0f) [ SNew(STextBlock) .Text(LOCTEXT("GitUserEmail", "E-Mail")) .Font(Font) ] +SHorizontalBox::Slot() .FillWidth(2.0f) [ SNew(STextBlock) .Text(this, &SGitSourceControlSettings::GetUserEmail) .Font(Font) ] ] // Separator +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SSeparator) ] // Explanation text +SVerticalBox::Slot() .FillHeight(1.0f) .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) +SHorizontalBox::Slot() .FillWidth(1.0f) .HAlign(HAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("RepositoryNotFound", "Current Project is not contained in a Git Repository. Fill the form below to initialize a new Repository.")) .ToolTipText(LOCTEXT("RepositoryNotFound_Tooltip", "No Repository found at the level or above the current Project")) .Font(Font) ] ] // Option to configure the URL of the default remote 'origin' // TODO: option to configure the name of the remote instead of the default origin +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) .ToolTipText(LOCTEXT("ConfigureOrigin_Tooltip", "Configure the URL of the default remote 'origin'")) +SHorizontalBox::Slot() .FillWidth(1.0f) [ SNew(STextBlock) .Text(LOCTEXT("ConfigureOrigin", "URL of the remote server 'origin'")) .Font(Font) ] +SHorizontalBox::Slot() .FillWidth(2.0f) .VAlign(VAlign_Center) [ SNew(SEditableTextBox) .Text(this, &SGitSourceControlSettings::GetRemoteUrl) .OnTextCommitted(this, &SGitSourceControlSettings::OnRemoteUrlCommited) .Font(Font) ] ] // Option to add a proper .gitignore file (true by default) +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) .ToolTipText(LOCTEXT("CreateGitIgnore_Tooltip", "Create and add a standard '.gitignore' file")) +SHorizontalBox::Slot() .FillWidth(0.1f) [ SNew(SCheckBox) .IsChecked(ECheckBoxState::Checked) .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedCreateGitIgnore) ] +SHorizontalBox::Slot() .FillWidth(2.9f) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("CreateGitIgnore", "Add a .gitignore file")) .Font(Font) ] ] // Option to add a README.md file with custom content +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) .ToolTipText(LOCTEXT("CreateReadme_Tooltip", "Add a README.md file")) +SHorizontalBox::Slot() .FillWidth(0.1f) [ SNew(SCheckBox) .IsChecked(ECheckBoxState::Checked) .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedCreateReadme) ] +SHorizontalBox::Slot() .FillWidth(0.9f) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("CreateReadme", "Add a basic README.md file")) .Font(Font) ] +SHorizontalBox::Slot() .FillWidth(2.0f) .Padding(2.0f) [ SNew(SMultiLineEditableTextBox) .Text(this, &SGitSourceControlSettings::GetReadmeContent) .OnTextCommitted(this, &SGitSourceControlSettings::OnReadmeContentCommited) .IsEnabled(this, &SGitSourceControlSettings::GetAutoCreateReadme) .SelectAllTextWhenFocused(true) .Font(Font) ] ] // Option to add a proper .gitattributes file for Git LFS (false by default) +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) .ToolTipText(LOCTEXT("CreateGitAttributes_Tooltip", "Create and add a '.gitattributes' file to enable Git LFS for the whole 'Content/' directory (needs Git LFS extensions to be installed).")) +SHorizontalBox::Slot() .FillWidth(0.1f) [ SNew(SCheckBox) .IsChecked(ECheckBoxState::Unchecked) .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedCreateGitAttributes) .IsEnabled(this, &SGitSourceControlSettings::CanInitializeGitLfs) ] +SHorizontalBox::Slot() .FillWidth(2.9f) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("CreateGitAttributes", "Add a .gitattributes file to enable Git LFS")) .Font(Font) ] ] // Option to use the Git LFS File Locking workflow (false by default) // Enabled even after init to switch it off in case of no network // TODO LFS turning it off afterwards does not work because all files are readonly ! +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .ToolTipText(LOCTEXT("UseGitLfsLocking_Tooltip", "Uses Git LFS 2 File Locking workflow (CheckOut and Commit/Push).")) +SHorizontalBox::Slot() .FillWidth(0.1f) [ SNew(SCheckBox) .IsChecked(SGitSourceControlSettings::IsUsingGitLfsLocking()) .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedUseGitLfsLocking) .IsEnabled(this, &SGitSourceControlSettings::CanUseGitLfsLocking) ] +SHorizontalBox::Slot() .FillWidth(0.9f) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("UseGitLfsLocking", "Uses Git LFS 2 File Locking workflow")) .Font(Font) ] // Username credential used to access the Git LFS 2 File Locks server +SHorizontalBox::Slot() .FillWidth(2.0f) .VAlign(VAlign_Center) [ SNew(SEditableTextBox) .Text(this, &SGitSourceControlSettings::GetLfsUserName) .OnTextCommitted(this, &SGitSourceControlSettings::OnLfsUserNameCommited) .IsEnabled(this, &SGitSourceControlSettings::GetIsUsingGitLfsLocking) .HintText(LOCTEXT("LfsUserName_Hint", "Username to lock files on the LFS server")) .Font(Font) ] ] // Option to Make the initial Git commit with custom message +SVerticalBox::Slot() .AutoHeight() .Padding(2.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) .ToolTipText(LOCTEXT("InitialGitCommit_Tooltip", "Make the initial Git commit")) +SHorizontalBox::Slot() .FillWidth(0.1f) [ SNew(SCheckBox) .IsChecked(ECheckBoxState::Checked) .OnCheckStateChanged(this, &SGitSourceControlSettings::OnCheckedInitialCommit) ] +SHorizontalBox::Slot() .FillWidth(0.9f) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("InitialGitCommit", "Make the initial Git commit")) .Font(Font) ] +SHorizontalBox::Slot() .FillWidth(2.0f) .Padding(2.0f) [ SNew(SMultiLineEditableTextBox) .Text(this, &SGitSourceControlSettings::GetInitialCommitMessage) .OnTextCommitted(this, &SGitSourceControlSettings::OnInitialCommitMessageCommited) .IsEnabled(this, &SGitSourceControlSettings::GetAutoInitialCommit) .SelectAllTextWhenFocused(true) .Font(Font) ] ] // Button to initialize the project with Git, create .gitignore/.gitattributes files, and make the first commit) +SVerticalBox::Slot() .FillHeight(2.5f) .Padding(4.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) .Visibility(this, &SGitSourceControlSettings::MustInitializeGitRepository) +SHorizontalBox::Slot() .FillWidth(1.0f) [ SNew(SButton) .Text(LOCTEXT("GitInitRepository", "Initialize project with Git")) .ToolTipText(LOCTEXT("GitInitRepository_Tooltip", "Initialize current project as a new Git repository")) .OnClicked(this, &SGitSourceControlSettings::OnClickedInitializeGitRepository) .IsEnabled(this, &SGitSourceControlSettings::CanInitializeGitRepository) .HAlign(HAlign_Center) .ContentPadding(6) ] ] ] ]; } SGitSourceControlSettings::~SGitSourceControlSettings() { RemoveInProgressNotification(); } FString SGitSourceControlSettings::GetBinaryPathString() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); return GitSourceControl.AccessSettings().GetBinaryPath(); } void SGitSourceControlSettings::OnBinaryPathPicked( const FString& PickedPath ) const { FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); FString PickedFullPath = FPaths::ConvertRelativePathToFull(PickedPath); const bool bChanged = GitSourceControl.AccessSettings().SetBinaryPath(PickedFullPath); if(bChanged) { // Re-Check provided git binary path for each change GitSourceControl.GetProvider().CheckGitAvailability(); if(GitSourceControl.GetProvider().IsGitAvailable()) { GitSourceControl.SaveSettings(); } } } FText SGitSourceControlSettings::GetPathToRepositoryRoot() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); return FText::FromString(GitSourceControl.GetProvider().GetPathToRepositoryRoot()); } FText SGitSourceControlSettings::GetUserName() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); return FText::FromString(GitSourceControl.GetProvider().GetUserName()); } FText SGitSourceControlSettings::GetUserEmail() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); return FText::FromString(GitSourceControl.GetProvider().GetUserEmail()); } EVisibility SGitSourceControlSettings::MustInitializeGitRepository() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); const bool bGitAvailable = GitSourceControl.GetProvider().IsGitAvailable(); const bool bGitRepositoryFound = GitSourceControl.GetProvider().IsEnabled(); return (bGitAvailable && !bGitRepositoryFound) ? EVisibility::Visible : EVisibility::Collapsed; } bool SGitSourceControlSettings::CanInitializeGitRepository() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); const bool bGitAvailable = GitSourceControl.GetProvider().IsGitAvailable(); const bool bGitRepositoryFound = GitSourceControl.GetProvider().IsEnabled(); const FString LfsUserName = GitSourceControl.AccessSettings().GetLfsUserName(); const bool bIsUsingGitLfsLocking = GitSourceControl.AccessSettings().IsUsingGitLfsLocking(); const bool bGitLfsConfigOk = !bIsUsingGitLfsLocking || !LfsUserName.IsEmpty(); const bool bInitialCommitConfigOk = !bAutoInitialCommit || !InitialCommitMessage.IsEmpty(); return (bGitAvailable && !bGitRepositoryFound && bGitLfsConfigOk && bInitialCommitConfigOk); } bool SGitSourceControlSettings::CanInitializeGitLfs() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); const FString& PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); const bool bGitLfsAvailable = GitSourceControl.GetProvider().GetGitVersion().bHasGitLfs; return bGitLfsAvailable; } bool SGitSourceControlSettings::CanUseGitLfsLocking() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); const bool bGitLfsLockingAvailable = GitSourceControl.GetProvider().GetGitVersion().bHasGitLfsLocking; // TODO LFS SRombauts : check if .gitattributes file is present and if Content/ is already tracked! const bool bGitAttributesCreated = true; return (bGitLfsLockingAvailable && (bAutoCreateGitAttributes || bGitAttributesCreated)); } FReply SGitSourceControlSettings::OnClickedInitializeGitRepository() { FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); const FString& PathToGitBinary = GitSourceControl.AccessSettings().GetBinaryPath(); const FString PathToProjectDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); TArray InfoMessages; TArray ErrorMessages; // 1.a. Synchronous (very quick) "git init" operation: initialize a Git local repository with a .git/ subdirectory GitSourceControlUtils::RunCommand(TEXT("init"), PathToGitBinary, PathToProjectDir, TArray(), TArray(), InfoMessages, ErrorMessages); // 1.b. Synchronous (very quick) "git remote add" operation: configure the URL of the default remote server 'origin' if specified if(!RemoteUrl.IsEmpty()) { TArray Parameters; Parameters.Add(TEXT("add origin")); Parameters.Add(RemoteUrl.ToString()); GitSourceControlUtils::RunCommand(TEXT("remote"), PathToGitBinary, PathToProjectDir, Parameters, TArray(), InfoMessages, ErrorMessages); } // Check the new repository status to enable connection (branch, user e-mail) GitSourceControl.GetProvider().CheckRepositoryStatus(PathToGitBinary); if(GitSourceControl.GetProvider().IsAvailable()) { // List of files to add to Source Control (.uproject, Config/, Content/, Source/ files and .gitignore/.gitattributes if any) TArray ProjectFiles; ProjectFiles.Add(FPaths::GetProjectFilePath()); ProjectFiles.Add(FPaths::ProjectConfigDir()); ProjectFiles.Add(FPaths::ProjectContentDir()); if (FPaths::DirectoryExists(FPaths::GameSourceDir())) { ProjectFiles.Add(FPaths::GameSourceDir()); } if(bAutoCreateGitIgnore) { // 2.a. Create a standard ".gitignore" file with common patterns for a typical Blueprint & C++ project const FString GitIgnoreFilename = FPaths::Combine(FPaths::ProjectDir(), TEXT(".gitignore")); const FString GitIgnoreContent = TEXT("Binaries\nDerivedDataCache\nIntermediate\nSaved\n.vscode\n.vs\n*.VC.db\n*.opensdf\n*.opendb\n*.sdf\n*.sln\n*.suo\n*.xcodeproj\n*.xcworkspace\n*.log"); if(FFileHelper::SaveStringToFile(GitIgnoreContent, *GitIgnoreFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) { ProjectFiles.Add(GitIgnoreFilename); } } if(bAutoCreateReadme) { // 2.b. Create a "README.md" file with a custom description const FString ReadmeFilename = FPaths::Combine(FPaths::ProjectDir(), TEXT("README.md")); if (FFileHelper::SaveStringToFile(ReadmeContent.ToString(), *ReadmeFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) { ProjectFiles.Add(ReadmeFilename); } } if(bAutoCreateGitAttributes) { // 2.c. Synchronous (very quick) "lfs install" operation: needs only to be run once by user GitSourceControlUtils::RunCommand(TEXT("lfs install"), PathToGitBinary, PathToProjectDir, TArray(), TArray(), InfoMessages, ErrorMessages); // 2.d. Create a ".gitattributes" file to enable Git LFS (Large File System) for the whole "Content/" subdir const FString GitAttributesFilename = FPaths::Combine(FPaths::ProjectDir(), TEXT(".gitattributes")); FString GitAttributesContent; if(GitSourceControl.AccessSettings().IsUsingGitLfsLocking()) { // Git LFS 2.x File Locking mechanism GitAttributesContent = TEXT("Content/** filter=lfs diff=lfs merge=lfs -text lockable\n"); } else { GitAttributesContent = TEXT("Content/** filter=lfs diff=lfs merge=lfs -text\n"); } if(FFileHelper::SaveStringToFile(GitAttributesContent, *GitAttributesFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) { ProjectFiles.Add(GitAttributesFilename); } } // 3. Add files to Source Control: launch an asynchronous MarkForAdd operation LaunchMarkForAddOperation(ProjectFiles); // 4. The CheckIn will follow, at completion of the MarkForAdd operation } return FReply::Handled(); } // Launch an asynchronous "MarkForAdd" operation and start an ongoing notification void SGitSourceControlSettings::LaunchMarkForAddOperation(const TArray& InFiles) { FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); TSharedRef MarkForAddOperation = ISourceControlOperation::Create(); ECommandResult::Type Result = GitSourceControl.GetProvider().Execute(MarkForAddOperation, InFiles, EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateSP(this, &SGitSourceControlSettings::OnSourceControlOperationComplete)); if (Result == ECommandResult::Succeeded) { DisplayInProgressNotification(MarkForAddOperation); } else { DisplayFailureNotification(MarkForAddOperation); } } // Launch an asynchronous "CheckIn" operation and start another ongoing notification void SGitSourceControlSettings::LaunchCheckInOperation() { TSharedRef CheckInOperation = ISourceControlOperation::Create(); CheckInOperation->SetDescription(InitialCommitMessage); FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); ECommandResult::Type Result = GitSourceControl.GetProvider().Execute(CheckInOperation, TArray(), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateSP(this, &SGitSourceControlSettings::OnSourceControlOperationComplete)); if (Result == ECommandResult::Succeeded) { DisplayInProgressNotification(CheckInOperation); } else { DisplayFailureNotification(CheckInOperation); } } /// Delegate called when a source control operation has completed: launch the next one and manage notifications void SGitSourceControlSettings::OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult) { RemoveInProgressNotification(); // Report result with a notification if (InResult == ECommandResult::Succeeded) { DisplaySuccessNotification(InOperation); } else { DisplayFailureNotification(InOperation); } if ((InOperation->GetName() == "MarkForAdd") && (InResult == ECommandResult::Succeeded) && bAutoInitialCommit) { // 4. optional initial Asynchronous commit with custom message: launch a "CheckIn" Operation LaunchCheckInOperation(); } } // Display an ongoing notification during the whole operation void SGitSourceControlSettings::DisplayInProgressNotification(const FSourceControlOperationRef& InOperation) { FNotificationInfo Info(InOperation->GetInProgressString()); Info.bFireAndForget = false; Info.ExpireDuration = 0.0f; Info.FadeOutDuration = 1.0f; OperationInProgressNotification = FSlateNotificationManager::Get().AddNotification(Info); if (OperationInProgressNotification.IsValid()) { OperationInProgressNotification.Pin()->SetCompletionState(SNotificationItem::CS_Pending); } } // Remove the ongoing notification at the end of the operation void SGitSourceControlSettings::RemoveInProgressNotification() { if (OperationInProgressNotification.IsValid()) { OperationInProgressNotification.Pin()->ExpireAndFadeout(); OperationInProgressNotification.Reset(); } } // Display a temporary success notification at the end of the operation void SGitSourceControlSettings::DisplaySuccessNotification(const FSourceControlOperationRef& InOperation) { const FText NotificationText = FText::Format(LOCTEXT("InitialCommit_Success", "{0} operation was successfull!"), FText::FromName(InOperation->GetName())); FNotificationInfo Info(NotificationText); Info.bUseSuccessFailIcons = true; Info.Image = FEditorStyle::GetBrush(TEXT("NotificationList.SuccessImage")); FSlateNotificationManager::Get().AddNotification(Info); } // Display a temporary failure notification at the end of the operation void SGitSourceControlSettings::DisplayFailureNotification(const FSourceControlOperationRef& InOperation) { const FText NotificationText = FText::Format(LOCTEXT("InitialCommit_Failure", "Error: {0} operation failed!"), FText::FromName(InOperation->GetName())); FNotificationInfo Info(NotificationText); Info.ExpireDuration = 8.0f; FSlateNotificationManager::Get().AddNotification(Info); } void SGitSourceControlSettings::OnCheckedCreateGitIgnore(ECheckBoxState NewCheckedState) { bAutoCreateGitIgnore = (NewCheckedState == ECheckBoxState::Checked); } void SGitSourceControlSettings::OnCheckedCreateReadme(ECheckBoxState NewCheckedState) { bAutoCreateReadme = (NewCheckedState == ECheckBoxState::Checked); } bool SGitSourceControlSettings::GetAutoCreateReadme() const { return bAutoCreateReadme; } void SGitSourceControlSettings::OnReadmeContentCommited(const FText& InText, ETextCommit::Type InCommitType) { ReadmeContent = InText; } FText SGitSourceControlSettings::GetReadmeContent() const { return ReadmeContent; } void SGitSourceControlSettings::OnCheckedCreateGitAttributes(ECheckBoxState NewCheckedState) { bAutoCreateGitAttributes = (NewCheckedState == ECheckBoxState::Checked); } void SGitSourceControlSettings::OnCheckedUseGitLfsLocking(ECheckBoxState NewCheckedState) { FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); GitSourceControl.AccessSettings().SetUsingGitLfsLocking(NewCheckedState == ECheckBoxState::Checked); GitSourceControl.AccessSettings().SaveSettings(); } bool SGitSourceControlSettings::GetIsUsingGitLfsLocking() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); return GitSourceControl.AccessSettings().IsUsingGitLfsLocking(); } ECheckBoxState SGitSourceControlSettings::IsUsingGitLfsLocking() const { return (GetIsUsingGitLfsLocking() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked); } void SGitSourceControlSettings::OnLfsUserNameCommited(const FText& InText, ETextCommit::Type InCommitType) { FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); GitSourceControl.AccessSettings().SetLfsUserName(InText.ToString()); GitSourceControl.AccessSettings().SaveSettings(); } FText SGitSourceControlSettings::GetLfsUserName() const { const FGitSourceControlModule& GitSourceControl = FModuleManager::GetModuleChecked("GitSourceControl"); return FText::FromString(GitSourceControl.AccessSettings().GetLfsUserName()); } void SGitSourceControlSettings::OnCheckedInitialCommit(ECheckBoxState NewCheckedState) { bAutoInitialCommit = (NewCheckedState == ECheckBoxState::Checked); } bool SGitSourceControlSettings::GetAutoInitialCommit() const { return bAutoInitialCommit; } void SGitSourceControlSettings::OnInitialCommitMessageCommited(const FText& InText, ETextCommit::Type InCommitType) { InitialCommitMessage = InText; } FText SGitSourceControlSettings::GetInitialCommitMessage() const { return InitialCommitMessage; } void SGitSourceControlSettings::OnRemoteUrlCommited(const FText& InText, ETextCommit::Type InCommitType) { RemoteUrl = InText; } FText SGitSourceControlSettings::GetRemoteUrl() const { return RemoteUrl; } #undef LOCTEXT_NAMESPACE