Files
UnrealEngine/Engine/Source/Developer/SourceControl/Private/ScopedSourceControlProgress.cpp
2025-05-18 13:04:45 +08:00

258 lines
6.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ScopedSourceControlProgress.h"
#if SOURCE_CONTROL_WITH_SLATE
#include "Framework/Application/SlateApplication.h"
#include "Framework/Docking/TabManager.h"
#include "ISourceControlProvider.h"
#include "Input/Reply.h"
#include "Layout/Visibility.h"
#include "Misc/App.h"
#include "RHI.h"
#include "RenderingThread.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Images/SThrobber.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SWindow.h"
#include "Widgets/Text/STextBlock.h"
#define LOCTEXT_NAMESPACE "SourceControlProgress"
namespace SourceControlConstants
{
/** The time (in seconds) we wait before letting the user know that an operation is taking a while */
float OperationTimeOut = 10.0f;
}
class SSourceControlProgress : public SBorder
{
public:
SLATE_BEGIN_ARGS(SSourceControlProgress) {}
/** The text to display */
SLATE_ATTRIBUTE(FText, Text)
/** The delegate to call when the cancel button is clicked */
SLATE_ARGUMENT(FSimpleDelegate, OnCancelled)
SLATE_END_ARGS()
public:
void Construct(const FArguments& InArgs)
{
OnCancelled = InArgs._OnCancelled;
bCancelClicked = false;
TimeStamp = FPlatformTime::Seconds();
SBorder::Construct( SBorder::FArguments()
.BorderImage( FAppStyle::Get().GetBrush("ChildWindow.Background") )
.Padding(16.0f)
.VAlign(VAlign_Center)
[
SNew(SBox)
.WidthOverride(500.0f)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f)
[
SNew(STextBlock)
.Text(InArgs._Text)
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f)
[
SNew(SHorizontalBox)
.Visibility(this, &SSourceControlProgress::GetWarningVisibility)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
[
SNew(SImage)
.Image(FAppStyle::Get().GetBrush("SourceControl.ProgressWindow.Warning"))
]
+SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.AutoWidth()
[
SNew(STextBlock)
.Text(LOCTEXT("LongTaskMessage", "Operation is taking a long time to complete. Click cancel to stop the current operation, you can try again later."))
.WrapTextAt(450.0f)
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 8.0f, 0.0f, 0.0f)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1.0f)
.VAlign(VAlign_Center)
.HAlign(HAlign_Left)
[
SNew(SThrobber)
]
+SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
[
// buttons
SNew(SUniformGridPanel)
.SlotPadding(FAppStyle::Get().GetMargin("StandardDialog.SlotPadding"))
.MinDesiredSlotWidth(FAppStyle::Get().GetFloat("StandardDialog.MinDesiredSlotWidth"))
.MinDesiredSlotHeight(FAppStyle::Get().GetFloat("StandardDialog.MinDesiredSlotHeight"))
+SUniformGridPanel::Slot(0, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.Text(LOCTEXT("ContinueButtonLabel", "Continue"))
.OnClicked(this, &SSourceControlProgress::OnContinueClicked)
.Visibility(this, &SSourceControlProgress::GetWarningVisibility)
]
+SUniformGridPanel::Slot(1, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.Text(LOCTEXT("CancelButtonLabel", "Cancel"))
.OnClicked(this, &SSourceControlProgress::OnCancelClicked)
.IsEnabled(this, &SSourceControlProgress::IsCancelEnabled)
.Visibility(this, &SSourceControlProgress::GetCancelVisibility)
]
]
]
]
]);
}
private:
/** Delegate used to get the visibility of the cancel button */
EVisibility GetCancelVisibility() const
{
return OnCancelled.IsBound() ? EVisibility::Visible : EVisibility::Collapsed;
}
/** Delegate used to get the visibility of the warning text */
EVisibility GetWarningVisibility() const
{
if(!OnCancelled.IsBound())
{
return EVisibility::Collapsed;
}
else if(FPlatformTime::Seconds() - TimeStamp > SourceControlConstants::OperationTimeOut)
{
return EVisibility::Visible;
}
return EVisibility::Hidden;
}
/** Handler for the continue button */
FReply OnContinueClicked()
{
TimeStamp = FPlatformTime::Seconds();
return FReply::Handled();
}
/** Handler for the cancel button */
FReply OnCancelClicked()
{
bCancelClicked = true;
OnCancelled.ExecuteIfBound();
return FReply::Handled();
}
/** Delegate used to get the enabled state of the cancel button */
bool IsCancelEnabled() const
{
return !bCancelClicked;
}
private:
/** Flag used to disable the cancel button once clicked */
bool bCancelClicked;
/** The timer we use to determine when to display the 'long task' message */
double TimeStamp;
/** The delegate to call when the cancel button is clicked */
FSimpleDelegate OnCancelled;
};
#endif // SOURCE_CONTROL_WITH_SLATE
FScopedSourceControlProgress::FScopedSourceControlProgress(const FText& InText, const FSimpleDelegate& InOnCancelled)
{
#if SOURCE_CONTROL_WITH_SLATE
if(!(FApp::IsUnattended() || IsRunningCommandlet()) && !InText.IsEmpty())
{
TSharedRef<SWindow> Window = SNew(SWindow)
.IsPopupWindow(true)
.SupportsMaximize(false)
.SupportsMinimize(false)
.CreateTitleBar(false)
.SizingRule(ESizingRule::Autosized);
WindowPtr = Window;
Window->SetContent(
SNew(SSourceControlProgress)
.Text(InText)
.OnCancelled(InOnCancelled)
);
TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow();
FSlateApplication::Get().AddModalWindow(Window, RootWindow, true);
Window->ShowWindow();
Tick();
}
#endif //SOURCE_CONTROL_WITH_SLATE
}
FScopedSourceControlProgress::~FScopedSourceControlProgress()
{
#if SOURCE_CONTROL_WITH_SLATE
if(WindowPtr.IsValid())
{
WindowPtr.Pin()->RequestDestroyWindow();
}
#endif
}
void FScopedSourceControlProgress::Tick()
{
#if SOURCE_CONTROL_WITH_SLATE
if (!(FApp::IsUnattended() || IsRunningCommandlet()) && WindowPtr.IsValid() && FSlateApplication::Get().CanDisplayWindows())
{
// Testing if we are already ticking the rendering. That is to prevent a double "EndFrame" in case the user wrongly uses the FSlateApplication::OnPreTick to start a slow task.
bool bIsTicking = FSlateApplication::Get().IsTicking();
// Tick Slate application
FSlateApplication::Get().Tick();
// End frame so frame fence number gets incremented
if (!bIsTicking && GIsRHIInitialized)
{
ENQUEUE_RENDER_COMMAND(EndFrameCmd)([](FRHICommandListImmediate& RHICmdList) { RHICmdList.EndFrame(); });
}
// Sync the game thread and the render thread. This is needed if many StatusUpdate are called.
FSlateApplication::Get().GetRenderer()->Sync();
}
#endif
}
#undef LOCTEXT_NAMESPACE