You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1363 lines
40 KiB
C++
1363 lines
40 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "GameProjectGenerationPrivatePCH.h"
|
|
#include "MainFrame.h"
|
|
#include "DesktopPlatformModule.h"
|
|
#include "SourceCodeNavigation.h"
|
|
#include "SScrollBorder.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "NewProjectWizard"
|
|
|
|
FName SNewProjectWizard::TemplatePageName = TEXT("Template");
|
|
FName SNewProjectWizard::NameAndLocationPageName = TEXT("NameAndLocation");
|
|
|
|
struct FTemplateItem
|
|
{
|
|
FText Name;
|
|
FText Description;
|
|
FString ProjectFile;
|
|
TSharedPtr<FSlateBrush> TemplateThumbnail;
|
|
bool bGenerateCode;
|
|
|
|
FTemplateItem(const FText& InName, const FText& InDescription, const TSharedPtr<FSlateBrush>& InTemplateThumbnail, bool InGenerateCode, const FString& InProjectFile)
|
|
: Name(InName)
|
|
, Description(InDescription)
|
|
, ProjectFile(InProjectFile)
|
|
, TemplateThumbnail(InTemplateThumbnail)
|
|
, bGenerateCode(InGenerateCode)
|
|
{}
|
|
};
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void SNewProjectWizard::Construct( const FArguments& InArgs )
|
|
{
|
|
LastValidityCheckTime = 0;
|
|
ValidityCheckFrequency = 4;
|
|
bLastGlobalValidityCheckSuccessful = true;
|
|
bLastNameAndLocationValidityCheckSuccessful = true;
|
|
bPreventPeriodicValidityChecksUntilNextChange = false;
|
|
bItemsAreExpanded = false;
|
|
bCopyStarterContent = GEditor->GetGameAgnosticSettings().bCopyStarterContentPreference;
|
|
bStarterContentIsAvailable = GameProjectUtils::IsStarterContentAvailableForNewProjects();
|
|
|
|
// If the items should be initially visible, show them now
|
|
ExpanderRolloutCurve = FCurveSequence(0.0f, 0.1f, ECurveEaseFunction::QuadOut);
|
|
if(bItemsAreExpanded)
|
|
{
|
|
ExpanderRolloutCurve.JumpToEnd();
|
|
}
|
|
|
|
// Find all template projects
|
|
FindTemplateProjects();
|
|
SetDefaultProjectLocation();
|
|
|
|
// Width of all labels; increase this if adding a longer label to this page
|
|
const float LabelWidth = 46;
|
|
const float EditableTextHeight = 26;
|
|
|
|
SAssignNew(TemplateListView, SListView< TSharedPtr<FTemplateItem> >)
|
|
.ListItemsSource(&TemplateList)
|
|
.SelectionMode(ESelectionMode::Single)
|
|
.ClearSelectionOnClick(false)
|
|
.OnGenerateRow(this, &SNewProjectWizard::HandleTemplateListViewGenerateRow)
|
|
.OnMouseButtonDoubleClick(this, &SNewProjectWizard::HandleTemplateListViewDoubleClick)
|
|
.OnSelectionChanged(this, &SNewProjectWizard::HandleTemplateListViewSelectionChanged);
|
|
|
|
ChildSlot
|
|
[
|
|
SNew(SOverlay)
|
|
|
|
// Wizard
|
|
+SOverlay::Slot()
|
|
[
|
|
SAssignNew(MainWizard, SWizard)
|
|
.ShowPageList(false)
|
|
.ShowCancelButton(false)
|
|
.CanFinish(this, &SNewProjectWizard::HandleCreateProjectWizardCanFinish)
|
|
.FinishButtonText(LOCTEXT("FinishButtonText", "Create Project"))
|
|
.FinishButtonToolTip(LOCTEXT("FinishButtonToolTip", "Creates your new project in the specified location with the specified template and name.") )
|
|
.OnFinished(this, &SNewProjectWizard::HandleCreateProjectWizardFinished)
|
|
.OnFirstPageBackClicked(InArgs._OnBackRequested)
|
|
|
|
// Choose Template
|
|
+SWizard::Page()
|
|
.OnEnter(this, &SNewProjectWizard::OnPageVisited, TemplatePageName)
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
// Page description
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(10.0f, 0.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FText::Format(LOCTEXT("ChooseTemplateDescription", "Choose a template for your project. If you choose a C++ template, you must have {0} installed to compile.\nBlueprint templates use only visual scripting, and do not require additional software."), FSourceCodeNavigation::GetSuggestedSourceCodeIDE()))
|
|
]
|
|
|
|
// Templates list
|
|
+SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
.Padding(0.0f, 10.0f)
|
|
[
|
|
SNew(SScrollBorder, TemplateListView.ToSharedRef())
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
[
|
|
TemplateListView.ToSharedRef()
|
|
]
|
|
]
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 0.0f, 0.0f, 6.0f)
|
|
[
|
|
SNew(SCheckBox)
|
|
.ToolTipText( LOCTEXT("CopyStarterContent_ToolTip", "Adds example content to your new project, including simple placeable meshes with basic materials and textures. You can turn this off to start with a project that is only has the bare essentials for the selected project template."))
|
|
.OnCheckStateChanged(this, &SNewProjectWizard::OnCopyStarterContentChanged)
|
|
.IsChecked(this, &SNewProjectWizard::IsCopyStarterContentChecked)
|
|
.Visibility(this, &SNewProjectWizard::GetCopyStarterContentCheckboxVisibility)
|
|
.Content()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("CopyStarterContent", "Include starter content" ))
|
|
]
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 0.0f, 0.0f, 8.0f)
|
|
[
|
|
// Constant height, whether the label is visible or not
|
|
SNew(SBox)
|
|
.HeightOverride(20)
|
|
[
|
|
SNew(SBorder)
|
|
.Visibility(this, &SNewProjectWizard::GetNameAndLocationErrorLabelVisibility)
|
|
.BorderImage(FEditorStyle::GetBrush("GameProjectDialog.ErrorLabelBorder"))
|
|
.Content()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SNewProjectWizard::GetNameAndLocationErrorLabelText)
|
|
.TextStyle(FEditorStyle::Get(), "GameProjectDialog.ErrorLabelFont")
|
|
]
|
|
]
|
|
]
|
|
|
|
// Properties
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FEditorStyle::GetBrush("DetailsView.CategoryTop"))
|
|
.BorderBackgroundColor(FLinearColor(0.6f, 0.6f, 0.6f, 1.0f ))
|
|
.Padding(FMargin(6.0f, 4.0f, 7.0f, 4.0f))
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
// Name
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
// Name label
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f, 4.0f, 6.0f, 4.0f)
|
|
[
|
|
SNew(SBox)
|
|
.HAlign(HAlign_Right)
|
|
.WidthOverride(LabelWidth)
|
|
[
|
|
SNew(STextBlock)
|
|
.TextStyle(FEditorStyle::Get(), "GameProjectDialog.ProjectNamePathLabels")
|
|
.Text(LOCTEXT("ProjectNameLabel", "Name"))
|
|
]
|
|
]
|
|
|
|
// Name editable text
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(6.0f, 4.0f, 0.0f, 4.0f)
|
|
[
|
|
SNew(SBox)
|
|
.HeightOverride(EditableTextHeight)
|
|
.WidthOverride( 200.0f ) // We don't need a giant text field for the project name, so set a max width
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &SNewProjectWizard::GetCurrentProjectFileName)
|
|
.OnTextChanged(this, &SNewProjectWizard::OnCurrentProjectFileNameChanged)
|
|
]
|
|
]
|
|
|
|
+SHorizontalBox::Slot() // Spacer
|
|
|
|
// Expander button
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(6.0f, 4.0f, 0.0f, 4.0f)
|
|
[
|
|
SNew(SButton)
|
|
.OnClicked(this, &SNewProjectWizard::OnExpanderClicked)
|
|
.ToolTipText(LOCTEXT("ExpanderButtonTooltip", "Click to toggle display of the full path and file creation preview"))
|
|
[
|
|
SNew(SBox)
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SImage)
|
|
.Image(this, &SNewProjectWizard::GetExpanderIcon)
|
|
.ColorAndOpacity(FLinearColor::Black)
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
// Expander content
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SBorder)
|
|
.Padding(0.0f)
|
|
.BorderImage(FStyleDefaults::GetNoBrush())
|
|
.Visibility(this, &SNewProjectWizard::GetExpandedItemsVisibility)
|
|
.DesiredSizeScale(this, &SNewProjectWizard::GetExpandedItemsScale)
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
// Path
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
// Path label
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(0.0f, 4.0f, 6.0f, 4.0f)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SBox)
|
|
.HAlign(HAlign_Right)
|
|
.WidthOverride(LabelWidth)
|
|
[
|
|
SNew(STextBlock)
|
|
.TextStyle(FEditorStyle::Get(), "GameProjectDialog.ProjectNamePathLabels")
|
|
.Text(LOCTEXT("ProjectPathLabel", "Folder"))
|
|
]
|
|
]
|
|
|
|
// Path editable text
|
|
+SHorizontalBox::Slot()
|
|
.Padding(6.0f, 4.0f, 0.0f, 4.0f)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SBox)
|
|
.HeightOverride(EditableTextHeight)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &SNewProjectWizard::GetCurrentProjectFilePath)
|
|
.OnTextChanged(this, &SNewProjectWizard::OnCurrentProjectFilePathChanged)
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(6.0f, 1.0f, 0.0f, 0.0f)
|
|
[
|
|
SNew(SButton)
|
|
.VAlign(VAlign_Center)
|
|
.OnClicked(this, &SNewProjectWizard::HandleBrowseButtonClicked)
|
|
.Text(LOCTEXT( "BrowseButtonText", "Choose Folder"))
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
// Proj file preview
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
// Proj file preview label
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f, 4.0f, 6.0f, 4.0f)
|
|
[
|
|
SNew(SBox)
|
|
.HAlign(HAlign_Right)
|
|
.WidthOverride(LabelWidth)
|
|
[
|
|
SNew(STextBlock)
|
|
.TextStyle(FEditorStyle::Get(), "GameProjectDialog.ProjectNamePathLabels")
|
|
.Text(LOCTEXT("ProjFilePreviewLabel", "File"))
|
|
]
|
|
]
|
|
|
|
// Proj file preview label
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(6.0f, 4.0f, 0.0f, 4.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SNewProjectWizard::GetProjectFilenameWithPathLabelText)
|
|
]
|
|
]
|
|
|
|
// Layout explanation
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(LabelWidth + 12.0f, 4.0f, 6.0f, 4.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FText::Format(LOCTEXT("ProjectNameAndLocationDetails", "A folder containing a project file (.{0}) and a few other folders will be created for you at the specified path."), FText::FromString(IProjectManager::GetProjectFileExtension())))
|
|
]
|
|
]
|
|
|
|
// Layout diagram
|
|
+SHorizontalBox::Slot()
|
|
.Padding(6.0f, 4.0f, 0.0f, 0.0f)
|
|
[
|
|
SNew(SBox)
|
|
[
|
|
ConstructDiagram()
|
|
]
|
|
]
|
|
]
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f)
|
|
[
|
|
SNew(SBorder)
|
|
.Padding(FMargin(0.0f, 3.0f, 0.0f, 0.0f))
|
|
.BorderImage(FEditorStyle::GetBrush("DetailsView.CategoryBottom"))
|
|
.BorderBackgroundColor(FLinearColor(0.6f, 0.6f, 0.6f, 1.0f ))
|
|
]
|
|
]
|
|
]
|
|
|
|
// Global Error label
|
|
+SOverlay::Slot()
|
|
.HAlign(HAlign_Left)
|
|
.VAlign(VAlign_Bottom)
|
|
.Padding( FMargin( 0.0f, 5.0f, 0.0f, 0.0f ) )
|
|
[
|
|
// Constant height, whether the label is visible or not
|
|
SNew(SBox)
|
|
.HeightOverride(20.0f)
|
|
[
|
|
SNew(SBorder)
|
|
.Visibility(this, &SNewProjectWizard::GetGlobalErrorLabelVisibility)
|
|
.BorderImage(FEditorStyle::GetBrush("GameProjectDialog.ErrorLabelBorder"))
|
|
.Content()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.FillWidth(1.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SNewProjectWizard::GetGlobalErrorLabelText)
|
|
.TextStyle(FEditorStyle::Get(), TEXT("GameProjectDialog.ErrorLabelFont"))
|
|
]
|
|
|
|
// A link to a platform-specific IDE, only shown when a compiler is not available
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SHyperlink)
|
|
.Text(FText::Format(LOCTEXT("IDEDownloadLinkText", "Download {0}"), FSourceCodeNavigation::GetSuggestedSourceCodeIDE()))
|
|
.OnNavigate(this, &SNewProjectWizard::OnDownloadIDEClicked, FSourceCodeNavigation::GetSuggestedSourceCodeIDEDownloadURL())
|
|
.Visibility(this, &SNewProjectWizard::GetGlobalErrorLabelIDELinkVisibility)
|
|
]
|
|
|
|
// A button to close the persistent global error text
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SButton)
|
|
.ButtonStyle(FEditorStyle::Get(), "NoBorder")
|
|
.ContentPadding(0.0f)
|
|
.OnClicked(this, &SNewProjectWizard::OnCloseGlobalErrorLabelClicked)
|
|
.Visibility(this, &SNewProjectWizard::GetGlobalErrorLabelCloseButtonVisibility)
|
|
[
|
|
SNew(SImage)
|
|
.Image(FEditorStyle::GetBrush("GameProjectDialog.ErrorLabelCloseButton"))
|
|
]
|
|
]
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
// Initialize the current page name. Assuming the template page.
|
|
CurrentPageName = TemplatePageName;
|
|
|
|
// Select the first item
|
|
if (TemplateList.Num() > 0)
|
|
{
|
|
TemplateListView->SetSelection(TemplateList[0], ESelectInfo::Direct);
|
|
}
|
|
|
|
UpdateProjectFileValidity();
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
void SNewProjectWizard::OnCopyStarterContentChanged(ESlateCheckBoxState::Type InNewState)
|
|
{
|
|
bCopyStarterContent = InNewState == ESlateCheckBoxState::Checked;
|
|
}
|
|
ESlateCheckBoxState::Type SNewProjectWizard::IsCopyStarterContentChecked() const
|
|
{
|
|
return bCopyStarterContent ? ESlateCheckBoxState::Checked : ESlateCheckBoxState::Unchecked;
|
|
}
|
|
|
|
EVisibility SNewProjectWizard::GetCopyStarterContentCheckboxVisibility() const
|
|
{
|
|
return bStarterContentIsAvailable ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
void SNewProjectWizard::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
|
|
{
|
|
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
|
|
|
// Every few seconds, the project file path is checked for validity in case the disk contents changed and the location is now valid or invalid.
|
|
// After project creation, periodic checks are disabled to prevent a brief message indicating that the project you created already exists.
|
|
// This feature is re-enabled if the user did not restart and began editing parameters again.
|
|
if ( !bPreventPeriodicValidityChecksUntilNextChange && (InCurrentTime > LastValidityCheckTime + ValidityCheckFrequency) )
|
|
{
|
|
UpdateProjectFileValidity();
|
|
}
|
|
}
|
|
|
|
|
|
TSharedRef<SWidget> SNewProjectWizard::ConstructDiagram()
|
|
{
|
|
const FMargin FolderPadding(0.0f, 0.0f, 2.0f, 0.0f);
|
|
|
|
return SNew(SVerticalBox)
|
|
.ToolTipText(LOCTEXT("FileDiagramTooltip", "List of files and folders that will be created"))
|
|
// Parent folder
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.Padding(FolderPadding)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SImage)
|
|
.Image(FEditorStyle::GetBrush("GameProjectDialog.FolderIconOpen"))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SNewProjectWizard::GetCurrentProjectFileParentFolder)
|
|
]
|
|
]
|
|
|
|
// Project folder
|
|
+SVerticalBox::Slot()
|
|
.Padding(10.0f, 0.0f)
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.Padding(FolderPadding)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SImage)
|
|
.Image(FEditorStyle::GetBrush("GameProjectDialog.FolderIconOpen"))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SNewProjectWizard::GetCurrentProjectFileNameString)
|
|
]
|
|
]
|
|
|
|
// Config folder
|
|
+SVerticalBox::Slot()
|
|
.Padding(20.0f, 0.0f)
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.Padding(FolderPadding)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SImage)
|
|
.Image(FEditorStyle::GetBrush("GameProjectDialog.FolderIconClosed"))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FText::FromString(TEXT("Config"))) // Not localized on purpose. This folder does not change with different languages!
|
|
]
|
|
]
|
|
|
|
// Content folder
|
|
+SVerticalBox::Slot()
|
|
.Padding(20.0f, 0.0f)
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.Padding(FolderPadding)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SImage)
|
|
.Image(FEditorStyle::GetBrush("GameProjectDialog.FolderIconClosed"))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FText::FromString(TEXT("Content"))) // Not localized on purpose. This folder does not change with different languages!
|
|
]
|
|
]
|
|
|
|
// Project file
|
|
+SVerticalBox::Slot()
|
|
.Padding(20.0f, 0.0f)
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.Padding(FolderPadding)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SImage)
|
|
.Image(FEditorStyle::GetBrush("GameProjectDialog.ProjectFileIcon"))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SNewProjectWizard::GetCurrentProjectFileNameStringWithExtension)
|
|
]
|
|
];
|
|
}
|
|
|
|
|
|
EVisibility SNewProjectWizard::GetExpandedItemsVisibility() const
|
|
{
|
|
// The section is visible if its scale in Y is greater than 0
|
|
const float Scale = GetExpandedItemsScale().Y;
|
|
return (Scale > 0) ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
|
|
FVector2D SNewProjectWizard::GetExpandedItemsScale() const
|
|
{
|
|
const float Scale = ExpanderRolloutCurve.GetLerp();
|
|
return FVector2D(1.0f, Scale);
|
|
}
|
|
|
|
|
|
const FSlateBrush* SNewProjectWizard::GetExpanderIcon() const
|
|
{
|
|
return (bItemsAreExpanded) ? FEditorStyle::GetBrush("DetailsView.PulldownArrow.Up") : FEditorStyle::GetBrush("DetailsView.PulldownArrow.Down");
|
|
}
|
|
|
|
|
|
FReply SNewProjectWizard::OnExpanderClicked()
|
|
{
|
|
bItemsAreExpanded = !bItemsAreExpanded;
|
|
|
|
if(bItemsAreExpanded)
|
|
{
|
|
ExpanderRolloutCurve = FCurveSequence(0.0f, 0.1f, ECurveEaseFunction::CubicOut);
|
|
ExpanderRolloutCurve.Play();
|
|
}
|
|
else
|
|
{
|
|
ExpanderRolloutCurve = FCurveSequence(0.0f, 0.1f, ECurveEaseFunction::CubicIn);
|
|
ExpanderRolloutCurve.PlayReverse();
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
|
|
const FSlateBrush* SNewProjectWizard::GetTemplateItemImage(TWeakPtr<FTemplateItem> TemplateItem) const
|
|
{
|
|
if ( TemplateItem.IsValid() )
|
|
{
|
|
TSharedPtr<FTemplateItem> Item = TemplateItem.Pin();
|
|
if ( Item->TemplateThumbnail.IsValid() )
|
|
{
|
|
return Item->TemplateThumbnail.Get();
|
|
}
|
|
}
|
|
|
|
return FEditorStyle::GetBrush("GameProjectDialog.DefaultGameThumbnail");
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::HandleTemplateListViewSelectionChanged(TSharedPtr<FTemplateItem> TemplateItem, ESelectInfo::Type SelectInfo)
|
|
{
|
|
UpdateProjectFileValidity();
|
|
}
|
|
|
|
|
|
TSharedPtr<FTemplateItem> SNewProjectWizard::GetSelectedTemplateItem() const
|
|
{
|
|
TArray< TSharedPtr<FTemplateItem> > SelectedItems = TemplateListView->GetSelectedItems();
|
|
if ( SelectedItems.Num() > 0 )
|
|
{
|
|
return SelectedItems[0];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
FText SNewProjectWizard::GetSelectedTemplateName() const
|
|
{
|
|
TSharedPtr<FTemplateItem> SelectedItem = GetSelectedTemplateItem();
|
|
if ( SelectedItem.IsValid() )
|
|
{
|
|
return SelectedItem->Name;
|
|
}
|
|
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
|
|
FText SNewProjectWizard::GetCurrentProjectFileName() const
|
|
{
|
|
return FText::FromString( GetCurrentProjectFileNameString() );
|
|
}
|
|
|
|
|
|
FString SNewProjectWizard::GetCurrentProjectFileNameString() const
|
|
{
|
|
return CurrentProjectFileName;
|
|
}
|
|
|
|
|
|
FString SNewProjectWizard::GetCurrentProjectFileNameStringWithExtension() const
|
|
{
|
|
return CurrentProjectFileName + TEXT(".") + IProjectManager::GetProjectFileExtension();
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::OnCurrentProjectFileNameChanged(const FText& InValue)
|
|
{
|
|
CurrentProjectFileName = InValue.ToString();
|
|
UpdateProjectFileValidity();
|
|
}
|
|
|
|
|
|
FText SNewProjectWizard::GetCurrentProjectFilePath() const
|
|
{
|
|
return FText::FromString(CurrentProjectFilePath);
|
|
}
|
|
|
|
|
|
FString SNewProjectWizard::GetCurrentProjectFileParentFolder() const
|
|
{
|
|
if ( CurrentProjectFilePath.EndsWith(TEXT("/")) || CurrentProjectFilePath.EndsWith("\\") )
|
|
{
|
|
return FPaths::GetCleanFilename( CurrentProjectFilePath.LeftChop(1) );
|
|
}
|
|
else
|
|
{
|
|
return FPaths::GetCleanFilename( CurrentProjectFilePath );
|
|
}
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::OnCurrentProjectFilePathChanged(const FText& InValue)
|
|
{
|
|
CurrentProjectFilePath = InValue.ToString();
|
|
FPaths::MakePlatformFilename(CurrentProjectFilePath);
|
|
UpdateProjectFileValidity();
|
|
}
|
|
|
|
|
|
FString SNewProjectWizard::GetProjectFilenameWithPathLabelText() const
|
|
{
|
|
return GetProjectFilenameWithPath();
|
|
}
|
|
|
|
|
|
FString SNewProjectWizard::GetProjectFilenameWithPath() const
|
|
{
|
|
if ( CurrentProjectFilePath.IsEmpty() )
|
|
{
|
|
// Don't even try to assemble the path or else it may be relative to the binaries folder!
|
|
return TEXT("");
|
|
}
|
|
else
|
|
{
|
|
const FString ProjectName = CurrentProjectFileName;
|
|
const FString ProjectPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*CurrentProjectFilePath);
|
|
const FString Filename = ProjectName + TEXT(".") + IProjectManager::GetProjectFileExtension();
|
|
FString ProjectFilename = FPaths::Combine( *ProjectPath, *ProjectName, *Filename );
|
|
FPaths::MakePlatformFilename(ProjectFilename);
|
|
return ProjectFilename;
|
|
}
|
|
}
|
|
|
|
|
|
FReply SNewProjectWizard::HandleBrowseButtonClicked()
|
|
{
|
|
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
|
if ( DesktopPlatform )
|
|
{
|
|
void* ParentWindowWindowHandle = NULL;
|
|
|
|
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
|
|
const TSharedPtr<SWindow>& MainFrameParentWindow = MainFrameModule.GetParentWindow();
|
|
if ( MainFrameParentWindow.IsValid() && MainFrameParentWindow->GetNativeWindow().IsValid() )
|
|
{
|
|
ParentWindowWindowHandle = MainFrameParentWindow->GetNativeWindow()->GetOSWindowHandle();
|
|
}
|
|
|
|
FString FolderName;
|
|
const FString Title = LOCTEXT("NewProjectBrowseTitle", "Choose a project location").ToString();
|
|
const bool bFolderSelected = DesktopPlatform->OpenDirectoryDialog(
|
|
ParentWindowWindowHandle,
|
|
Title,
|
|
LastBrowsePath,
|
|
FolderName
|
|
);
|
|
|
|
if ( bFolderSelected )
|
|
{
|
|
if ( !FolderName.EndsWith(TEXT("/")) )
|
|
{
|
|
FolderName += TEXT("/");
|
|
}
|
|
|
|
FPaths::MakePlatformFilename(FolderName);
|
|
LastBrowsePath = FolderName;
|
|
CurrentProjectFilePath = FolderName;
|
|
}
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::OnDownloadIDEClicked(FString URL)
|
|
{
|
|
FPlatformProcess::LaunchURL( *URL, NULL, NULL );
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::HandleTemplateListViewDoubleClick( TSharedPtr<FTemplateItem> TemplateItem )
|
|
{
|
|
// Advance to the name/location page
|
|
const int32 NamePageIdx = 1;
|
|
if ( MainWizard->CanShowPage(NamePageIdx) )
|
|
{
|
|
MainWizard->ShowPage(NamePageIdx);
|
|
}
|
|
}
|
|
|
|
|
|
bool SNewProjectWizard::IsCreateProjectEnabled() const
|
|
{
|
|
if ( CurrentPageName == NAME_None )//|| CurrentPageName == TemplatePageName )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return bLastGlobalValidityCheckSuccessful && bLastNameAndLocationValidityCheckSuccessful;
|
|
}
|
|
|
|
|
|
bool SNewProjectWizard::HandlePageCanShow( FName PageName ) const
|
|
{
|
|
if (PageName == NameAndLocationPageName)
|
|
{
|
|
return bLastGlobalValidityCheckSuccessful;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::OnPageVisited(FName NewPageName)
|
|
{
|
|
CurrentPageName = NewPageName;
|
|
}
|
|
|
|
|
|
EVisibility SNewProjectWizard::GetGlobalErrorLabelVisibility() const
|
|
{
|
|
return GetGlobalErrorLabelText().IsEmpty() ? EVisibility::Hidden : EVisibility::Visible;
|
|
}
|
|
|
|
|
|
EVisibility SNewProjectWizard::GetGlobalErrorLabelCloseButtonVisibility() const
|
|
{
|
|
return PersistentGlobalErrorLabelText.IsEmpty() ? EVisibility::Collapsed : EVisibility::Visible;
|
|
}
|
|
|
|
|
|
EVisibility SNewProjectWizard::GetGlobalErrorLabelIDELinkVisibility() const
|
|
{
|
|
return (IsCompilerRequired() && !FSourceCodeNavigation::IsCompilerAvailable()) ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
|
|
FText SNewProjectWizard::GetGlobalErrorLabelText() const
|
|
{
|
|
if ( !PersistentGlobalErrorLabelText.IsEmpty() )
|
|
{
|
|
return PersistentGlobalErrorLabelText;
|
|
}
|
|
|
|
if ( !bLastGlobalValidityCheckSuccessful )
|
|
{
|
|
return LastGlobalValidityErrorText;
|
|
}
|
|
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
|
|
FReply SNewProjectWizard::OnCloseGlobalErrorLabelClicked()
|
|
{
|
|
PersistentGlobalErrorLabelText = FText();
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
|
|
EVisibility SNewProjectWizard::GetNameAndLocationErrorLabelVisibility() const
|
|
{
|
|
return GetNameAndLocationErrorLabelText().IsEmpty() ? EVisibility::Hidden : EVisibility::Visible;
|
|
}
|
|
|
|
|
|
FText SNewProjectWizard::GetNameAndLocationErrorLabelText() const
|
|
{
|
|
if ( !bLastNameAndLocationValidityCheckSuccessful )
|
|
{
|
|
return LastNameAndLocationValidityErrorText;
|
|
}
|
|
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
void SNewProjectWizard::FindTemplateProjects()
|
|
{
|
|
// Add some default non-data driven templates
|
|
TemplateList.Add( MakeShareable(new FTemplateItem(
|
|
LOCTEXT("BlankProjectName", "Blank"),
|
|
LOCTEXT("BlankProjectDescription", "A clean empty project with no code."),
|
|
MakeShareable( new FSlateBrush( *FEditorStyle::GetBrush("GameProjectDialog.BlankProjectThumbnail") ) ),
|
|
false, // bGenerateCode
|
|
TEXT("") // No filename, this is a generation template
|
|
)) );
|
|
|
|
TemplateList.Add( MakeShareable(new FTemplateItem(
|
|
LOCTEXT("BasicCodeProjectName", "Basic Code"),
|
|
LOCTEXT("BasicCodeProjectDescription", "An empty project with some basic game framework code classes created."),
|
|
MakeShareable( new FSlateBrush( *FEditorStyle::GetBrush("GameProjectDialog.BasicCodeThumbnail") ) ),
|
|
true, // bGenerateCode
|
|
TEXT("") // No filename, this is a generation template
|
|
)) );
|
|
|
|
// Now discover and all data driven templates
|
|
TArray<FString> TemplateRootFolders;
|
|
|
|
// @todo rocket make template folder locations extensible.
|
|
TemplateRootFolders.Add( FPaths::RootDir() + TEXT("Templates") );
|
|
|
|
// Form a list of all folders that could contain template projects
|
|
TArray<FString> AllTemplateFolders;
|
|
for ( auto TemplateRootFolderIt = TemplateRootFolders.CreateConstIterator(); TemplateRootFolderIt; ++TemplateRootFolderIt )
|
|
{
|
|
const FString Root = *TemplateRootFolderIt;
|
|
const FString SearchString = Root / TEXT("*");
|
|
TArray<FString> TemplateFolders;
|
|
IFileManager::Get().FindFiles(TemplateFolders, *SearchString, /*Files=*/false, /*Directories=*/true);
|
|
for ( auto TemplateFolderIt = TemplateFolders.CreateConstIterator(); TemplateFolderIt; ++TemplateFolderIt )
|
|
{
|
|
AllTemplateFolders.Add( Root / (*TemplateFolderIt) );
|
|
}
|
|
}
|
|
|
|
// Add a template item for every discovered project
|
|
for ( auto TemplateFolderIt = AllTemplateFolders.CreateConstIterator(); TemplateFolderIt; ++TemplateFolderIt )
|
|
{
|
|
const FString SearchString = (*TemplateFolderIt) / TEXT("*.") + IProjectManager::GetProjectFileExtension();
|
|
TArray<FString> FoundProjectFiles;
|
|
IFileManager::Get().FindFiles(FoundProjectFiles, *SearchString, /*Files=*/true, /*Directories=*/false);
|
|
if ( FoundProjectFiles.Num() > 0 )
|
|
{
|
|
if ( ensure(FoundProjectFiles.Num() == 1) )
|
|
{
|
|
// Make sure a TemplateDefs ini file exists
|
|
const FString Root = *TemplateFolderIt;
|
|
UTemplateProjectDefs* TemplateDefs = GameProjectUtils::LoadTemplateDefs(Root);
|
|
if ( TemplateDefs )
|
|
{
|
|
// Found a template. Add it to the template items list.
|
|
const FString ProjectFilename = Root / FoundProjectFiles[0];
|
|
FText TemplateName = TemplateDefs->GetDisplayNameText();
|
|
FText TemplateDescription = TemplateDefs->GetLocalizedDescription();
|
|
|
|
// If no template name was specified for the current culture, just use the project name
|
|
if ( TemplateName.IsEmpty() )
|
|
{
|
|
TemplateName = FText::FromString(FPaths::GetBaseFilename(ProjectFilename));
|
|
}
|
|
|
|
// Only generate code if the template has a source folder
|
|
const FString SourceFolder = (Root / TEXT("Source"));
|
|
const bool bGenerateCode = IFileManager::Get().DirectoryExists(*SourceFolder);
|
|
|
|
TSharedPtr<FSlateDynamicImageBrush> DynamicBrush;
|
|
const FString ThumbnailPNGFile = FPaths::GetBaseFilename(ProjectFilename, false) + TEXT(".png");
|
|
if ( FPlatformFileManager::Get().GetPlatformFile().FileExists(*ThumbnailPNGFile) )
|
|
{
|
|
const FName BrushName = FName(*ThumbnailPNGFile);
|
|
DynamicBrush = MakeShareable( new FSlateDynamicImageBrush(BrushName , FVector2D(128,128) ) );
|
|
}
|
|
|
|
if (FPaths::GetCleanFilename(ProjectFilename) == GameProjectUtils::GetDefaultProjectTemplateFilename())
|
|
{
|
|
TemplateList.Insert(MakeShareable(new FTemplateItem(TemplateName, TemplateDescription, DynamicBrush, bGenerateCode, ProjectFilename)), 0);
|
|
}
|
|
else
|
|
{
|
|
TemplateList.Add(MakeShareable(new FTemplateItem(TemplateName, TemplateDescription, DynamicBrush, bGenerateCode, ProjectFilename)));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// More than one project file in this template? This is not legal, skip it.
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::SetDefaultProjectLocation( )
|
|
{
|
|
FString DefaultProjectFilePath;
|
|
|
|
// First, try and use the first previously used path that still exists
|
|
for ( const FString& CreatedProjectPath : GEditor->GetGameAgnosticSettings().CreatedProjectPaths )
|
|
{
|
|
if ( IFileManager::Get().DirectoryExists(*CreatedProjectPath) )
|
|
{
|
|
DefaultProjectFilePath = CreatedProjectPath;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( DefaultProjectFilePath.IsEmpty() )
|
|
{
|
|
// No previously used path, decide a default path.
|
|
DefaultProjectFilePath = FDesktopPlatformModule::Get()->GetDefaultProjectCreationPath();
|
|
IFileManager::Get().MakeDirectory(*DefaultProjectFilePath, true);
|
|
}
|
|
|
|
if ( !DefaultProjectFilePath.IsEmpty() && DefaultProjectFilePath.Right(1) == TEXT("/") )
|
|
{
|
|
DefaultProjectFilePath.LeftChop(1);
|
|
}
|
|
|
|
FPaths::NormalizeFilename(DefaultProjectFilePath);
|
|
FPaths::MakePlatformFilename(DefaultProjectFilePath);
|
|
const FString GenericProjectName = LOCTEXT("DefaultProjectName", "MyProject").ToString();
|
|
FString ProjectName = GenericProjectName;
|
|
|
|
// Check to make sure the project file doesn't already exist
|
|
FText FailReason;
|
|
if ( !GameProjectUtils::IsValidProjectFileForCreation(DefaultProjectFilePath / ProjectName / ProjectName + TEXT(".") + IProjectManager::GetProjectFileExtension(), FailReason) )
|
|
{
|
|
// If it exists, find an appropriate numerical suffix
|
|
const int MaxSuffix = 1000;
|
|
int32 Suffix;
|
|
for ( Suffix = 2; Suffix < MaxSuffix; ++Suffix )
|
|
{
|
|
ProjectName = GenericProjectName + FString::Printf(TEXT("%d"), Suffix);
|
|
if ( GameProjectUtils::IsValidProjectFileForCreation(DefaultProjectFilePath / ProjectName / ProjectName + TEXT(".") + IProjectManager::GetProjectFileExtension(), FailReason) )
|
|
{
|
|
// Found a name that is not taken. Break out.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Suffix >= MaxSuffix)
|
|
{
|
|
UE_LOG(LogGameProjectGeneration, Warning, TEXT("Failed to find a suffix for the default project name"));
|
|
ProjectName = TEXT("");
|
|
}
|
|
}
|
|
|
|
if ( !DefaultProjectFilePath.IsEmpty() )
|
|
{
|
|
CurrentProjectFileName = ProjectName;
|
|
CurrentProjectFilePath = DefaultProjectFilePath;
|
|
FPaths::MakePlatformFilename(CurrentProjectFilePath);
|
|
LastBrowsePath = CurrentProjectFilePath;
|
|
}
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::UpdateProjectFileValidity( )
|
|
{
|
|
// Global validity
|
|
{
|
|
bLastGlobalValidityCheckSuccessful = true;
|
|
|
|
TSharedPtr<FTemplateItem> SelectedTemplate = GetSelectedTemplateItem();
|
|
if ( !SelectedTemplate.IsValid() )
|
|
{
|
|
bLastGlobalValidityCheckSuccessful = false;
|
|
LastGlobalValidityErrorText = LOCTEXT("NoTemplateSelected", "No Template Selected");
|
|
}
|
|
else if ( IsCompilerRequired() )
|
|
{
|
|
if ( !FSourceCodeNavigation::IsCompilerAvailable() )
|
|
{
|
|
bLastGlobalValidityCheckSuccessful = false;
|
|
LastGlobalValidityErrorText = FText::Format( LOCTEXT("NoCompilerFound", "No compiler was found. In order to use a C++ template, you must first install {0}."), FSourceCodeNavigation::GetSuggestedSourceCodeIDE() );
|
|
}
|
|
else if ( !FModuleManager::Get().IsUnrealBuildToolAvailable() )
|
|
{
|
|
bLastGlobalValidityCheckSuccessful = false;
|
|
LastGlobalValidityErrorText = LOCTEXT("UBTNotFound", "Engine source code was not found. In order to use a C++ template, you must have engine source code in Engine/Source.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name and Location Validity
|
|
{
|
|
if ( CurrentProjectFileName.Contains(TEXT("/")) || CurrentProjectFileName.Contains(TEXT("\\")) )
|
|
{
|
|
bLastNameAndLocationValidityCheckSuccessful = false;
|
|
LastNameAndLocationValidityErrorText = LOCTEXT( "SlashOrBackslashInProjectName", "The project name may not contain a slash or backslash" );
|
|
}
|
|
else
|
|
{
|
|
FText FailReason;
|
|
bLastNameAndLocationValidityCheckSuccessful = GameProjectUtils::IsValidProjectFileForCreation( GetProjectFilenameWithPath(), FailReason );
|
|
if ( !bLastNameAndLocationValidityCheckSuccessful )
|
|
{
|
|
LastNameAndLocationValidityErrorText = FailReason;
|
|
}
|
|
}
|
|
|
|
if ( !FPlatformMisc::IsValidAbsolutePathFormat(CurrentProjectFilePath) )
|
|
{
|
|
bLastNameAndLocationValidityCheckSuccessful = false;
|
|
LastNameAndLocationValidityErrorText = LOCTEXT( "InvalidFolderPath", "The folder path is invalid" );
|
|
}
|
|
else
|
|
{
|
|
FText FailReason;
|
|
bLastNameAndLocationValidityCheckSuccessful = GameProjectUtils::IsValidProjectFileForCreation( GetProjectFilenameWithPath(), FailReason );
|
|
if ( !bLastNameAndLocationValidityCheckSuccessful )
|
|
{
|
|
LastNameAndLocationValidityErrorText = FailReason;
|
|
}
|
|
}
|
|
}
|
|
|
|
LastValidityCheckTime = FSlateApplication::Get().GetCurrentTime();
|
|
|
|
// Since this function was invoked, periodic validity checks should be re-enabled if they were disabled.
|
|
bPreventPeriodicValidityChecksUntilNextChange = false;
|
|
}
|
|
|
|
|
|
bool SNewProjectWizard::IsCompilerRequired( ) const
|
|
{
|
|
TSharedPtr<FTemplateItem> SelectedTemplate = GetSelectedTemplateItem();
|
|
return SelectedTemplate.IsValid() && SelectedTemplate->bGenerateCode;
|
|
}
|
|
|
|
|
|
bool SNewProjectWizard::CreateProject( const FString& ProjectFile )
|
|
{
|
|
// Get the selected template
|
|
TSharedPtr<FTemplateItem> SelectedTemplate = GetSelectedTemplateItem();
|
|
|
|
if (!ensure(SelectedTemplate.IsValid()))
|
|
{
|
|
// A template must be selected.
|
|
return false;
|
|
}
|
|
|
|
const bool bAddCode = SelectedTemplate->bGenerateCode;
|
|
FText FailReason;
|
|
|
|
if (!GameProjectUtils::CreateProject( ProjectFile, SelectedTemplate->ProjectFile, bAddCode, bCopyStarterContent, FailReason))
|
|
{
|
|
DisplayError(FailReason);
|
|
return false;
|
|
}
|
|
|
|
// Successfully created the project. Update the last created location string.
|
|
FString CreatedProjectPath = FPaths::GetPath(FPaths::GetPath(ProjectFile));
|
|
|
|
// if the original path was the drives root (ie: C:/) the double path call strips the last /
|
|
if (CreatedProjectPath.EndsWith(":"))
|
|
{
|
|
CreatedProjectPath.AppendChar('/');
|
|
}
|
|
|
|
GEditor->AccessGameAgnosticSettings().CreatedProjectPaths.Remove(CreatedProjectPath);
|
|
GEditor->AccessGameAgnosticSettings().CreatedProjectPaths.Insert(CreatedProjectPath, 0);
|
|
GEditor->AccessGameAgnosticSettings().bCopyStarterContentPreference = bCopyStarterContent;
|
|
GEditor->AccessGameAgnosticSettings().PostEditChange();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::CreateAndOpenProject( )
|
|
{
|
|
if( IsCreateProjectEnabled() )
|
|
{
|
|
FString ProjectFile = GetProjectFilenameWithPath();
|
|
if ( CreateProject(ProjectFile) )
|
|
{
|
|
// Prevent periodic validity checks. This is to prevent a brief error message about the project already existing while you are exiting.
|
|
bPreventPeriodicValidityChecksUntilNextChange = true;
|
|
|
|
const bool bCodeAdded = GetSelectedTemplateItem()->bGenerateCode;
|
|
if ( bCodeAdded )
|
|
{
|
|
// In non-rocket, the engine executable may need to be built in order to build the game binaries,
|
|
// just open the code editing ide now instead of automatically building for them since it is not safe to do so.
|
|
const bool bPromptForConfirmation = FApp::HasGameName(); /** Only prompt for project switching if we are already in a project */
|
|
OpenCodeIDE( ProjectFile, bPromptForConfirmation );
|
|
}
|
|
else
|
|
{
|
|
// Successfully created a content only project. Now open it.
|
|
const bool bPromptForConfirmation = false;
|
|
OpenProject( ProjectFile, bPromptForConfirmation );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool SNewProjectWizard::OpenProject( const FString& ProjectFile, bool bPromptForConfirmation )
|
|
{
|
|
if ( bPromptForConfirmation )
|
|
{
|
|
// Notify the user of the success, and ask to switch projects.
|
|
FText SuccessMessage = FText::Format( LOCTEXT("NewProjectSuccessful", "Project '{0}' was successfully created. Would you like to open it now?"), FText::FromString(FPaths::GetBaseFilename(ProjectFile)) );
|
|
if ( FMessageDialog::Open( EAppMsgType::YesNo, SuccessMessage ) == EAppReturnType::No )
|
|
{
|
|
// The user opted out of opening the new project. Just close the window.
|
|
CloseWindowIfAppropriate();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FText FailReason;
|
|
if ( GameProjectUtils::OpenProject( ProjectFile, FailReason ) )
|
|
{
|
|
// Successfully opened the project, the editor is closing.
|
|
// Close this window in case something prevents the editor from closing (save dialog, quit confirmation, etc)
|
|
CloseWindowIfAppropriate();
|
|
return true;
|
|
}
|
|
|
|
DisplayError( FailReason );
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SNewProjectWizard::OpenCodeIDE( const FString& ProjectFile, bool bPromptForConfirmation )
|
|
{
|
|
if ( bPromptForConfirmation )
|
|
{
|
|
// Notify the user of the success, and ask to switch projects.
|
|
FText SuccessMessage = FText::Format( LOCTEXT("NewProjectSuccessfulIDE", "Project '{0}' was successfully created. Would you like to open it in {1}?"), FText::FromString(FPaths::GetBaseFilename(ProjectFile)), FSourceCodeNavigation::GetSuggestedSourceCodeIDE() );
|
|
if ( FMessageDialog::Open( EAppMsgType::YesNo, SuccessMessage ) == EAppReturnType::No )
|
|
{
|
|
// The user opted out of opening the new project. Just close the window.
|
|
CloseWindowIfAppropriate(true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FText FailReason;
|
|
|
|
if ( GameProjectUtils::OpenCodeIDE( ProjectFile, FailReason ) )
|
|
{
|
|
// Successfully opened code editing IDE, the editor is closing
|
|
// Close this window in case something prevents the editor from closing (save dialog, quit confirmation, etc)
|
|
CloseWindowIfAppropriate(true);
|
|
return true;
|
|
}
|
|
|
|
DisplayError( FailReason );
|
|
return false;
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::CloseWindowIfAppropriate( bool ForceClose )
|
|
{
|
|
if ( ForceClose || FApp::HasGameName() )
|
|
{
|
|
FWidgetPath WidgetPath;
|
|
TSharedPtr<SWindow> ContainingWindow = FSlateApplication::Get().FindWidgetWindow( AsShared(), WidgetPath);
|
|
|
|
if ( ContainingWindow.IsValid() )
|
|
{
|
|
ContainingWindow->RequestDestroyWindow();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::DisplayError( const FText& ErrorText )
|
|
{
|
|
UE_LOG(LogGameProjectGeneration, Log, TEXT("%s"), *ErrorText.ToString());
|
|
PersistentGlobalErrorLabelText = ErrorText;
|
|
}
|
|
|
|
|
|
/* SNewProjectWizard event handlers
|
|
*****************************************************************************/
|
|
|
|
bool SNewProjectWizard::HandleCreateProjectWizardCanFinish( ) const
|
|
{
|
|
return IsCreateProjectEnabled();
|
|
}
|
|
|
|
|
|
void SNewProjectWizard::HandleCreateProjectWizardFinished( )
|
|
{
|
|
CreateAndOpenProject();
|
|
}
|
|
|
|
|
|
TSharedRef<ITableRow> SNewProjectWizard::HandleTemplateListViewGenerateRow( TSharedPtr<FTemplateItem> TemplateItem, const TSharedRef<STableViewBase>& OwnerTable )
|
|
{
|
|
if (!ensure(TemplateItem.IsValid()))
|
|
{
|
|
return SNew(STableRow<TSharedPtr<FTemplateItem>>, OwnerTable);
|
|
}
|
|
|
|
const FString CodeTemplateToolTip = FText::Format( LOCTEXT("CodeTemplateToolTip", "This template contains C++ source code files. To use this template you must have {0} installed."), FSourceCodeNavigation::GetSuggestedSourceCodeIDE() ).ToString();
|
|
const int32 ThumbnailBorderPadding = 5;
|
|
const int32 ThumbnailSize = 128;
|
|
|
|
return SNew( STableRow<TSharedPtr<FTemplateItem>>, OwnerTable )
|
|
.Style(FEditorStyle::Get(), "GameProjectDialog.TemplateListView.TableRow")
|
|
[
|
|
SNew(SBox)
|
|
.HeightOverride(ThumbnailSize+ThumbnailBorderPadding+5)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
// Thumbnail
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SBox)
|
|
.Padding(ThumbnailBorderPadding)
|
|
.WidthOverride(ThumbnailSize+ThumbnailBorderPadding * 2)
|
|
.HeightOverride(ThumbnailSize+ThumbnailBorderPadding * 2)
|
|
[
|
|
SNew(SImage)
|
|
.Image(this, &SNewProjectWizard::GetTemplateItemImage, TWeakPtr<FTemplateItem>(TemplateItem))
|
|
]
|
|
]
|
|
|
|
// Name and description
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.Padding(10.0f)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+ SVerticalBox::Slot()
|
|
.Padding(0.0f, 0.0f, 0.0f, 8.0f)
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.TextStyle(FEditorStyle::Get(), "GameProjectDialog.TemplateItemTitle")
|
|
.Text(TemplateItem->Name.ToString())
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SImage)
|
|
.Visibility(TemplateItem->bGenerateCode ? EVisibility::Visible : EVisibility::Collapsed)
|
|
.ToolTipText(CodeTemplateToolTip)
|
|
.Image(FEditorStyle::GetBrush("GameProjectDialog.CodeImage"))
|
|
]
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.WrapTextAt(596)
|
|
.Text(TemplateItem->Description.ToString())
|
|
]
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|