Files
UnrealEngine/Engine/Source/Editor/LevelEditor/Private/SLevelEditorBuildAndSubmit.cpp
2025-05-18 13:04:45 +08:00

453 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SLevelEditorBuildAndSubmit.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Views/SHeaderRow.h"
#include "Widgets/Views/STableViewBase.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Views/SListView.h"
#include "Widgets/Input/SCheckBox.h"
#include "Styling/AppStyle.h"
#include "ISourceControlModule.h"
#include "FileHelpers.h"
#include "LevelEditorActions.h"
#include "EditorBuildUtils.h"
#include "Logging/MessageLog.h"
#include "LightingBuildOptions.h"
#include "ProfilingDebugging/CpuProfilerTrace.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SExpandableArea.h"
#define LOCTEXT_NAMESPACE "SLevelEditorBuildAndSubmit"
static const FName NAME_NameColumn(TEXT("Name"));
static const float SIZE_NameColumn = 180.0f;
static const FName NAME_StateColumn(TEXT("State"));
static const float SIZE_StateColumn = 180.0f;
/** Multi-column item used in the additional package list - represents an FPackageItem */
class SPackageItem : public SMultiColumnTableRow<TSharedPtr<SLevelEditorBuildAndSubmit::FPackageItem>>
{
public:
SLATE_BEGIN_ARGS( SPackageItem )
: _Item()
{}
SLATE_ARGUMENT( TSharedPtr<SLevelEditorBuildAndSubmit::FPackageItem>, Item )
SLATE_END_ARGS()
/**
* Generates a widget for task graph events list column
*
* @param InArgs A declaration from which to construct the widget
*/
virtual TSharedRef< SWidget > GenerateWidgetForColumn( const FName& ColumnName ) override
{
if (NAME_NameColumn == ColumnName)
{
// Name column contains a check box and the name.
// The name of the package will probably get truncated by the narrowness of the column so display the full name in a tooltip too.
return SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(4.f, 1.f)
[
SNew(SCheckBox)
.OnCheckStateChanged(this, &SPackageItem::OnCheckStateChanged)
.IsChecked(Item->Selected ? ECheckBoxState::Checked : ECheckBoxState::Unchecked)
[
SNew(STextBlock)
.Font(FAppStyle::GetFontStyle("BuildAndSubmit.NormalFont"))
.Text(FText::FromString(Item->Name))
.ToolTipText(FText::FromString(Item->Name))
]
];
}
else if (NAME_StateColumn == ColumnName)
{
// The state column shows the source control status. It should only ever be one of the three states shown below.
// Uses a smaller font and uses CAPS to make them stand out next to the name column.
FText StateText;
if(Item->SourceControlState->IsCheckedOut())
{
StateText = LOCTEXT("SourceControlState_CheckedOut", "CHECKED OUT");
}
else if(!Item->SourceControlState->IsSourceControlled())
{
StateText = LOCTEXT("SourceControlState_NotInDepot", "NOT IN DEPOT");
}
else if(Item->SourceControlState->IsAdded())
{
StateText = LOCTEXT("SourceControlState_OpenForAdd", "OPEN FOR ADD");
}
else
{
StateText = LOCTEXT("SourceControlState_Unknown", "UNKNOWN");
}
return SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(5.f, 0.f)
[
SNew(STextBlock)
.Font(FAppStyle::GetFontStyle("BuildAndSubmit.SmallFont"))
.Text(StateText)
];
}
else
{
return SNullWidget::NullWidget;
}
}
/**
* Construct the widget
*
* @param InArgs A declaration from which to construct the widget
*/
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView )
{
Item = InArgs._Item;
FSuperRowType::Construct( FSuperRowType::FArguments(), InOwnerTableView );
}
private:
/** User clicked the check box on this item - set the item struct's selected flag to match */
void OnCheckStateChanged( const ECheckBoxState NewCheckedState )
{
Item->Selected = (NewCheckedState == ECheckBoxState::Checked);
}
/** the item that this row represents */
TSharedPtr<SLevelEditorBuildAndSubmit::FPackageItem> Item;
};
SLevelEditorBuildAndSubmit::~SLevelEditorBuildAndSubmit()
{
UPackage::PackageDirtyStateChangedEvent.RemoveAll(this);
ISourceControlModule::Get().GetProvider().UnregisterSourceControlStateChanged_Handle(OnSourceControlStateChangedDelegateHandle);
}
void SLevelEditorBuildAndSubmit::Construct( const FArguments& InArgs, const TSharedRef< class ILevelEditor >& OwningLevelEditor )
{
bIsExtraPackagesSectionExpanded = false;
LevelEditor = OwningLevelEditor;
TSharedRef< SHeaderRow > PackagesListHeaderRow =
SNew( SHeaderRow )
+ SHeaderRow::Column(NAME_NameColumn)
.FillWidth(SIZE_NameColumn)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding( 0.0f, 3.0f, 0.0f, 0.0f )
[
SNew( STextBlock )
.Text(LOCTEXT("ColumnHeader_Name", "Name"))
]
]
+ SHeaderRow::Column(NAME_StateColumn)
.FillWidth(SIZE_StateColumn)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding( 0.0f, 3.0f, 0.0f, 0.0f )
[
SNew( STextBlock )
.Text(LOCTEXT("ColumnHeader_State", "State"))
]
];
ChildSlot
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.Padding(5.f)
[
SNew(SExpandableArea)
.AreaTitle(LOCTEXT("DescriptionSectionTitle", "Submission Description"))
.Padding(2.f)
.BodyContent()
[
SAssignNew(DescriptionBox, SEditableTextBox)
.HintText(LOCTEXT("DescriptionDefaultText", "Enter change description here..."))
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(5)
[
SNew(SExpandableArea )
.AreaTitle(LOCTEXT("ExtraPackagesSectionTitle", "Additional Files to Submit"))
.Padding(2.f)
.InitiallyCollapsed(true)
.MaxHeight(300.f)
.OnAreaExpansionChanged(this, &SLevelEditorBuildAndSubmit::OnShowHideExtraPackagesSection)
.BodyContent()
[
SNew(SListView<TSharedPtr<FPackageItem>>)
.ListItemsSource(&PackagesList)
.OnGenerateRow( this, &SLevelEditorBuildAndSubmit::OnGenerateWidgetForPackagesList )
.SelectionMode( ESelectionMode::None )
.HeaderRow(PackagesListHeaderRow)
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(5.f)
[
SNew( SExpandableArea )
.AreaTitle(LOCTEXT("BuildOptionsSectionTitle", "Build Options"))
.Padding(2.f)
.BodyContent()
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.Padding(2.f)
[
SAssignNew(NoSubmitOnMapErrorBox, SCheckBox)
[
SNew(STextBlock)
.Text(LOCTEXT("NoSubmitMapErrorsButtonLabel", "Don't Submit in Event of Map Errors"))
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(2.f)
[
SAssignNew(NoSubmitOnSaveErrorBox, SCheckBox)
[
SNew(STextBlock)
.Text(LOCTEXT("NoSubmitSaveErrorsButtonLabel", "Don't Submit in Event of Save Errors"))
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(2.f)
[
SAssignNew(ShowPackagesNotInSCBox, SCheckBox)
.OnCheckStateChanged(this, &SLevelEditorBuildAndSubmit::OnShowPackagesNotInSCBoxChanged)
[
SNew(STextBlock)
.Text(LOCTEXT("ShowPackagesButtonLabel", "Show Files not in Revision Control"))
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(2.f)
[
SAssignNew(AddFilesToSCBox, SCheckBox)
.IsChecked(ECheckBoxState::Checked)
[
SNew(STextBlock)
.Text(LOCTEXT("AddFilesButtonLabel", "Add Files to Revision Control if Necessary"))
]
]
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(5.f)
.HAlign(HAlign_Right)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.f, 0.f, 10.f, 0.f)
[
SNew(SButton)
.OnClicked(this, &SLevelEditorBuildAndSubmit::OnBuildAndCloseClicked)
.Text(LOCTEXT("BuildAndCloseButtonLabel", "Build and Close"))
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.OnClicked(this, &SLevelEditorBuildAndSubmit::OnBuildClicked)
.Text(LOCTEXT("BuildButtonLabel", "Build"))
]
]
];
UpdatePackagesList();
OnSourceControlStateChangedDelegateHandle = ISourceControlModule::Get().GetProvider().RegisterSourceControlStateChanged_Handle(FSourceControlStateChanged::FDelegate::CreateRaw(this, &SLevelEditorBuildAndSubmit::OnSourceControlStateChanged));
UPackage::PackageDirtyStateChangedEvent.AddRaw(this, &SLevelEditorBuildAndSubmit::OnEditorPackageModified);
}
void SLevelEditorBuildAndSubmit::OnEditorPackageModified(UPackage* Package)
{
if (bIsExtraPackagesSectionExpanded && Package->IsDirty())
{
UpdatePackagesList();
}
}
void SLevelEditorBuildAndSubmit::OnSourceControlStateChanged()
{
TRACE_CPUPROFILER_EVENT_SCOPE(SLevelEditorBuildAndSubmit::OnSourceControlStateChanged);
if (bIsExtraPackagesSectionExpanded)
{
UpdatePackagesList();
}
}
TSharedRef< ITableRow > SLevelEditorBuildAndSubmit::OnGenerateWidgetForPackagesList( TSharedPtr<FPackageItem> InItem, const TSharedRef<STableViewBase>& OwnerTable )
{
return SNew( SPackageItem, OwnerTable )
.Item( InItem );
}
void SLevelEditorBuildAndSubmit::UpdatePackagesList()
{
int32 NumSelected = 0;
PackagesList.Empty();
TMap<FString, FSourceControlStatePtr> PackageStates;
FEditorFileUtils::FindAllSubmittablePackageFiles( PackageStates, false );
for (TMap<FString, FSourceControlStatePtr>::TConstIterator PackageIter(PackageStates); PackageIter; ++PackageIter)
{
FString PackageName = *PackageIter.Key();
const FSourceControlStatePtr CurPackageSCCState = PackageIter.Value();
// Only show files in the depot or we have the flag enabled
if ( CurPackageSCCState.IsValid() && (CurPackageSCCState->IsSourceControlled() || ShowPackagesNotInSCBox->IsChecked()) )
{
UPackage* Package = FindPackage(NULL, *PackageName);
if (Package != NULL)
{
bool bSelected = false;
if (CurPackageSCCState->IsCheckedOut() || CurPackageSCCState->IsAdded())
{
if (Package->IsDirty())
{
// Checked out, dirty packages are selected by default
bSelected = true;
}
}
TSharedPtr<FPackageItem> Item(new FPackageItem());
Item->Name = PackageName;
Item->SourceControlState = CurPackageSCCState;
Item->Selected = bSelected;
// Put pre-selected items at the start of the list
if (CurPackageSCCState->IsSourceControlled())
{
PackagesList.Insert(Item, NumSelected++);
}
else
{
PackagesList.Add(Item);
}
}
}
}
}
FReply SLevelEditorBuildAndSubmit::OnBuildAndCloseClicked()
{
OnBuildClicked();
// Close the parent dockable tab
if (ParentDockTab.IsValid())
{
ParentDockTab.Pin()->RemoveTabFromParent();
}
return FReply::Handled();
}
FReply SLevelEditorBuildAndSubmit::OnBuildClicked()
{
FLevelEditorActionCallbacks::ConfigureLightingBuildOptions(FLightingBuildOptions());
// Configure build settings for the automated build
FEditorBuildUtils::FEditorAutomatedBuildSettings BuildSettings;
// Set the settings that are based on user checkboxes in the UI
BuildSettings.BuildErrorBehavior = NoSubmitOnMapErrorBox->IsChecked() ? FEditorBuildUtils::ABB_FailOnError : FEditorBuildUtils::ABB_ProceedOnError;
BuildSettings.FailedToSaveBehavior = NoSubmitOnSaveErrorBox->IsChecked() ? FEditorBuildUtils::ABB_FailOnError : FEditorBuildUtils::ABB_ProceedOnError;
BuildSettings.bCheckInPackages = true;
BuildSettings.bAutoAddNewFiles = AddFilesToSCBox->IsChecked();
for (int32 i = 0; i < PackagesList.Num(); i++)
{
TSharedPtr<FPackageItem> Item = PackagesList[i];
if(Item->Selected)
{
BuildSettings.PackagesToCheckIn.Add(Item->Name);
}
}
BuildSettings.ChangeDescription = DescriptionBox->GetText().ToString();
// The editor shouldn't be shutdown while using this special editor window
BuildSettings.bShutdownEditorOnCompletion = false;
// Prompt the user on what to do if unsaved maps are detected or if a file can't be checked out for some reason
BuildSettings.NewMapBehavior = FEditorBuildUtils::ABB_PromptOnError;
BuildSettings.UnableToCheckoutFilesBehavior = FEditorBuildUtils::ABB_PromptOnError;
// Attempt the automated build process
FText ErrorMessage;
FEditorBuildUtils::EditorAutomatedBuildAndSubmit( BuildSettings, ErrorMessage );
// If the build failed, display any relevant error message to the user
// Push the errors to the log
if ( !ErrorMessage.IsEmpty() )
{
FMessageLog BuildAndSubmitErrors("BuildAndSubmitErrors");
BuildAndSubmitErrors.NewPage(LOCTEXT("BuildAndSubmitErrorsNewPage", "Build and Submit"));
TArray<FString> Errors;
ErrorMessage.ToString().Replace(LINE_TERMINATOR, TEXT("\n")).ParseIntoArray(Errors, TEXT("\n"), true);
for (int32 ErrorIdx = 0; ErrorIdx < Errors.Num(); ErrorIdx++)
{
BuildAndSubmitErrors.Error(FText::FromString(Errors[ErrorIdx]));
}
BuildAndSubmitErrors.Open();
}
return FReply::Handled();
}
void SLevelEditorBuildAndSubmit::OnShowHideExtraPackagesSection(bool bIsExpanded)
{
bIsExtraPackagesSectionExpanded = bIsExpanded;
if (bIsExtraPackagesSectionExpanded)
{
UpdatePackagesList();
}
}
void SLevelEditorBuildAndSubmit::OnShowPackagesNotInSCBoxChanged(ECheckBoxState InNewState)
{
if (bIsExtraPackagesSectionExpanded)
{
UpdatePackagesList();
}
}
#undef LOCTEXT_NAMESPACE