#include "CSNewProjectWizard.h" #include "CSManager.h" #include "DesktopPlatformModule.h" #include "IDesktopPlatform.h" #include "Interfaces/IPluginManager.h" #include "Runtime/AppFramework/Public/Widgets/Workflow/SWizard.h" #include "UnrealSharpEditor/UnrealSharpEditor.h" #include "UnrealSharpProcHelper/CSProcHelper.h" #define LOCTEXT_NAMESPACE "UnrealSharpEditor" void SCSNewProjectDialog::Construct(const FArguments& InArgs) { static FName ProjectDestination(TEXT("")); const FString ScriptPath = FPaths::ConvertRelativePathToFull(FCSProcHelper::GetScriptFolderDirectory()); FText ProjectDestinationName = FText::FromString(FString::Printf(TEXT("%s (This Project)"), FApp::GetProjectName())); ProjectDestinations.Add(MakeShared(ProjectDestination, ProjectDestinationName, FApp::GetProjectName(), ScriptPath, 0)); IPluginManager& PluginManager = IPluginManager::Get(); TArray> EnabledPlugins = PluginManager.GetEnabledPlugins(); for (const TSharedRef& Plugin : EnabledPlugins) { const FString PluginFilePath = FPaths::ConvertRelativePathToFull(Plugin->GetBaseDir()); if (!FPaths::IsUnderDirectory(PluginFilePath, FCSProcHelper::GetPluginsDirectory()) || Plugin->GetName() == UE_PLUGIN_NAME) { continue; } FString ScriptDirectory = PluginFilePath / "Script"; ProjectDestinations.Add(MakeShared(FName(Plugin->GetName()), FText::FromString(Plugin->GetFriendlyName()), Plugin->GetName(), ScriptDirectory, ProjectDestinations.Num(), Plugin)); } SelectedProjectDestinationIndex = 0; ChildSlot [ SNew(SWizard) .ShowPageList(false) .FinishButtonText(LOCTEXT("CreateProject", "Create C# Project")) .FinishButtonToolTip(LOCTEXT("CreateProjectTooltip", "Create a new C# project with the specified settings")) .OnCanceled(this, &SCSNewProjectDialog::OnCancel) .OnFinished(this, &SCSNewProjectDialog::OnFinish) .CanFinish(this, &SCSNewProjectDialog::CanFinish) + SWizard::Page() [ SNew(SOverlay) + SOverlay::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding(0, 0, 0, 10) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0, 0, 10, 0) [ SNew(STextBlock) .Text(LOCTEXT("NewProjectName", "Name")) ] + SHorizontalBox::Slot() .FillWidth(1) [ SAssignNew(NameTextBox, SEditableTextBox) ] ] + SVerticalBox::Slot() .Padding(0, 0, 0, 10) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0, 0, 10, 0) [ SNew(STextBlock) .Text(LOCTEXT("NewProjectOwner", "Owner")) ] + SHorizontalBox::Slot() .FillWidth(1) [ SAssignNew(ProjectDestinationComboBox, SComboBox>) .OptionsSource(&ProjectDestinations) .InitiallySelectedItem(ProjectDestinations[SelectedProjectDestinationIndex]) .OnSelectionChanged(this, &SCSNewProjectDialog::OnProjectDestinationChanged) .OnGenerateWidget_Static(&SCSNewProjectDialog::OnGenerateProjectDestinationWidget) .Content() [ SNew(STextBlock).Text_Lambda([this] { if (SelectedProjectDestinationIndex == INDEX_NONE) { return FText(); } return ProjectDestinations[SelectedProjectDestinationIndex]->GetDisplayName(); }) ] ] ] + SVerticalBox::Slot() .Padding(0, 0, 0, 10) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0, 0, 10, 0) [ SNew(STextBlock) .Text(LOCTEXT("NewProjectLocation", "Location")) ] + SHorizontalBox::Slot() .FillWidth(1) [ SAssignNew(PathTextBox, SEditableTextBox) .Text(FText::FromString(ScriptPath)) ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(SButton) .VAlign(VAlign_Center) .ButtonStyle(FAppStyle::Get(), "SimpleButton") .OnClicked(this, &SCSNewProjectDialog::OnExplorerButtonClicked) [ SNew(SImage) .Image(FAppStyle::Get().GetBrush("Icons.FolderClosed")) .ColorAndOpacity(FSlateColor::UseForeground()) ] ] ] ] ] ]; OnProjectDestinationChanged(ProjectDestinations[SelectedProjectDestinationIndex], ESelectInfo::Direct); } void SCSNewProjectDialog::OnProjectDestinationChanged(TSharedPtr NewProjectDestination, ESelectInfo::Type) { if (NewProjectDestination == nullptr) { SelectedProjectDestinationIndex = INDEX_NONE; return; } SelectedProjectDestinationIndex = NewProjectDestination->GetIndex(); PathTextBox->SetText(FText::FromString(NewProjectDestination->GetPath())); FString ManagedName = TEXT("Managed") + NewProjectDestination->GetName(); NameTextBox->SetText(FText::FromString(ManagedName)); } TSharedRef SCSNewProjectDialog::OnGenerateProjectDestinationWidget(TSharedRef Destination) { return SNew(STextBlock) .Text(Destination->GetDisplayName()); } void SCSNewProjectDialog::OnPathSelected(const FString& NewPath) { if (NewPath.IsEmpty()) { return; } PathTextBox->SetText(FText::FromString(NewPath)); } FReply SCSNewProjectDialog::OnExplorerButtonClicked() { IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); if (!DesktopPlatform) { return FReply::Handled(); } TSharedPtr ParentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared()); void* ParentWindowWindowHandle = ParentWindow.IsValid() ? ParentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr; FString FolderName; const FString Title = TEXT("Choose a location for new project"); if (DesktopPlatform->OpenDirectoryDialog(ParentWindowWindowHandle, Title,FCSProcHelper::GetScriptFolderDirectory(), FolderName)) { if (!FolderName.EndsWith(TEXT("/")) ) { FolderName += TEXT("/"); } } if (FolderName.IsEmpty()) { return FReply::Handled(); } PathTextBox->SetText(FText::FromString(FolderName)); return FReply::Handled(); } void SCSNewProjectDialog::OnCancel() { CloseWindow(); } void SCSNewProjectDialog::OnFinish() { TMap Arguments; FString ModuleName = NameTextBox->GetText().ToString(); FString ProjectParentFolder = PathTextBox->GetText().ToString(); FString ProjectRoot; FString GlueProjectLocation; FString GlueProjectName; auto MakeGlueNameAndLocation = [](FString& GlueLocation, FString& GlueName, const FString& PluginName, const FString& CsProjFolder) { GlueName = FString::Printf(TEXT("%s.Glue"), *PluginName); GlueLocation = FPaths::Combine(CsProjFolder, FString::Printf(TEXT("%s.csproj"), *GlueName)); }; if (ProjectDestinations.IsValidIndex(SelectedProjectDestinationIndex) && SelectedProjectDestinationIndex > 0) { const TSharedRef& Destination = ProjectDestinations[SelectedProjectDestinationIndex]; const TSharedPtr& Plugin = Destination->GetPlugin(); ProjectRoot = Plugin->GetBaseDir(); MakeGlueNameAndLocation(GlueProjectLocation, GlueProjectName, Plugin->GetName(), FPaths::Combine(Destination->GetPath(), GlueProjectName)); } else { ProjectRoot = FPaths::ProjectDir(); MakeGlueNameAndLocation(GlueProjectLocation, GlueProjectName, FApp::GetProjectName(), FCSProcHelper::GetProjectGlueFolderPath()); } if (!FPaths::FileExists(GlueProjectLocation)) { Arguments.Add(TEXT("SkipIncludeProjectGlue"), TEXT("true")); } Arguments.Add(TEXT("GlueProjectName"), GlueProjectName); FUnrealSharpEditorModule::Get().AddNewProject(ModuleName, ProjectParentFolder, ProjectRoot, Arguments); CloseWindow(); } bool SCSNewProjectDialog::CanFinish() const { FString Name = NameTextBox->GetText().ToString(); FString Path = PathTextBox->GetText().ToString(); FString Filename = Name + ".csproj"; FString AbsolutePath = Path / Filename; // Path can't be empty, name can't be empty, and path must contain the script path if (Path.IsEmpty() || Name.IsEmpty() || SelectedProjectDestinationIndex == INDEX_NONE || !Path.Contains(ProjectDestinations[SelectedProjectDestinationIndex]->GetPath())) { return false; } // Name can't contain spaces if (Name.Contains(TEXT(" "))) { return false; } // Path must be a valid directory if (FPaths::DirectoryExists(Path / Name)) { return false; } // File must not already exist IFileManager& FileManager = IFileManager::Get(); TArray AssemblyPaths; FileManager.FindFiles(AssemblyPaths, *Path, TEXT(".csproj")); for (const FString& AssemblyPath : AssemblyPaths) { FString ProjectName = FPaths::GetBaseFilename(AssemblyPath); if (ProjectName == Name) { return false; } } return true; } void SCSNewProjectDialog::CloseWindow() { TSharedPtr ContainingWindow = FSlateApplication::Get().FindWidgetWindow(AsShared()); if (ContainingWindow.IsValid()) { ContainingWindow->RequestDestroyWindow(); } } #undef LOCTEXT_NAMESPACE