// Copyright Epic Games, Inc. All Rights Reserved. #include "SubmitToolWidget.h" #include "ConfirmDialogWidget.h" #include "ISettingsModule.h" #include "OutputLogModule.h" #include "OutputLogSettings.h" #include "OutputLogCreationParams.h" #include "View/SubmitToolStyle.h" #include "View/SubmitToolCommandHandler.h" #include "View/SubmitToolMenu.h" #include "View/Widgets/SIntegrationWidget.h" #include "Widgets/Layout/SExpandableArea.h" #include "Widgets/Layout/SUniformGridPanel.h" #include "Widgets/Layout/SSpacer.h" #include "Widgets/Layout/SScrollBox.h" #include "Widgets/Layout/SSeparator.h" #include "Widgets/Input/SMultiLineEditableTextBox.h" #include "Widgets/Views/SListView.h" #include "Widgets/Input/SHyperlink.h" #include "Widgets/Input/SEditableText.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Images/SThrobber.h" #include "Widgets/Docking/SDockTab.h" #include "Models/ModelInterface.h" #include "Models/SubmitToolUserPrefs.h" #include "TagSectionWidget.h" #include "ValidatorsWidget.h" #include "Version/AppVersion.h" #include "Modules/ModuleManager.h" #include "Framework/Docking/TabManager.h" #include "Framework/Application/SlateApplication.h" #include "CommandLine/CmdLineParameters.h" #include "SubmitToolUtils.h" #include "Configuration/Configuration.h" #define LOCTEXT_NAMESPACE "SubmitToolWidget" void SubmitToolWidget::OnCLDescriptionUpdated() { DescriptionBox->Refresh(); } void SubmitToolWidget::HandleApplicationActivationStateChanged(bool bActive) { if(ModelInterface == nullptr) { return; } if(ModelInterface->IsP4OperationRunning() || ModelInterface->IsBlockingOperationRunning()) { return; } if(bActive) { ModelInterface->CheckForFileEdits(); ModelInterface->UpdateCLFromP4Async(); } else { ModelInterface->SendDescriptionToP4(); } } void SubmitToolWidget::Construct(const FArguments& InArgs) { ParentTab = InArgs._ParentTab.Get(); ModelInterface = InArgs._ModelInterface; IntegrationWidget = SNew(SIntegrationWidget) .ModelInterface(ModelInterface) .MainWindow(InArgs._ParentWindow); FSlateApplication::Get().OnApplicationActivationStateChanged().AddRaw(this, &SubmitToolWidget::HandleApplicationActivationStateChanged); OnValidatorFinishedHandle = ModelInterface->AddSingleValidatorFinishedCallback(FOnSingleTaskFinished::FDelegate::CreateRaw(this, &SubmitToolWidget::OnSingleValidatorFinished)); OnValidationUpdateHandle = ModelInterface->AddValidationUpdatedCallback(FOnTaskFinished::FDelegate::CreateRaw(this, &SubmitToolWidget::OnValidationUpdated)); OnCLDescriptionUpdatedHandle = ModelInterface->GetCLDescriptionUpdatedDelegate().Add(FOnCLDescriptionUpdated::FDelegate::CreateRaw(this, &SubmitToolWidget::OnCLDescriptionUpdated)); TSharedPtr Contents; ChildSlot [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) [ SAssignNew(Contents, SVerticalBox) ] ]; /**** Tags ****/ TSharedRef TagSection = SNew(STagSectionWidget) .ParentWindow(InArgs._ParentWindow) .ModelInterface(InArgs._ModelInterface); TSharedRef TagSectionBox = SNew(SBox) .MaxDesiredHeight_Lambda([TagSection]{ return FMath::Min(TagSection->GetDesiredSize().Y, FSubmitToolUserPrefs::Get()->TagSectionSize);}) [ TagSection ]; Contents->AddSlot() .AutoHeight() [ TagSectionBox ]; TSharedPtr ResizeBorder; Contents->AddSlot() .AutoHeight() [ SAssignNew(ResizeBorder, SBorder) .OnMouseButtonUp_Lambda([](const FGeometry& Geometry, const FPointerEvent& PointerEvent) { return FReply::Handled().ReleaseMouseCapture(); }) .OnMouseMove_Lambda([TagSection, this](const FGeometry& Geometry, const FPointerEvent& PointerEvent){ if (PointerEvent.IsMouseButtonDown(EKeys::LeftMouseButton)) { FSubmitToolUserPrefs::Get()->TagSectionSize = FMath::Clamp(FSubmitToolUserPrefs::Get()->TagSectionSize + PointerEvent.GetCursorDelta().Y, ModelInterface->GetTagsArray().Num() == 0 ? 0 : 35, TagSection->GetDesiredSize().Y); return FReply::Handled(); } return FReply::Unhandled(); }) [ SNew(SSeparator).Thickness(5) .Cursor(EMouseCursor::ResizeUpDown) ] ]; ResizeBorder->SetOnMouseButtonDown(FPointerEventHandler::CreateLambda([ResizeBorder](const FGeometry& Geometry, const FPointerEvent& PointerEvent) { return FReply::Handled().CaptureMouse(ResizeBorder.ToSharedRef()); })); TSharedPtr Splitter; Contents->AddSlot() .FillHeight(1.f) [ SAssignNew(Splitter, SSplitter) .Orientation(EOrientation::Orient_Vertical) ]; TSharedPtr BottomLine = SNew(SHorizontalBox) +SHorizontalBox::Slot() .HAlign(HAlign_Left) [ SNew(SEditableText) .IsReadOnly(true) .Text(FText::FromString(FAppVersion::GetVersion())) ]; for(size_t i = 0; i < ModelInterface->GetParameters().GeneralParameters.HelpLinks.Num(); ++i) { const FDocumentationLink& DocLink = ModelInterface->GetParameters().GeneralParameters.HelpLinks[i]; SHorizontalBox::FScopedWidgetSlotArguments Slot = BottomLine->AddSlot(); Slot.AttachWidget( SNew(SHyperlink) .Style(FSubmitToolStyle::Get(), TEXT("NavigationHyperlink")) .Text(FText::FromString(DocLink.Text)) .ToolTipText(FText::FromString(DocLink.Tooltip)) .OnNavigate_Lambda([&DocLink]() { FPlatformProcess::LaunchURL(*DocLink.Link, nullptr, nullptr); }) ); if(i != ModelInterface->GetParameters().GeneralParameters.HelpLinks.Num() - 1) { Slot.HAlign(HAlign_Center); } else { Slot.HAlign(HAlign_Right); } } /**** Version + feedback ****/ Contents->AddSlot() .AutoHeight() [ BottomLine.ToSharedRef() ]; /**** Description + Buttons ****/ DescriptionBox = SNew(SMultiLineEditableTextBox) .Text_Lambda([&ModelInterface = ModelInterface]() { return FText::FromString(ModelInterface->GetCLDescription()); }) .OnTextChanged_Lambda([&ModelInterface = ModelInterface](const FText& newText) { ModelInterface->SetCLDescription(newText); }) .OnTextCommitted_Lambda([this](const FText& Text, ETextCommit::Type CommitType) { if(CommitType != ETextCommit::OnEnter) { ModelInterface->ValidateCLDescription(); }}) .AutoWrapText(true) .OnIsTypedCharValid(FOnIsTypedCharValid::CreateLambda([](const TCHAR) { return true; })) .IsReadOnly_Lambda([]{ return !FModelInterface::GetInputEnabled();}); FString PerforceClientName; FCmdLineParameters::Get().GetValue(FSubmitToolCmdLine::P4Client, PerforceClientName); P4SectionSlot = Splitter->AddSlot() .Resizable(true) .MinSize(150) [ SNew(SBox) .WidthOverride(520) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew(SBorder) .BorderBackgroundColor(FAppStyle::GetColor("ValidatorStateFail")) .Visibility_Lambda([this]() { if (FDateTime::Now().GetHour() < ModelInterface->GetParameters().GeneralParameters.EarlySubmitHour24 || FDateTime::Now().GetHour() >= ModelInterface->GetParameters().GeneralParameters.LateSubmitHour24) { return EVisibility::All; } else { return EVisibility::Collapsed; } }) [ SNew(STextBlock) .Justification(ETextJustify::Center) .TextStyle(FAppStyle::Get(), "BoldTextNormalSize") .Text_Lambda([]() { return FText::FromString(FString::Printf(TEXT("**** It's %s local time, be mindful of submitting late and going away, please remain alert and available for a reasonable period of time (2-3 hours) in case your changes cause issues ****"), *FDateTime::Now().ToFormattedString(TEXT("%H:%M")))); }) ] ] +SVerticalBox::Slot() .AutoHeight() .Padding(FMargin(5)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Left) .AutoWidth() [ SNew(STextBlock) .Font(FAppStyle::Get().GetFontStyle("SmallFontBold")) .Text(NSLOCTEXT("SourceControl.SubmitPanel", "ChangeListDesc", "Changelist Description")) ] + SHorizontalBox::Slot() .FillWidth(1.f) [ SNew(SSpacer) ] // STREAM + SHorizontalBox::Slot() .HAlign(HAlign_Right) .AutoWidth() [ SNew(STextBlock) .Font(FAppStyle::Get().GetFontStyle("SmallFontBold")) .Text(NSLOCTEXT("SourceControl.SubmitPanel", "Stream", "Stream")) ] + SHorizontalBox::Slot() .HAlign(HAlign_Right) .Padding(5, 0, 0, 0) .AutoWidth() [ SNew(STextBlock) .Font(FAppStyle::Get().GetFontStyle("SmallFont")) .Text_Lambda([this]() { return FText::FromString(ModelInterface->GetCurrentStream()); }) ] // WORKSPACE + SHorizontalBox::Slot() .HAlign(HAlign_Right) .Padding(15, 0, 0, 0) .AutoWidth() [ SNew(STextBlock) .Font(FAppStyle::Get().GetFontStyle("SmallFontBold")) .Text(NSLOCTEXT("SourceControl.SubmitPanel", "Workspace", "Workspace")) ] + SHorizontalBox::Slot() .HAlign(HAlign_Right) .Padding(5, 0, 0, 0) .AutoWidth() [ SNew(STextBlock) .Font(FAppStyle::Get().GetFontStyle("SmallFont")) .Text(FText::FromString(PerforceClientName)) ] ] +SVerticalBox::Slot() .FillHeight(.2f) .Padding(FMargin(5, 0, 5, 5)) [ DescriptionBox.ToSharedRef() ] + SVerticalBox::Slot() .AutoHeight() .Padding(FMargin(5, 0, 5, 5)) [ BuildButtonRow() ] ] ].GetSlot(); P4SectionSlot->SetSizeValue(FSubmitToolUserPrefs::Get()->P4SectionSize); TSharedPtr LogSection = BuildOutputLogWidget(); TSharedPtr FilesInCLSection = BuildFilesInCLWidget(); // Files & Validators ValidatorSectionSlot = Splitter->AddSlot() .MinSize(45) .SizeRule(SSplitter::ESizeRule::FractionOfParent) .Resizable(true) [ SNew(SBox) .Padding(0.f,4.f) [ SNew(SScrollBox) .Orientation(EOrientation::Orient_Vertical) +SScrollBox::Slot() .Padding(FMargin(0.f, 2.f)) .AutoSize() [ FilesInCLSection.ToSharedRef() ] +SScrollBox::Slot() .Padding(FMargin(0.f, 2.f)) .AutoSize() [ SNew(SValidatorsWidget) .OnViewLog_Lambda([this](TSharedPtr validator) { LogTabManager->DrawAttention(ValidatorLogTab.ToSharedRef()); }) .ModelInterface(ModelInterface) ] ] ].GetSlot(); ValidatorSectionSlot->SetSizeValue(FSubmitToolUserPrefs::Get()->ValidatorSectionSize); // Log LogSectionSlot = Splitter->AddSlot() .MinSize(200) .SizeRule(SSplitter::ESizeRule::FractionOfParent) .Resizable(true) [ SNew(SVerticalBox) +SVerticalBox::Slot() .Padding(FMargin(0.f, 4.f)) .FillHeight(1.f) [ LogSection.ToSharedRef() ] ].GetSlot(); LogSectionSlot->SetSizeValue(FSubmitToolUserPrefs::Get()->LogSectionSize); } SubmitToolWidget::~SubmitToolWidget() { if(ModelInterface != nullptr) { FSubmitToolUserPrefs* UserPrefs = FSubmitToolUserPrefs::Get(); UserPrefs->P4SectionSize = P4SectionSlot->GetSizeValue(); UserPrefs->ValidatorSectionSize = ValidatorSectionSlot->GetSizeValue(); UserPrefs->LogSectionSize = LogSectionSlot->GetSizeValue(); ModelInterface->RemoveValidationFinishedCallback(OnValidatorFinishedHandle); ModelInterface->RemoveValidationUpdatedCallback(OnValidationUpdateHandle); ModelInterface->GetCLDescriptionUpdatedDelegate().Remove(OnCLDescriptionUpdatedHandle); ModelInterface = nullptr; } } TSharedRef SubmitToolWidget::BuildButtonRow() { ValidateBtn = SNew(SButton) .ToolTipText_Raw(this, &SubmitToolWidget::GetValidateButtonTooltip) .IsEnabled_Lambda([&ModelInterface = ModelInterface] { return (FModelInterface::GetInputEnabled() && !ModelInterface->GetFilesInCL().IsEmpty()) || ModelInterface->IsP4OperationRunning(); }) .ButtonStyle(FAppStyle::Get(), "PrimaryButton") .OnClicked(this, &SubmitToolWidget::ValidateClicked) [ SNew(STextBlock) .MinDesiredWidth(130) .Justification(ETextJustify::Center) .Text_Raw(this, &SubmitToolWidget::GetValidateButtonText) ]; return SNew(SHorizontalBox) #if PLATFORM_WINDOWS + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SButton) .ButtonStyle(FAppStyle::Get(), "Button") .OnClicked(this, &SubmitToolWidget::CopyAllLogsClicked) [ SNew(STextBlock) .MinDesiredWidth(130) .Justification(ETextJustify::Center) .Text(FText::FromString("Copy All Logs")) ] ] #endif +SHorizontalBox::Slot() .FillWidth(1.f) [ SNew(SSpacer) ] + SHorizontalBox::Slot() .FillWidth(1.f) .HAlign(HAlign_Center) .MaxWidth(700) .Padding(8, 0) .AutoWidth() [ SNew(SThrobber) .NumPieces(8) .Visibility_Lambda([this]() { return ModelInterface->IsP4OperationRunning() || ModelInterface->IsBlockingOperationRunning() ? EVisibility::Visible : EVisibility::Collapsed; }) ] +SHorizontalBox::Slot() .FillWidth(1.f) .HAlign(HAlign_Right) .MaxWidth(700) .Padding(4, 0) .AutoWidth() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SCheckBox) .IsChecked_Lambda([this]() { return ModelInterface->bSubmitOnSuccessfulValidation ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .OnCheckStateChanged_Lambda([this](ECheckBoxState InNewState) { ModelInterface->bSubmitOnSuccessfulValidation = !ModelInterface->bSubmitOnSuccessfulValidation; }) .IsFocusable(false) ] +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SButton) .ButtonStyle(FAppStyle::Get(), "InvisibleButton") .IsFocusable(false) .OnClicked_Lambda([this]() { ModelInterface->bSubmitOnSuccessfulValidation = !ModelInterface->bSubmitOnSuccessfulValidation; return FReply::Handled(); }) [ SNew(STextBlock) .Justification(ETextJustify::Left) .Font(FAppStyle::Get().GetFontStyle("SmallFont")) .MinDesiredWidth(60) .Text(FText::FromString(TEXT("Submit On Successful Validation"))) ] ] ] +SHorizontalBox::Slot() .FillWidth(1.f) .HAlign(HAlign_Right) .MaxWidth(700) .AutoWidth() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SCheckBox) .IsChecked_Lambda([this]() { return FSubmitToolUserPrefs::Get()->bOpenJiraOnSubmit ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .OnCheckStateChanged_Lambda([this](ECheckBoxState InNewState) { FSubmitToolUserPrefs::Get()->bOpenJiraOnSubmit = !FSubmitToolUserPrefs::Get()->bOpenJiraOnSubmit; }) ] +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SButton) .ButtonStyle(FAppStyle::Get(), "InvisibleButton") .IsFocusable(false) .OnClicked_Lambda([this]() { FSubmitToolUserPrefs::Get()->bOpenJiraOnSubmit = !FSubmitToolUserPrefs::Get()->bOpenJiraOnSubmit; return FReply::Handled(); }) [ SNew(STextBlock) .Justification(ETextJustify::Left) .Font(FAppStyle::Get().GetFontStyle("SmallFont")) .MinDesiredWidth(60) .Text(FText::FromString(TEXT("Open Ticket on Submit"))) ] ] ] +SHorizontalBox::Slot() .FillWidth(1.f) .HAlign(HAlign_Right) .MaxWidth(700) .AutoWidth() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SCheckBox) .IsChecked_Lambda([this]() { return FSubmitToolUserPrefs::Get()->bCloseOnSubmit ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .OnCheckStateChanged_Lambda([this](ECheckBoxState InNewState) { FSubmitToolUserPrefs::Get()->bCloseOnSubmit = !FSubmitToolUserPrefs::Get()->bCloseOnSubmit; }) ] +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SButton) .ButtonStyle(FAppStyle::Get(), "InvisibleButton") .IsFocusable(false) .OnClicked_Lambda([this]() { FSubmitToolUserPrefs::Get()->bCloseOnSubmit = !FSubmitToolUserPrefs::Get()->bCloseOnSubmit; return FReply::Handled(); }) [ SNew(STextBlock) .Justification(ETextJustify::Left) .Font(FAppStyle::Get().GetFontStyle("SmallFont")) .MinDesiredWidth(60) .Text(FText::FromString(TEXT("Close on Submit Success"))) ] ] ] +SHorizontalBox::Slot() .FillWidth(1.f) .HAlign(HAlign_Right) .MaxWidth(700) .AutoWidth() [ SNew(SButton) .Text(FText::FromString(TEXT("Open Integration Window"))) .Visibility_Lambda([this] { return ModelInterface->IsIntegrationRequired() && ModelInterface->bIsUserInAllowlist ? EVisibility::Visible : EVisibility::Collapsed; }) .OnClicked_Lambda([this] { IntegrationWidget->Open(); return FReply::Handled(); }) .IsEnabled_Lambda([&ModelInterface = ModelInterface] { if (ModelInterface->IsPreflightRequestInProgress()) { return false; } return ModelInterface->IsCLValid(); }) [ SNew(STextBlock) .MinDesiredWidth(130) .Justification(ETextJustify::Center) .Text(FText::FromString(TEXT("Open Integration Window"))) ] ] + SHorizontalBox::Slot() .FillWidth(1.f) .HAlign(HAlign_Right) .MaxWidth(700) .AutoWidth() [ SNew(SUniformGridPanel) .SlotPadding(FAppStyle::GetMargin("StandardDialog.SlotPadding")) .MinDesiredSlotWidth(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotWidth")) + SUniformGridPanel::Slot(0, 0) [ SNew(SButton) .IsEnabled_Lambda([&ModelInterface = ModelInterface] { if (ModelInterface->IsPreflightRequestInProgress()) { return false; } if (ModelInterface->GetState() == ESubmitToolAppState::Finished) { return true; } if (ModelInterface->GetState() == ESubmitToolAppState::WaitingUserInput || (ModelInterface->IsIntegrationRequired() && ModelInterface->bIsUserInAllowlist)) { return ModelInterface->IsCLValid(); } if(ModelInterface->IsIntegrationRequired()) { return ModelInterface->IsCLValid() || (ModelInterface->GetFilesInCL().IsEmpty() && ModelInterface->HasShelvedFiles() && ModelInterface->HasSubmitToolTag()); } return false; }) .OnClicked(this, &SubmitToolWidget::SubmitClicked) .ButtonStyle(FAppStyle::Get(), "PrimaryButton") [ SNew(STextBlock) .MinDesiredWidth(130) .Justification(ETextJustify::Center) .Text_Raw(this, &SubmitToolWidget::GetMainButtonText) ] ] +SUniformGridPanel::Slot(1, 0) [ ValidateBtn.ToSharedRef() ] ]; } TSharedRef SubmitToolWidget::BuildOutputLogWidget() { /*** Output Log Widget ***/ FOutputLogModule& OutputLogModule = FModuleManager::Get().LoadModuleChecked("OutputLog"); // hide the debug console IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("OutputLogModule.HideConsole")); if(ensure(CVar)) { CVar->Set(true); } // setup OutputLog settings UOutputLogSettings* Settings = GetMutableDefault(); if(Settings) { Settings->CategoryColorizationMode = ELogCategoryColorizationMode::ColorizeWholeLine; } ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); if(SettingsModule) { SettingsModule->RegisterSettings("Editor", "General", "Output Log", NSLOCTEXT("OutputLog", "OutputLogSettingsName", "Output Log"), NSLOCTEXT("OutputLog", "OutputLogSettingsDescription", "Set up preferences for the Output Log appearance and workflow."), Settings ); } LogTabManager = FGlobalTabmanager::Get()->NewTabManager(ParentTab.Pin().ToSharedRef()); LogTabManager->SetCanDoDragOperation(false); // Menu // TODO BC: this is a poor solution to have the menu maintained between the two tabs please revisit TSharedRef CommandList = MakeShared(); FSubmitToolCommandHandler CommandHandler; CommandHandler.AddToCommandList(ModelInterface, CommandList); FMenuBarBuilder MenuBarBuilder = FMenuBarBuilder(CommandList); MenuBarBuilder.AddPullDownMenu(LOCTEXT("MainMenu", "Main Menu"), LOCTEXT("OpensMainMenu", "Opens Main Menu"), FNewMenuDelegate::CreateStatic(&FSubmitToolMenu::FillMainMenuEntries)); #if !UE_BUILD_SHIPPING if(!FPaths::IsStaged()) { MenuBarBuilder.AddPullDownMenu(LOCTEXT("Debug Tools", "Debug"), LOCTEXT("OpensDebugMenu", "Opens Debug Menu"), FNewMenuDelegate::CreateStatic(&FSubmitToolMenu::FillDebugMenuEntries)); } #endif const TSharedRef MenuWidget = MenuBarBuilder.MakeWidget(); LogTabManager->SetAllowWindowMenuBar(true); LogTabManager->SetMenuMultiBox(MenuBarBuilder.GetMultiBox(), MenuWidget); const FName SummaryTabId = "SummaryLogTab"; const FName ValidatorLogTabId = "ValidatorLogTab"; const FName PresubmitLogTabId = "PresubmitLogTab"; // ---------------------------------------------------------------------- // Summary Log Tab // ---------------------------------------------------------------------- FOutputLogCreationParams SummaryLogCreationParams; SummaryLogCreationParams.AllowAsInitialLogCategory = FAllowLogCategoryCallback::CreateLambda([](const FName LogCategory) { return LogCategory == LogSubmitTool.GetCategoryName(); }); SummaryLogCreationParams.DefaultCategorySelection.Emplace(LogSubmitTool.GetCategoryName(), true); SummaryLogCreationParams.DefaultCategorySelection.Emplace(LogValidators.GetCategoryName(), false); SummaryLogCreationParams.DefaultCategorySelection.Emplace(LogValidatorsResult.GetCategoryName(), true); SummaryLogCreationParams.DefaultCategorySelection.Emplace(LogPresubmitResult.GetCategoryName(), true); SummaryLogCreationParams.DefaultCategorySelection.Emplace(LogOutputDevice.GetCategoryName(), true); SummaryLogCreationParams.DefaultCategorySelection.Emplace(LogSubmitToolP4.GetCategoryName(), true); SummaryLogCreationParams.DefaultCategorySelection.Emplace(LogPresubmit.GetCategoryName(), false); SummaryLogCreationParams.DefaultCategorySelection.Emplace("SourceControl", true); SummaryLogDockTab = SNew(SDockTab) .CanEverClose(false) .TabRole(ETabRole::PanelTab) .Label(FText::FromString(TEXT("Summary"))) .OnCanCloseTab(SDockTab::FCanCloseTab::CreateLambda([] { return false; })) [ OutputLogModule.MakeOutputLogWidget(SummaryLogCreationParams) ]; LogTabManager->RegisterTabSpawner(SummaryTabId, FOnSpawnTab::CreateLambda([this](const FSpawnTabArgs& SpawnArgs) { return SummaryLogDockTab.ToSharedRef(); })); // ---------------------------------------------------------------------- // Validators Log Tab // ---------------------------------------------------------------------- FOutputLogCreationParams ValidatorLogCreationParams; ValidatorLogCreationParams.AllowAsInitialLogCategory = FAllowLogCategoryCallback::CreateLambda([](const FName LogCategory) { return LogCategory == LogValidators.GetCategoryName(); }); ValidatorLogCreationParams.DefaultCategorySelection.Emplace(LogSubmitTool.GetCategoryName(), false); ValidatorLogCreationParams.DefaultCategorySelection.Emplace(LogValidators.GetCategoryName(), true); ValidatorLogCreationParams.DefaultCategorySelection.Emplace(LogValidatorsResult.GetCategoryName(), false); ValidatorLogCreationParams.DefaultCategorySelection.Emplace(LogPresubmitResult.GetCategoryName(), false); ValidatorLogCreationParams.DefaultCategorySelection.Emplace(LogOutputDevice.GetCategoryName(), false); ValidatorLogCreationParams.DefaultCategorySelection.Emplace(LogSubmitToolP4.GetCategoryName(), false); ValidatorLogCreationParams.DefaultCategorySelection.Emplace(LogPresubmit.GetCategoryName(), false); ValidatorLogTab = SNew(SDockTab) .CanEverClose(false) .Label(FText::FromString(TEXT("Validators Log"))) .TabRole(ETabRole::PanelTab) .OnCanCloseTab(SDockTab::FCanCloseTab::CreateLambda([] { return false; })) [ OutputLogModule.MakeOutputLogWidget(ValidatorLogCreationParams) ]; LogTabManager->RegisterTabSpawner(ValidatorLogTabId, FOnSpawnTab::CreateLambda([this](const FSpawnTabArgs& SpawnArgs) { return ValidatorLogTab.ToSharedRef(); })); // ---------------------------------------------------------------------- // Pre Submit Log Tab // ---------------------------------------------------------------------- FOutputLogCreationParams PresubmitLogCreationParams; PresubmitLogCreationParams.AllowAsInitialLogCategory = FAllowLogCategoryCallback::CreateLambda([](const FName LogCategory) { return LogCategory == LogPresubmit.GetCategoryName(); }); PresubmitLogCreationParams.DefaultCategorySelection.Emplace(LogPresubmit.GetCategoryName(), true); PresubmitLogCreationParams.DefaultCategorySelection.Emplace(LogSubmitTool.GetCategoryName(), false); PresubmitLogCreationParams.DefaultCategorySelection.Emplace(LogValidators.GetCategoryName(), false); PresubmitLogCreationParams.DefaultCategorySelection.Emplace(LogValidatorsResult.GetCategoryName(), false); PresubmitLogCreationParams.DefaultCategorySelection.Emplace(LogPresubmitResult.GetCategoryName(), false); PresubmitLogCreationParams.DefaultCategorySelection.Emplace(LogOutputDevice.GetCategoryName(), false); PresubmitLogCreationParams.DefaultCategorySelection.Emplace(LogSubmitToolP4.GetCategoryName(), false); PresubmitLogTab = SNew(SDockTab) .CanEverClose(false) .Label(FText::FromString(TEXT("Presubmit Log"))) .TabRole(ETabRole::PanelTab) .OnCanCloseTab(SDockTab::FCanCloseTab::CreateLambda([] { return false; })) [ OutputLogModule.MakeOutputLogWidget(PresubmitLogCreationParams) ]; LogTabManager->RegisterTabSpawner(PresubmitLogTabId, FOnSpawnTab::CreateLambda([this](const FSpawnTabArgs& SpawnArgs) { return PresubmitLogTab.ToSharedRef(); })); // ---------------------------------------------------------------------- // Logs Layout // ---------------------------------------------------------------------- TSharedRef Layout = FTabManager::NewLayout("SubmitToolLogLayout") ->AddArea ( FTabManager::NewPrimaryArea() ->SetOrientation(Orient_Horizontal) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(1.0f) ->SetHideTabWell(true) ->AddTab(SummaryTabId, ETabState::OpenedTab) ->AddTab(ValidatorLogTabId, ETabState::OpenedTab) ->AddTab(PresubmitLogTabId, ETabState::OpenedTab) ->SetForegroundTab(SummaryTabId) ) ); return LogTabManager->RestoreFrom(Layout, ParentTab.Pin()->GetParentWindow()).ToSharedRef(); } TSharedRef SubmitToolWidget::BuildFilesInCLWidget() { TSharedPtr> FileList = SNew(SListView) .SelectionMode(ESelectionMode::Single) .ListItemsSource(&ModelInterface->GetFilesInCL()) .OnGenerateRow_Lambda([this](FSourceControlStateRef InItem, const TSharedRef& OwnerTable) { return SNew(STableRow, OwnerTable) .Padding(2.0f) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .MaxWidth(24) .HAlign(HAlign_Left) [ SNew(SImage) .Image(InItem->GetIcon().GetIcon()) ] + SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Left) [ SNew(STextBlock) .Text(FText::FromString(InItem->GetFilename())) ] ]; }); ModelInterface->FileRefreshedCallback.AddLambda([FileList]() {FileList->RequestListRefresh(); }); return SNew(SExpandableArea) .InitiallyCollapsed(!FSubmitToolUserPrefs::Get()->bExpandFilesInCL) .OnAreaExpansionChanged_Lambda([](bool bExpanded) { FSubmitToolUserPrefs::Get()->bExpandFilesInCL = bExpanded; }) .BorderImage(FAppStyle::Get().GetBrush("Brushes.Header")) .BodyBorderImage(FAppStyle::Get().GetBrush("Brushes.Recessed")) .HeaderPadding(FMargin(4.0f, 2.0f)) .Padding(1.0f) .MaxHeight(200.0f) .AllowAnimatedTransition(true) .HeaderContent() [ SNew(STextBlock) .Text(NSLOCTEXT("SourceControl.SubmitPanel", "ChangeListFiles", "Files in Changelist")) ] .BodyContent() [ SNew(SBox) .Padding(2.5) [ FileList.ToSharedRef() ] ]; } FReply SubmitToolWidget::SubmitClicked() { if(ModelInterface->GetState() == ESubmitToolAppState::Finished) { ParentTab.Pin()->RequestCloseTab(); } else { if (ModelInterface->IsIntegrationRequired() && !ModelInterface->bIsUserInAllowlist) { IntegrationWidget->Open(); return FReply::Handled(); } if(ModelInterface->IsCLValid()) { ModelInterface->StartSubmitProcess(); } } return FReply::Handled(); } FReply SubmitToolWidget::ValidateClicked() { if(ModelInterface->IsValidationRunning()) { ModelInterface->CancelValidations(); } else { if(ModelInterface->IsP4OperationRunning()) { ModelInterface->CancelP4Operations(); } else { ModelInterface->ValidateChangelist(); } } return FReply::Handled(); } FText SubmitToolWidget::GetMainButtonText() const { FText FinalText; if(ModelInterface->GetState() == ESubmitToolAppState::Finished) { FinalText = NSLOCTEXT("SourceControl.SubmitPanel", "CloseButton", "Close"); } else { FinalText = ModelInterface->IsIntegrationRequired() && !ModelInterface->bIsUserInAllowlist ? NSLOCTEXT("SourceControl.SubmitPanel", "IntegrationButton", "Open Integration Window") : NSLOCTEXT("SourceControl.SubmitPanel", "SubmitButton", "Submit"); } return FinalText; } FText SubmitToolWidget::GetValidateButtonTooltip() const { FText FinalText; if(ModelInterface->IsValidationRunning()) { FinalText = NSLOCTEXT("SourceControl.SubmitPanel", "CancelValidateButtonTooltip", "Stops the currently running validations."); } else { if(ModelInterface->IsP4OperationRunning()) { FinalText = NSLOCTEXT("SourceControl.SubmitPanel", "CancelP4OpButtonTooltip", "Cancels the currently running P4 Operations."); } else { FinalText = NSLOCTEXT("SourceControl.SubmitPanel", "ValidateButtonTooltip", "Run all the validators for this changelist."); } } return FinalText; } FText SubmitToolWidget::GetValidateButtonText() const { FText FinalText; if(ModelInterface->IsValidationRunning()) { FinalText = NSLOCTEXT("SourceControl.SubmitPanel", "CancelValidateButton", "Stop Validations"); } else { if(ModelInterface->IsP4OperationRunning()) { FinalText = NSLOCTEXT("SourceControl.SubmitPanel", "CancelP4OpButtonText", "Cancel P4 Operations"); } else { FinalText = NSLOCTEXT("SourceControl.SubmitPanel", "ValidateButtonText", "Validate"); } } return FinalText; } void SubmitToolWidget::OnSingleValidatorFinished(const FValidatorBase& InValidator) { if(!InValidator.GetHasPassed()) { ValidatorLogTab->FlashTab(); } } void SubmitToolWidget::OnValidationUpdated(bool bValid) { if(bValid) { ValidateBtn->SetButtonStyle(&FAppStyle::Get().GetWidgetStyle("Button")); } else { ValidateBtn->SetButtonStyle(&FAppStyle::Get().GetWidgetStyle("PrimaryButton")); } } #if PLATFORM_WINDOWS FReply SubmitToolWidget::CopyAllLogsClicked() { TArray Files; for (const FString& Path : ModelInterface->GetParameters().CopyLogParameters.LogsToCollect) { Files.Emplace(FPaths::ConvertRelativePathToFull(FConfiguration::Substitute(Path))); } FSubmitToolUtils::CopyDiagnosticFilesToClipboard(Files); UE_LOG(LogSubmitTool,Display, TEXT("Log files have been copied to the clipboard")); return FReply::Handled(); } #endif #undef LOCTEXT_NAMESPACE