Files
UnrealEngineUWP/Engine/Source/Developer/ProjectLauncher/Private/Widgets/SProjectLauncherProgress.h
Jamie Dale bbb0624bff Fixed code relying on SLATE_TEXT_ATTRIBUTE for tooltips.
UETOOL-213 - Minimize Slate FString -> FText conversion (remove SLATE_TEXT_ATTRIBUTE)

This fixes any editor/engine specific code that was passing text to Slate as FString rather than FText.

[CL 2401019 by Jamie Dale in Main branch]
2015-01-08 11:35:01 -05:00

620 lines
16 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#define LOCTEXT_NAMESPACE "SProjectLauncherProgress"
/**
* Implements the launcher's progress page.
*/
class SProjectLauncherProgress
: public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SProjectLauncherProgress) { }
SLATE_EVENT(FOnClicked, OnCloseClicked)
SLATE_EVENT(FOnClicked, OnRerunClicked)
SLATE_END_ARGS()
public:
/**
* Destructor.
*/
~SProjectLauncherProgress( )
{
}
public:
/**
* Constructs the widget.
*
* @param InArgs The Slate argument list.
*/
void Construct( const FArguments& InArgs )
{
OnCloseClicked = InArgs._OnCloseClicked;
OnRerunClicked = InArgs._OnRerunClicked;
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(8.0, 16.0, 16.0, 0.0)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(this, &SProjectLauncherProgress::HandleProgressTextBlockText)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0, 4.0, 0.0, 0.0)
[
SAssignNew(ProgressBar, SProgressBar)
.Percent(this, &SProjectLauncherProgress::HandleProgressBarPercent)
]
]
+ SVerticalBox::Slot()
.FillHeight(0.5)
.Padding(0.0, 32.0, 8.0, 0.0)
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(0.0f)
[
SAssignNew(TaskListView, SListView<ILauncherTaskPtr>)
.HeaderRow
(
SNew(SHeaderRow)
+ SHeaderRow::Column("Icon")
.DefaultLabel(LOCTEXT("TaskListIconColumnHeader", " "))
.FixedWidth(20.0)
+ SHeaderRow::Column("Task")
.DefaultLabel(LOCTEXT("TaskListTaskColumnHeader", "Task"))
.FillWidth(1.0)
+ SHeaderRow::Column("Duration")
.DefaultLabel(LOCTEXT("TaskListDurationColumnHeader", "Duration"))
.FixedWidth(64.0)
+ SHeaderRow::Column("Status")
.DefaultLabel(LOCTEXT("TaskListStatusColumnHeader", "Status"))
.FixedWidth(80.0)
)
.ListItemsSource(&TaskList)
.OnGenerateRow(this, &SProjectLauncherProgress::HandleTaskListViewGenerateRow)
.ItemHeight(24.0)
.SelectionMode(ESelectionMode::Single)
]
]
//content area for the log
+ SVerticalBox::Slot()
.FillHeight(0.5)
.Padding(0.0, 32.0, 8.0, 0.0)
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(0.0f)
[
SAssignNew(MessageListView, SListView< TSharedPtr<FProjectLauncherMessage> >)
.HeaderRow
(
SNew(SHeaderRow)
+ SHeaderRow::Column("Status")
.DefaultLabel(LOCTEXT("TaskListStatusColumnHeader", "Status"))
.FillWidth(1.0)
)
.ListItemsSource(&MessageList)
.OnGenerateRow(this, &SProjectLauncherProgress::HandleMessageListViewGenerateRow)
.ItemHeight(24.0)
.SelectionMode(ESelectionMode::Multi)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 5.0f, 0.0f, 0.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[
// copy button
SAssignNew(CopyButton, SButton)
.ContentPadding(FMargin(6.0f, 2.0f))
.IsEnabled(false)
.Text(LOCTEXT("CopyButtonText", "Copy"))
.ToolTipText(LOCTEXT("CopyButtonTooltip", "Copy the selected log messages to the clipboard"))
.OnClicked(this, &SProjectLauncherProgress::HandleCopyButtonClicked)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
[
// clear button
SAssignNew(ClearButton, SButton)
.ContentPadding(FMargin(6.0f, 2.0f))
.IsEnabled(false)
.Text(LOCTEXT("ClearButtonText", "Clear Log"))
.ToolTipText(LOCTEXT("ClearButtonTooltip", "Clear the log window"))
.OnClicked(this, &SProjectLauncherProgress::HandleClearButtonClicked)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
[
// save button
SAssignNew(SaveButton, SButton)
.ContentPadding(FMargin(6.0f, 2.0f))
.IsEnabled(false)
.Text(LOCTEXT("ExportButtonText", "Save Log..."))
.ToolTipText(LOCTEXT("SaveButtonTooltip", "Save the entire log to a file"))
.Visibility((FDesktopPlatformModule::Get() != NULL) ? EVisibility::Visible : EVisibility::Collapsed)
.OnClicked(this, &SProjectLauncherProgress::HandleSaveButtonClicked)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
[
// Re-Run button
SNew(SButton)
.ContentPadding(FMargin(6.0f, 2.0f))
.IsEnabled(this, &SProjectLauncherProgress::IsRerunButtonEnabled)
.OnClicked(this, &SProjectLauncherProgress::HandleRerunButtonClicked)
.ToolTipText(this, &SProjectLauncherProgress::GetRerunButtonToolTip)
[
SNew(STextBlock)
.Text(this, &SProjectLauncherProgress::GetRerunButtonText)
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
[
// Cancel / Done button
SNew(SButton)
.ContentPadding(FMargin(6.0f, 2.0f))
.IsEnabled(this, &SProjectLauncherProgress::IsDoneButtonEnabled)
.OnClicked(this, &SProjectLauncherProgress::HandleDoneButtonClicked)
.ToolTipText(this, &SProjectLauncherProgress::GetDoneButtonToolTip)
[
SNew(STextBlock)
.Text(this, &SProjectLauncherProgress::GetDoneButtonText)
]
]
]
];
}
/**
* Sets the launcher worker to track the progress for.
*
* @param Worker The launcher worker.
*/
void SetLauncherWorker( const ILauncherWorkerRef& Worker )
{
LauncherWorker = Worker;
Worker->GetTasks(TaskList);
TaskListView->RequestListRefresh();
MessageList.Reset();
Worker->OnOutputReceived().AddRaw(this, &SProjectLauncherProgress::HandleOutputReceived);
MessageListView->RequestListRefresh();
}
void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override
{
if (PendingMessages.Num() > 0)
{
FScopeLock ScopedLock(&CriticalSection);
for (int32 Index = 0; Index < PendingMessages.Num(); ++Index)
{
MessageList.Add(PendingMessages[Index]);
}
PendingMessages.Reset();
MessageListView->RequestListRefresh();
MessageListView->RequestScrollIntoView(MessageList.Last());
}
SaveButton->SetEnabled(MessageList.Num() > 0);
ClearButton->SetEnabled(MessageList.Num() > 0);
CopyButton->SetEnabled(MessageListView->GetNumItemsSelected() > 0);
}
private:
void HandleOutputReceived(const FString& InMessage)
{
FScopeLock ScopedLock(&CriticalSection);
ELogVerbosity::Type Verbosity = ELogVerbosity::Log;
if (InMessage.Contains(TEXT("Automation.ParseCommandLine:"), ESearchCase::CaseSensitive))
{
Verbosity = ELogVerbosity::Display;
}
else if ( InMessage.Contains(TEXT("Error:"), ESearchCase::IgnoreCase) )
{
Verbosity = ELogVerbosity::Error;
}
else if ( InMessage.Contains(TEXT("Warning:"), ESearchCase::IgnoreCase) )
{
Verbosity = ELogVerbosity::Warning;
}
TSharedPtr<FProjectLauncherMessage> Message = MakeShareable(new FProjectLauncherMessage(FText::FromString(InMessage), Verbosity));
PendingMessages.Add(Message);
}
// Callback for getting the filled percentage of the progress bar.
TOptional<float> HandleProgressBarPercent( ) const
{
if (TaskList.Num() > 0)
{
ILauncherWorkerPtr LauncherWorkerPtr = LauncherWorker.Pin();
if (LauncherWorkerPtr.IsValid())
{
int32 NumFinished = 0;
for (int32 TaskIndex = 0; TaskIndex < TaskList.Num(); ++TaskIndex)
{
if (TaskList[TaskIndex]->IsFinished())
{
++NumFinished;
}
}
return ((float)NumFinished / TaskList.Num());
}
}
return 0.0f;
}
// Callback for getting the text of the progress text box.
FText HandleProgressTextBlockText( ) const
{
ILauncherWorkerPtr LauncherWorkerPtr = LauncherWorker.Pin();
if (LauncherWorkerPtr.IsValid())
{
if ((LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Busy) ||
(LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Canceling))
{
return LOCTEXT("OperationInProgressText", "Operation in progress...");
}
int32 NumCanceled = 0;
int32 NumCompleted = 0;
int32 NumFailed = 0;
for (int32 TaskIndex = 0; TaskIndex < TaskList.Num(); ++TaskIndex)
{
ELauncherTaskStatus::Type TaskStatus = TaskList[TaskIndex]->GetStatus();
if (TaskStatus == ELauncherTaskStatus::Canceled)
{
++NumCanceled;
}
else if (TaskStatus == ELauncherTaskStatus::Completed)
{
++NumCompleted;
}
else if (TaskStatus == ELauncherTaskStatus::Failed)
{
++NumFailed;
}
}
FFormatNamedArguments Args;
Args.Add(TEXT("NumCompleted"), NumCompleted);
Args.Add(TEXT("NumFailed"), NumFailed);
Args.Add(TEXT("NumCanceled"), NumCanceled);
return FText::Format(LOCTEXT("TasksFinishedFormatText", "Operation finished. Completed: {NumCompleted}, Failed: {NumFailed}, Canceled: {NumCanceled}"), Args);
}
return FText::GetEmpty();
}
// Callback for generating a row in the task list view.
TSharedRef<ITableRow> HandleTaskListViewGenerateRow( ILauncherTaskPtr InItem, const TSharedRef<STableViewBase>& OwnerTable ) const
{
return SNew(SProjectLauncherTaskListRow)
.Task(InItem)
.OwnerTableView(OwnerTable);
}
// Callback for generating a row in the task list view.
TSharedRef<ITableRow> HandleMessageListViewGenerateRow( TSharedPtr<FProjectLauncherMessage> InItem, const TSharedRef<STableViewBase>& OwnerTable ) const
{
return SNew(SProjectLauncherMessageListRow, OwnerTable)
.Message(InItem)
.ToolTipText(InItem->Message);
}
FReply HandleClearButtonClicked( )
{
ClearLog();
return FReply::Handled();
}
FReply HandleCopyButtonClicked( )
{
CopyLog();
return FReply::Handled();
}
FReply HandleSaveButtonClicked( )
{
SaveLog();
return FReply::Handled();
}
bool IsRerunButtonEnabled() const
{
ILauncherWorkerPtr LauncherWorkerPtr = LauncherWorker.Pin();
if (LauncherWorkerPtr.IsValid() &&
(LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Canceled || LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Completed))
{
return true;
}
return false;
}
FReply HandleRerunButtonClicked()
{
if (OnRerunClicked.IsBound())
{
return OnRerunClicked.Execute();
}
return FReply::Handled();
}
FText GetRerunButtonToolTip() const
{
return LOCTEXT("DoneButtonCloseTooltip", "Run this launch profile.");
}
FText GetRerunButtonText() const
{
return LOCTEXT("DoneButtonDoneLabel", "Run");
}
bool IsDoneButtonEnabled() const
{
ILauncherWorkerPtr LauncherWorkerPtr = LauncherWorker.Pin();
if (LauncherWorkerPtr.IsValid() && LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Canceling)
{
return false;
}
return true;
}
FReply HandleDoneButtonClicked()
{
ILauncherWorkerPtr LauncherWorkerPtr = LauncherWorker.Pin();
if (LauncherWorkerPtr.IsValid() && LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Busy)
{
LauncherWorkerPtr->Cancel();
}
else if (LauncherWorkerPtr.IsValid() && LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Canceling)
{
// do nothing
}
else if (OnCloseClicked.IsBound())
{
return OnCloseClicked.Execute();
}
return FReply::Handled();
}
FText GetDoneButtonToolTip() const
{
ILauncherWorkerPtr LauncherWorkerPtr = LauncherWorker.Pin();
if (LauncherWorkerPtr.IsValid())
{
if (LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Busy)
{
return LOCTEXT("DoneButtonCancelTooltip", "Cancel the run of this profile.");
}
else if (LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Canceling)
{
return LOCTEXT("DoneButtonCancellingTooltip", "Currently canceling.");
}
}
return LOCTEXT("DoneButtonCloseTooltip", "Close this page.");
}
FText GetDoneButtonText() const
{
ILauncherWorkerPtr LauncherWorkerPtr = LauncherWorker.Pin();
if (LauncherWorkerPtr.IsValid())
{
if (LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Busy)
{
return LOCTEXT("DoneButtonCancelLabel", "Cancel");
}
else if (LauncherWorkerPtr->GetStatus() == ELauncherWorkerStatus::Canceling)
{
return LOCTEXT("DoneButtonCancellingLabel", "Cancelling");
}
}
return LOCTEXT("DoneButtonDoneLabel", "Done");
}
void ClearLog()
{
MessageList.Reset();
MessageListView->RequestListRefresh();
}
void CopyLog()
{
TArray<TSharedPtr<FProjectLauncherMessage > > SelectedItems = MessageListView->GetSelectedItems();
if (SelectedItems.Num() > 0)
{
FString SelectedText;
for( int32 Index = 0; Index < SelectedItems.Num(); ++Index )
{
SelectedText += SelectedItems[Index]->Message.ToString();
SelectedText += LINE_TERMINATOR;
}
FPlatformMisc::ClipboardCopy( *SelectedText );
}
}
void SaveLog()
{
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
if (DesktopPlatform != NULL)
{
TArray<FString> Filenames;
TSharedPtr<SWindow> ParentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared());
void* ParentWindowHandle = (ParentWindow.IsValid() && ParentWindow->GetNativeWindow().IsValid()) ? ParentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
if (DesktopPlatform->SaveFileDialog(
ParentWindowHandle,
LOCTEXT("SaveLogDialogTitle", "Save Log As...").ToString(),
LastLogFileSaveDirectory,
TEXT("ProjectLauncher.log"),
TEXT("Log Files (*.log)|*.log"),
EFileDialogFlags::None,
Filenames))
{
if (Filenames.Num() > 0)
{
FString Filename = Filenames[0];
// keep path as default for next time
LastLogFileSaveDirectory = FPaths::GetPath(Filename);
// add a file extension if none was provided
if (FPaths::GetExtension(Filename).IsEmpty())
{
Filename += Filename + TEXT(".log");
}
// save file
FArchive* LogFile = IFileManager::Get().CreateFileWriter(*Filename);
if (LogFile != NULL)
{
for( int32 Index = 0; Index < MessageList.Num(); ++Index )
{
FString LogEntry = MessageList[Index]->Message.ToString() + LINE_TERMINATOR;
LogFile->Serialize(TCHAR_TO_ANSI(*LogEntry), LogEntry.Len());
}
LogFile->Close();
delete LogFile;
}
else
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SaveLogDialogFileError", "Failed to open the specified file for saving!"));
}
}
}
}
else
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SaveLogDialogUnsupportedError", "Saving is not supported on this platform!"));
}
}
private:
// Holds the launcher worker.
TWeakPtr<ILauncherWorker> LauncherWorker;
// Holds the output log.
TArray<TSharedPtr<FString>> OutputList;
// Holds the output list view.
TSharedPtr<SListView<TSharedPtr<FString>>> OutputListView;
// Holds the progress bar.
TSharedPtr<SProgressBar> ProgressBar;
// Holds the task list.
TArray<ILauncherTaskPtr> TaskList;
// Holds the message list.
TArray< TSharedPtr<FProjectLauncherMessage>> MessageList;
// Holds the filtered message list.
TArray< TSharedPtr<FProjectLauncherMessage>> FilterMessageList;
// Holds the pending message list.
TArray< TSharedPtr<FProjectLauncherMessage>> PendingMessages;
// Holds the message list view.
TSharedPtr<SListView<TSharedPtr<FProjectLauncherMessage>>> MessageListView;
// Holds the task list view.
TSharedPtr<SListView<ILauncherTaskPtr>> TaskListView;
// Holds the box of task statuses.
TSharedPtr<SVerticalBox> TaskStatusBox;
// Critical section for updating the messages
FCriticalSection CriticalSection;
// Holds the directory where the log file was last saved to.
FString LastLogFileSaveDirectory;
// Holds the copy log button.
TSharedPtr<SButton> CopyButton;
// Holds the clear button.
TSharedPtr<SButton> ClearButton;
// Holds the save button.
TSharedPtr<SButton> SaveButton;
// Holds a delegate to be invoked when this panel is closed.
FOnClicked OnCloseClicked;
// Holds a delegate to be invoked when we want the launch profile rerun.
FOnClicked OnRerunClicked;
};
#undef LOCTEXT_NAMESPACE