2026-07-03 19:56:31 +02:00
|
|
|
|
// Copyright Steve Streeting 2022
|
|
|
|
|
|
// Released under the MIT license https://opensource.org/license/MIT/
|
|
|
|
|
|
#include "SUDSEditorToolkit.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "EditorReimportHandler.h"
|
|
|
|
|
|
#include "IDetailRootObjectCustomization.h"
|
|
|
|
|
|
#include "IDocumentation.h"
|
|
|
|
|
|
#include "SUDSDialogue.h"
|
|
|
|
|
|
#include "SUDSEditorScriptTools.h"
|
|
|
|
|
|
#include "SUDSEditorVoiceOverTools.h"
|
|
|
|
|
|
#include "SUDSLibrary.h"
|
|
|
|
|
|
#include "SUDSMessageLogger.h"
|
|
|
|
|
|
#include "SUDSScript.h"
|
|
|
|
|
|
#include "SUDSScriptNodeText.h"
|
|
|
|
|
|
#include "SUDSSubsystem.h"
|
|
|
|
|
|
#include "Framework/Text/SlateTextRun.h"
|
|
|
|
|
|
#include "Misc/MessageDialog.h"
|
|
|
|
|
|
#include "Widgets/Docking/SDockTab.h"
|
|
|
|
|
|
#include "Widgets/Input/SButton.h"
|
|
|
|
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
|
|
|
|
#include "Widgets/Input/SComboButton.h"
|
|
|
|
|
|
#include "Widgets/Input/SEditableTextBox.h"
|
|
|
|
|
|
#include "Widgets/Input/SMultiLineEditableTextBox.h"
|
|
|
|
|
|
#include "Widgets/Input/SNumericEntryBox.h"
|
|
|
|
|
|
#include "Widgets/SToolTip.h"
|
|
|
|
|
|
#include "Widgets/Views/SListView.h"
|
|
|
|
|
|
#include "Toolkits/AssetEditorToolkit.h"
|
|
|
|
|
|
#include "PropertyEditorModule.h"
|
|
|
|
|
|
|
|
|
|
|
|
const FName NAME_SpeakerLine("SpeakerLine");
|
|
|
|
|
|
const FName NAME_Choice("Choice");
|
|
|
|
|
|
const FName NAME_VariableSet("VariableSet");
|
|
|
|
|
|
const FName NAME_VariableEdit("VariableEdit");
|
|
|
|
|
|
const FName NAME_SelectEval("Condition");
|
|
|
|
|
|
const FName NAME_RandomEval("Random");
|
|
|
|
|
|
const FName NAME_Event("Event");
|
|
|
|
|
|
const FName NAME_Start("Start");
|
|
|
|
|
|
const FName NAME_Finish("Finish");
|
|
|
|
|
|
|
|
|
|
|
|
FName FSUDSEditorToolkit::DialogueOutputTabName("SUDSDialogueTab");
|
|
|
|
|
|
FName FSUDSEditorToolkit::VariablesTabName("SUDSVariablesTab");
|
|
|
|
|
|
FName FSUDSEditorToolkit::DetailsTabName("SUDSDetailsTab");
|
|
|
|
|
|
FName FSUDSEditorToolkit::LogTabName("SUDSLogTab");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FSUDSDetailRootObjectCustomization : public IDetailRootObjectCustomization
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
virtual TSharedPtr<SWidget> CustomizeObjectHeader(const FDetailsObjectSet& InRootObjectSet,
|
|
|
|
|
|
const TSharedPtr<ITableRow>& InTableRow) override
|
|
|
|
|
|
{
|
|
|
|
|
|
auto Ret = SNew(STextBlock)
|
|
|
|
|
|
.Margin(FMargin(10,5))
|
|
|
|
|
|
.TextStyle(&FAppStyle::Get().GetWidgetStyle<FTextBlockStyle>("SmallText"));
|
|
|
|
|
|
if (auto TN = Cast<USUDSScriptNodeText>(InRootObjectSet.RootObjects[0]))
|
|
|
|
|
|
{
|
|
|
|
|
|
Ret->SetText(FText::FromString(FString::Printf(TEXT("Speaker Line - ID: %s Source Line: %d"), *TN->GetTextID(), TN->GetSourceLineNo())));
|
|
|
|
|
|
}
|
|
|
|
|
|
return Ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virtual bool ShouldDisplayHeader(const FDetailsObjectSet& InRootObjectSet) const override
|
|
|
|
|
|
{
|
|
|
|
|
|
return InRootObjectSet.RootObjects[0]->IsA(USUDSScriptNodeText::StaticClass());
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::InitEditor(const TArray<UObject*>& InObjects)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (InObjects.Num() > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Script = Cast<USUDSScript>(InObjects[0]);
|
|
|
|
|
|
|
|
|
|
|
|
ReimportDelegateHandle = FReimportManager::Instance()->OnPostReimport().AddRaw(this, &FSUDSEditorToolkit::OnPostReimport);
|
|
|
|
|
|
|
|
|
|
|
|
const TSharedRef<FTabManager::FLayout> Layout = FTabManager::NewLayout("SUDSEditorLayout_v3")
|
|
|
|
|
|
->AddArea
|
|
|
|
|
|
(
|
|
|
|
|
|
FTabManager::NewPrimaryArea()->SetOrientation(Orient_Horizontal)
|
|
|
|
|
|
->Split
|
|
|
|
|
|
(
|
|
|
|
|
|
FTabManager::NewSplitter()
|
|
|
|
|
|
->SetSizeCoefficient(0.75f)
|
|
|
|
|
|
->SetOrientation(Orient_Horizontal)
|
|
|
|
|
|
->Split
|
|
|
|
|
|
(
|
|
|
|
|
|
FTabManager::NewSplitter()
|
|
|
|
|
|
->SetSizeCoefficient(0.6f)
|
|
|
|
|
|
->SetOrientation(Orient_Vertical)
|
|
|
|
|
|
->Split
|
|
|
|
|
|
(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FTabManager::NewStack()
|
|
|
|
|
|
->SetSizeCoefficient(0.8f)
|
|
|
|
|
|
->AddTab("SUDSDialogueTab", ETabState::OpenedTab)
|
|
|
|
|
|
)
|
|
|
|
|
|
->Split
|
|
|
|
|
|
(
|
|
|
|
|
|
FTabManager::NewStack()
|
|
|
|
|
|
->SetSizeCoefficient(0.4f)
|
|
|
|
|
|
->AddTab("SUDSLogTab", ETabState::OpenedTab)
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
->Split
|
|
|
|
|
|
(
|
|
|
|
|
|
FTabManager::NewSplitter()
|
|
|
|
|
|
->SetSizeCoefficient(0.25f)
|
|
|
|
|
|
->SetOrientation(Orient_Vertical)
|
|
|
|
|
|
->Split
|
|
|
|
|
|
(
|
|
|
|
|
|
FTabManager::NewStack()
|
|
|
|
|
|
->SetSizeCoefficient(0.6f)
|
|
|
|
|
|
->AddTab("SUDSVariablesTab", ETabState::OpenedTab)
|
|
|
|
|
|
)
|
|
|
|
|
|
->Split
|
|
|
|
|
|
(
|
|
|
|
|
|
FTabManager::NewStack()
|
|
|
|
|
|
->SetSizeCoefficient(0.4f)
|
|
|
|
|
|
->AddTab("SUDSDetailsTab", ETabState::OpenedTab)
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
FAssetEditorToolkit::InitAssetEditor(EToolkitMode::Standalone, {}, "SUDSEditor", Layout, true, true, InObjects);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
|
|
|
|
|
{
|
|
|
|
|
|
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
|
|
|
|
|
|
|
|
|
|
|
|
WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(INVTEXT("SUDS Editor"));
|
|
|
|
|
|
|
|
|
|
|
|
InTabManager->RegisterTabSpawner(DialogueOutputTabName, FOnSpawnTab::CreateLambda([this](const FSpawnTabArgs&)
|
|
|
|
|
|
{
|
|
|
|
|
|
OutputListView = SNew(SListView<TSharedPtr<FSUDSEditorOutputRow>>)
|
|
|
|
|
|
#if ENGINE_MINOR_VERSION < 5
|
|
|
|
|
|
.ItemHeight(24)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
.SelectionMode(ESelectionMode::None)
|
|
|
|
|
|
.ListItemsSource(&OutputRows)
|
|
|
|
|
|
.OnGenerateRow(this, &FSUDSEditorToolkit::OnGenerateRowForOutput)
|
|
|
|
|
|
.HeaderRow(
|
|
|
|
|
|
SNew(SHeaderRow)
|
|
|
|
|
|
+ SHeaderRow::Column("PrefixHeader")
|
|
|
|
|
|
.FillSized(PrefixColumnWidth)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew( STextBlock )
|
|
|
|
|
|
.Text( INVTEXT("") )
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
+ SHeaderRow::Column("LineHeader")
|
|
|
|
|
|
.FillWidth(1.0f)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew( STextBlock )
|
|
|
|
|
|
.Text( INVTEXT("") )
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ChoicesBox = SNew(SVerticalBox);
|
|
|
|
|
|
|
|
|
|
|
|
return SNew(SDockTab)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
+SVerticalBox::Slot()
|
|
|
|
|
|
.FillHeight(1)
|
|
|
|
|
|
[
|
|
|
|
|
|
OutputListView.ToSharedRef()
|
|
|
|
|
|
]
|
|
|
|
|
|
+SVerticalBox::Slot()
|
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
|
.HAlign(HAlign_Left)
|
|
|
|
|
|
.Padding(30, 15, 30, 15)
|
|
|
|
|
|
[
|
|
|
|
|
|
ChoicesBox.ToSharedRef()
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}))
|
|
|
|
|
|
.SetDisplayName(INVTEXT("Dialogue Output"))
|
|
|
|
|
|
.SetGroup(WorkspaceMenuCategory.ToSharedRef());
|
|
|
|
|
|
|
|
|
|
|
|
InTabManager->RegisterTabSpawner(VariablesTabName, FOnSpawnTab::CreateLambda([this](const FSpawnTabArgs&)
|
|
|
|
|
|
{
|
|
|
|
|
|
VariablesListView = SNew(SListView<TSharedPtr<FSUDSEditorVariableRow>>)
|
|
|
|
|
|
#if ENGINE_MINOR_VERSION < 5
|
|
|
|
|
|
.ItemHeight(28)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
.SelectionMode(ESelectionMode::None)
|
|
|
|
|
|
.ListItemsSource(&VariableRows)
|
|
|
|
|
|
.OnGenerateRow(this, &FSUDSEditorToolkit::OnGenerateRowForVariable)
|
|
|
|
|
|
.HeaderRow(
|
|
|
|
|
|
SNew(SHeaderRow)
|
|
|
|
|
|
+ SHeaderRow::Column("NameHeader")
|
|
|
|
|
|
.FillSized(VarColumnWidth)
|
|
|
|
|
|
.VAlignCell(VAlign_Center)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text(INVTEXT("Name"))
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
+ SHeaderRow::Column("ValueHeader")
|
|
|
|
|
|
.FillWidth(1.0f)
|
|
|
|
|
|
.VAlignCell(VAlign_Fill)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text(INVTEXT("Value"))
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// To ensure globals are pre-populated
|
|
|
|
|
|
UpdateVariables();
|
|
|
|
|
|
|
|
|
|
|
|
// Possibly use a SPropertyTable with a custom IPropertyTable to implement variable binding
|
|
|
|
|
|
return SNew(SDockTab)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
|
.VAlign(VAlign_Top)
|
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.HAlign(HAlign_Right)
|
|
|
|
|
|
.Padding(2)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
|
.Text(INVTEXT("Add Variable"))
|
|
|
|
|
|
.OnClicked(this, &FSUDSEditorToolkit::AddVariableClicked)
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
|
.FillHeight(1.0)
|
|
|
|
|
|
[
|
|
|
|
|
|
VariablesListView.ToSharedRef()
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}))
|
|
|
|
|
|
.SetDisplayName(INVTEXT("Variables"))
|
|
|
|
|
|
.SetGroup(WorkspaceMenuCategory.ToSharedRef());
|
|
|
|
|
|
|
|
|
|
|
|
InTabManager->RegisterTabSpawner(DetailsTabName, FOnSpawnTab::CreateLambda([this](const FSpawnTabArgs&)
|
|
|
|
|
|
{
|
|
|
|
|
|
FPropertyEditorModule& PropertyEditorModule = FModuleManager::Get().LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
|
|
|
|
FDetailsViewArgs DetailsViewArgs;
|
|
|
|
|
|
DetailsViewArgs.bShowOptions = false;
|
|
|
|
|
|
DetailsViewArgs.bUpdatesFromSelection = false;
|
|
|
|
|
|
DetailsViewArgs.bShowPropertyMatrixButton = false;
|
|
|
|
|
|
DetailsViewArgs.bAllowSearch = false;
|
|
|
|
|
|
DetailsViewArgs.bAllowMultipleTopLevelObjects = true;
|
|
|
|
|
|
DetailsViewArgs.bShowObjectLabel = false;
|
|
|
|
|
|
DetailsViewArgs.bHideSelectionTip = true;
|
|
|
|
|
|
auto DetailView = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
|
|
|
|
|
|
DetailView->SetRootObjectCustomizationInstance(MakeShareable(new FSUDSDetailRootObjectCustomization()));
|
|
|
|
|
|
TArray<UObject*> ObjectsInDetailView;
|
|
|
|
|
|
ObjectsInDetailView.Add(Script);
|
|
|
|
|
|
for (auto Node : Script->GetNodes())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (auto TNode = Cast<USUDSScriptNodeText>(Node))
|
|
|
|
|
|
{
|
|
|
|
|
|
ObjectsInDetailView.Add(TNode);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
DetailView->SetObjects(ObjectsInDetailView, true, true);
|
|
|
|
|
|
// Possibly use a SPropertyTable with a custom IPropertyTable to implement variable binding
|
|
|
|
|
|
return SNew(SDockTab)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
|
.VAlign(VAlign_Top)
|
|
|
|
|
|
[
|
|
|
|
|
|
DetailView
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}))
|
|
|
|
|
|
.SetDisplayName(INVTEXT("Details"))
|
|
|
|
|
|
.SetGroup(WorkspaceMenuCategory.ToSharedRef());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InTabManager->RegisterTabSpawner(LogTabName, FOnSpawnTab::CreateLambda([this](const FSpawnTabArgs&)
|
|
|
|
|
|
{
|
|
|
|
|
|
TraceLog = SNew(SSUDSTraceLog);
|
|
|
|
|
|
return SNew(SDockTab)
|
|
|
|
|
|
[
|
|
|
|
|
|
TraceLog.ToSharedRef()
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
.SetDisplayName(INVTEXT("Trace Log"))
|
|
|
|
|
|
.SetGroup(WorkspaceMenuCategory.ToSharedRef());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set up the toolbar
|
|
|
|
|
|
FSUDSToolbarCommands::Register();
|
|
|
|
|
|
|
|
|
|
|
|
const TWeakPtr<FAssetEditorToolkit> WeakToolkit = this->AsShared();
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<FExtender> ToolbarExtender = MakeShareable(new FExtender);
|
|
|
|
|
|
|
|
|
|
|
|
ToolbarExtender->AddToolBarExtension(
|
|
|
|
|
|
"Asset",
|
|
|
|
|
|
EExtensionHook::After,
|
|
|
|
|
|
GetToolkitCommands(),
|
|
|
|
|
|
FToolBarExtensionDelegate::CreateRaw(this, &FSUDSEditorToolkit::ExtendToolbar, TWeakPtr<SDockTab>(TabManager->GetOwnerTab())));
|
|
|
|
|
|
AddToolbarExtender(ToolbarExtender);
|
|
|
|
|
|
|
|
|
|
|
|
GetToolkitCommands()->MapAction(FSUDSToolbarCommands::Get().StartDialogue,
|
|
|
|
|
|
FExecuteAction::CreateSP(this, &FSUDSEditorToolkit::StartDialogue),
|
|
|
|
|
|
FCanExecuteAction());
|
|
|
|
|
|
GetToolkitCommands()->MapAction(FSUDSToolbarCommands::Get().WriteBackTextIDs,
|
|
|
|
|
|
FExecuteAction::CreateSP(this, &FSUDSEditorToolkit::WriteBackTextIDs),
|
|
|
|
|
|
FCanExecuteAction());
|
|
|
|
|
|
GetToolkitCommands()->MapAction(FSUDSToolbarCommands::Get().GenerateVOAssets,
|
|
|
|
|
|
FExecuteAction::CreateSP(this, &FSUDSEditorToolkit::GenerateVOAssets),
|
|
|
|
|
|
FCanExecuteAction());
|
|
|
|
|
|
|
|
|
|
|
|
//RegenerateMenusAndToolbars();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::EnsureTabsVisible()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Hard requirement for the dialogue output tab to be open, so restore it if the user had
|
|
|
|
|
|
// closed it (probably by accident)
|
|
|
|
|
|
if (!TabManager->FindExistingLiveTab(DialogueOutputTabName))
|
|
|
|
|
|
{
|
|
|
|
|
|
TabManager->TryInvokeTab(DialogueOutputTabName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::ExtendToolbar(FToolBarBuilder& ToolbarBuilder, TWeakPtr<SDockTab> Tab)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Tab.IsValid())
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ToolbarBuilder.BeginSection("CachedState");
|
|
|
|
|
|
{
|
|
|
|
|
|
ToolbarBuilder.AddToolBarButton(FSUDSToolbarCommands::Get().StartDialogue,
|
|
|
|
|
|
NAME_None, TAttribute<FText>(), TAttribute<FText>(),
|
|
|
|
|
|
FSlateIcon(
|
|
|
|
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 0
|
|
|
|
|
|
FAppStyle::GetAppStyleSetName(),
|
|
|
|
|
|
#else
|
|
|
|
|
|
FEditorStyle::GetStyleSetName(),
|
|
|
|
|
|
#endif
|
|
|
|
|
|
TEXT("Icons.Toolbar.Play")));
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<SWidget> LabelSelectionBox = SNew(SComboButton)
|
|
|
|
|
|
.OnGetMenuContent(this, &FSUDSEditorToolkit::GetStartLabelMenu)
|
|
|
|
|
|
.ButtonContent()
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.ToolTipText( INVTEXT("Choose where to start dialogue from") )
|
|
|
|
|
|
.Text(this, &FSUDSEditorToolkit::GetSelectedStartLabel )
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
ToolbarBuilder.AddWidget(LabelSelectionBox);
|
|
|
|
|
|
|
|
|
|
|
|
ToolbarBuilder.AddWidget(SNew(STextBlock)
|
|
|
|
|
|
.Text(FText::FromString(TEXT("Reset Variables:")))
|
|
|
|
|
|
.Margin(FMargin(10, 7, 10, 5)));
|
|
|
|
|
|
TSharedPtr<SToolTip> ResetTooltip = IDocumentation::Get()->CreateToolTip(
|
|
|
|
|
|
FText::FromString(TEXT(
|
|
|
|
|
|
"Whether to reset variable state created by the script (note: vars manually created in this editor are never reset, delete them yourself)")),
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
"",
|
|
|
|
|
|
TEXT("ResetVars"));
|
|
|
|
|
|
TSharedRef<SWidget> ResetVarsCheckbox = SNew(SCheckBox)
|
|
|
|
|
|
.ToolTip(ResetTooltip)
|
|
|
|
|
|
.IsChecked(this, &FSUDSEditorToolkit::GetResetVarsCheckState)
|
|
|
|
|
|
.OnCheckStateChanged(this, &FSUDSEditorToolkit::OnResetVarsCheckStateChanged);
|
|
|
|
|
|
ToolbarBuilder.AddWidget(ResetVarsCheckbox);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
ToolbarBuilder.EndSection();
|
|
|
|
|
|
|
|
|
|
|
|
ToolbarBuilder.AddSeparator();
|
|
|
|
|
|
|
|
|
|
|
|
ToolbarBuilder.BeginSection("CachedState");
|
|
|
|
|
|
{
|
|
|
|
|
|
ToolbarBuilder.AddToolBarButton(FSUDSToolbarCommands::Get().WriteBackTextIDs,
|
|
|
|
|
|
NAME_None, TAttribute<FText>(), TAttribute<FText>(),
|
|
|
|
|
|
FSlateIcon(
|
|
|
|
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 0
|
|
|
|
|
|
FAppStyle::GetAppStyleSetName(),
|
|
|
|
|
|
#else
|
|
|
|
|
|
FEditorStyle::GetStyleSetName(),
|
|
|
|
|
|
#endif
|
|
|
|
|
|
TEXT("Icons.Toolbar.Details")));
|
|
|
|
|
|
|
|
|
|
|
|
ToolbarBuilder.AddToolBarButton(FSUDSToolbarCommands::Get().GenerateVOAssets,
|
|
|
|
|
|
NAME_None, TAttribute<FText>(), TAttribute<FText>(),
|
|
|
|
|
|
FSlateIcon(
|
|
|
|
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 0
|
|
|
|
|
|
FAppStyle::GetAppStyleSetName(),
|
|
|
|
|
|
#else
|
|
|
|
|
|
FEditorStyle::GetStyleSetName(),
|
|
|
|
|
|
#endif
|
|
|
|
|
|
TEXT("Icons.Toolbar.Export")));
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
ToolbarBuilder.EndSection();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<SWidget> FSUDSEditorToolkit::GetStartLabelMenu()
|
|
|
|
|
|
{
|
|
|
|
|
|
FMenuBuilder MenuBuilder(true, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
const FUIAction StartAction(FExecuteAction::CreateSP(this, &FSUDSEditorToolkit::OnStartLabelSelected, FName(NAME_None)));
|
|
|
|
|
|
MenuBuilder.AddMenuEntry(INVTEXT("Beginning"), INVTEXT("Start from the beginning of the dialogue"), FSlateIcon(), StartAction);
|
|
|
|
|
|
MenuBuilder.AddSeparator();
|
|
|
|
|
|
|
|
|
|
|
|
if (IsValid(Script))
|
|
|
|
|
|
{
|
|
|
|
|
|
for (auto& LabelPair : Script->GetLabelList())
|
|
|
|
|
|
{
|
|
|
|
|
|
FUIAction LabelAction(FExecuteAction::CreateSP(this, &FSUDSEditorToolkit::OnStartLabelSelected, LabelPair.Key));
|
|
|
|
|
|
MenuBuilder.AddMenuEntry(FText::FromName(LabelPair.Key), FText(), FSlateIcon(), LabelAction);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnStartLabelSelected(FName Label)
|
|
|
|
|
|
{
|
|
|
|
|
|
StartLabel = Label;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FText FSUDSEditorToolkit::GetSelectedStartLabel() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return FText::Format(INVTEXT("From: {0}"),
|
|
|
|
|
|
StartLabel.IsNone() ? INVTEXT("Beginning") : FText::FromName(StartLabel));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ECheckBoxState FSUDSEditorToolkit::GetResetVarsCheckState() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return bResetVarsOnStart ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnResetVarsCheckStateChanged(ECheckBoxState NewState)
|
|
|
|
|
|
{
|
|
|
|
|
|
bResetVarsOnStart = NewState == ECheckBoxState::Checked;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
|
|
|
|
|
|
{
|
|
|
|
|
|
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
|
|
|
|
|
|
|
|
|
|
|
|
InTabManager->UnregisterTabSpawner("SUDSDialogueTab");
|
|
|
|
|
|
InTabManager->UnregisterTabSpawner("SUDSVariablesTab");
|
|
|
|
|
|
InTabManager->UnregisterTabSpawner("SUDSLogTab");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FText FSUDSEditorToolkit::GetBaseToolkitName() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return INVTEXT("SUDS Script Editor");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FName FSUDSEditorToolkit::GetToolkitFName() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return "SUDSScriptEditor";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FLinearColor FSUDSEditorToolkit::GetWorldCentricTabColorScale() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return FLinearColor();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FString FSUDSEditorToolkit::GetWorldCentricTabPrefix() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return "SUDS Script ";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::UserEditVariable(const FName& Name, FSUDSValue Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
FName GlobalName;
|
|
|
|
|
|
if (USUDSLibrary::IsDialogueVariableGlobal(Name, GlobalName))
|
|
|
|
|
|
{
|
|
|
|
|
|
USUDSSubsystem::Test_DummyGlobalVariables.Add(GlobalName, Value);
|
|
|
|
|
|
OnDialogueUserEditedVar(nullptr, Name, Value);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Update manual overrides if it's one of these
|
|
|
|
|
|
if (ManualOverrideVariables.Contains(Name))
|
|
|
|
|
|
{
|
|
|
|
|
|
ManualOverrideVariables[Name] = Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This will cause a refresh
|
|
|
|
|
|
if (Dialogue)
|
|
|
|
|
|
Dialogue->SetVariable(Name, Value);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// This won't get called if dialogue is not initialised, so call it to get log & refresh
|
|
|
|
|
|
OnDialogueUserEditedVar(nullptr, Name, Value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::DeleteVariable(const FName& Name)
|
|
|
|
|
|
{
|
|
|
|
|
|
FName GlobalName;
|
|
|
|
|
|
if (USUDSLibrary::IsDialogueVariableGlobal(Name, GlobalName))
|
|
|
|
|
|
{
|
|
|
|
|
|
USUDSSubsystem::Test_DummyGlobalVariables.Remove(GlobalName);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ManualOverrideVariables.Remove(Name);
|
|
|
|
|
|
if (Dialogue)
|
|
|
|
|
|
Dialogue->UnSetVariable(Name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UpdateVariables();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnClose()
|
|
|
|
|
|
{
|
|
|
|
|
|
FAssetEditorToolkit::OnClose();
|
|
|
|
|
|
FReimportManager::Instance()->OnPostReimport().Remove(ReimportDelegateHandle);
|
|
|
|
|
|
ReimportDelegateHandle.Reset();
|
|
|
|
|
|
|
|
|
|
|
|
DestroyDialogue();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::StartDialogue()
|
|
|
|
|
|
{
|
|
|
|
|
|
// In case the user closed important tabs
|
|
|
|
|
|
EnsureTabsVisible();
|
|
|
|
|
|
Clear();
|
|
|
|
|
|
if (!Dialogue)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Custom creation of USUDSDialogue, we want it to exist only for this window.
|
|
|
|
|
|
// Mark as a root object (we'll clear it on close)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Have to guard vs GC
|
|
|
|
|
|
FGCScopeGuard GCGuard;
|
|
|
|
|
|
const FName Name = MakeUniqueObjectName(GetTransientPackage(),
|
|
|
|
|
|
USUDSDialogue::StaticClass(),
|
|
|
|
|
|
Script->GetFName());
|
|
|
|
|
|
Dialogue = NewObject<USUDSDialogue>(GetTransientPackage(),
|
|
|
|
|
|
Name, RF_Transient | RF_MarkAsRootSet);
|
|
|
|
|
|
}
|
|
|
|
|
|
Dialogue->Initialise(Script);
|
|
|
|
|
|
|
|
|
|
|
|
Dialogue->InternalOnChoice.BindSP(this, &FSUDSEditorToolkit::OnDialogueChoice);
|
|
|
|
|
|
Dialogue->InternalOnEvent.BindSP(this, &FSUDSEditorToolkit::OnDialogueEvent);
|
|
|
|
|
|
Dialogue->InternalOnFinished.BindSP(this, &FSUDSEditorToolkit::OnDialogueFinished);
|
|
|
|
|
|
Dialogue->InternalOnProceeding.BindSP(this, &FSUDSEditorToolkit::OnDialogueProceeding);
|
|
|
|
|
|
Dialogue->InternalOnStarting.BindSP(this, &FSUDSEditorToolkit::OnDialogueStarting);
|
|
|
|
|
|
Dialogue->InternalOnSpeakerLine.BindSP(this, &FSUDSEditorToolkit::OnDialogueSpeakerLine);
|
|
|
|
|
|
Dialogue->InternalOnSetVar.BindSP(this, &FSUDSEditorToolkit::OnDialogueSetVar);
|
|
|
|
|
|
Dialogue->InternalOnSetVarByCode.BindSP(this, &FSUDSEditorToolkit::OnDialogueUserEditedVar);
|
|
|
|
|
|
Dialogue->InternalOnSelectEval.BindSP(this, &FSUDSEditorToolkit::OnDialogueSelectEval);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Reset things manually so we can set manual override vars
|
|
|
|
|
|
if (bResetVarsOnStart)
|
|
|
|
|
|
Dialogue->ResetState();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set manual override vars before we start
|
|
|
|
|
|
for (auto& Pair : ManualOverrideVariables)
|
|
|
|
|
|
{
|
|
|
|
|
|
Dialogue->SetVariable(Pair.Key, Pair.Value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Dialogue->Restart(false, StartLabel);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UpdateVariables();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::Clear()
|
|
|
|
|
|
{
|
|
|
|
|
|
OutputRows.Empty();
|
|
|
|
|
|
if (OutputListView.IsValid())
|
|
|
|
|
|
{
|
|
|
|
|
|
OutputListView->RequestListRefresh();
|
|
|
|
|
|
}
|
|
|
|
|
|
VariableRows.Empty();
|
|
|
|
|
|
if (VariablesListView.IsValid())
|
|
|
|
|
|
{
|
|
|
|
|
|
VariablesListView->RequestListRefresh();
|
|
|
|
|
|
}
|
|
|
|
|
|
//TraceLog->ClearMessages();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::DestroyDialogue()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Dialogue)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Handle garbage collection of our UObject
|
|
|
|
|
|
{
|
|
|
|
|
|
FGCScopeGuard GCGuard;
|
|
|
|
|
|
Dialogue->RemoveFromRoot();
|
|
|
|
|
|
Dialogue->MarkAsGarbage();
|
|
|
|
|
|
}
|
|
|
|
|
|
Dialogue = nullptr;
|
|
|
|
|
|
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::UpdateOutput()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (OutputListView.IsValid())
|
|
|
|
|
|
{
|
|
|
|
|
|
OutputListView->RequestListRefresh();
|
|
|
|
|
|
OutputListView->ScrollToBottom();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::UpdateChoiceButtons()
|
|
|
|
|
|
{
|
|
|
|
|
|
USUDSDialogue* D = Dialogue;
|
|
|
|
|
|
ChoicesBox->ClearChildren();
|
|
|
|
|
|
if (!D->IsEnded())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (D->IsSimpleContinue())
|
|
|
|
|
|
{
|
|
|
|
|
|
ChoicesBox->AddSlot()
|
|
|
|
|
|
.Padding(0, 0 ,0 , 5)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
|
.HAlign(HAlign_Left)
|
|
|
|
|
|
.Text(INVTEXT("(Continue...)"))
|
|
|
|
|
|
.OnClicked_Lambda([D]()
|
|
|
|
|
|
{
|
|
|
|
|
|
D->Continue();
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
|
})
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < D->GetNumberOfChoices(); ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
ChoicesBox->AddSlot()
|
|
|
|
|
|
.Padding(0, 0 ,0 , 5)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
|
.HAlign(HAlign_Left)
|
|
|
|
|
|
.Text(D->GetChoiceText(i))
|
|
|
|
|
|
.OnClicked_Lambda([D, i]()
|
|
|
|
|
|
{
|
|
|
|
|
|
D->Choose(i);
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
|
})
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueChoice(USUDSDialogue* D, int ChoiceIndex, int LineNo)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!D->IsSimpleContinue())
|
|
|
|
|
|
{
|
|
|
|
|
|
AddDialogueStep(NAME_Choice, LineNo,
|
|
|
|
|
|
FText::Format(INVTEXT("[{1}] {0}"), D->GetChoiceText(ChoiceIndex), ChoiceIndex),
|
|
|
|
|
|
INVTEXT("Choice"));
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueEvent(USUDSDialogue* D, FName EventName, const TArray<FSUDSValue>& Args, int LineNo)
|
|
|
|
|
|
{
|
2026-07-04 21:15:59 +02:00
|
|
|
|
#if ENGINE_MAJOR_VERSION ==5 && ENGINE_MINOR_VERSION >= 8
|
|
|
|
|
|
TStringBuilder<256> B;
|
|
|
|
|
|
#else
|
2026-07-03 19:56:31 +02:00
|
|
|
|
FStringBuilderBase B;
|
2026-07-04 21:15:59 +02:00
|
|
|
|
#endif
|
2026-07-03 19:56:31 +02:00
|
|
|
|
if (Args.Num() > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (auto& Arg : Args)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (B.Len() > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
B.Appendf(TEXT(", %s"), *Arg.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
B.Append("( ");
|
|
|
|
|
|
B.Append(Arg.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
B.Append(" )");
|
|
|
|
|
|
}
|
|
|
|
|
|
FText ArgText = FText::FromString(B.ToString());
|
|
|
|
|
|
AddDialogueStep(NAME_Event, LineNo,
|
|
|
|
|
|
FText::FormatOrdered(INVTEXT("{0} {1}"), FText::FromName(EventName), ArgText),
|
|
|
|
|
|
INVTEXT("Event"));
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueFinished(USUDSDialogue* D)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddDialogueStep(NAME_Finish, 0,
|
|
|
|
|
|
INVTEXT("Dialogue Finished"),
|
|
|
|
|
|
INVTEXT("Finish"));
|
|
|
|
|
|
UpdateChoiceButtons();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueProceeding(USUDSDialogue* D)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueStarting(USUDSDialogue* D, FName LabelName)
|
|
|
|
|
|
{
|
|
|
|
|
|
FString LabelStr = LabelName.IsNone() ? FString("beginning") : LabelName.ToString();
|
|
|
|
|
|
AddDialogueStep(NAME_Start, 0,
|
|
|
|
|
|
FText::Format(INVTEXT("Starting from {0}"), FText::FromString(LabelStr)),
|
|
|
|
|
|
INVTEXT("Start"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueSpeakerLine(USUDSDialogue* D, int LineNo)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddDialogueStep(NAME_SpeakerLine, LineNo , D->GetText(),D->GetSpeakerDisplayName());
|
|
|
|
|
|
UpdateChoiceButtons();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::AddOutputRow(const FText& Prefix,
|
|
|
|
|
|
const FText& Line,
|
|
|
|
|
|
const FSlateColor& PrefixColour,
|
|
|
|
|
|
const FSlateColor& LineColour)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Stupid table changes any colours I give it, white = dark grey
|
|
|
|
|
|
// Can't figure out where it's coming from, I f**king hate Slate
|
|
|
|
|
|
const FSlateColor BgColour = (OutputRows.Num() % 2) > 0 ? FSlateColor(FLinearColor::White) : FSlateColor(FLinearColor(0.9f, 0.9f, 0.9f, 1));
|
|
|
|
|
|
OutputRows.Add(MakeShareable(
|
|
|
|
|
|
new FSUDSEditorOutputRow(Prefix, Line, PrefixColour, LineColour, BgColour)));
|
|
|
|
|
|
UpdateOutput();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::AddTraceLogRow(const FName& Category, int SourceLineNo, const FString& Message)
|
|
|
|
|
|
{
|
|
|
|
|
|
FSlateColor Colour = GetColourForCategory(Category);
|
|
|
|
|
|
|
|
|
|
|
|
if (TraceLog)
|
|
|
|
|
|
{
|
|
|
|
|
|
TraceLog->AppendMessage(Category, SourceLineNo, Message, Colour);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FSlateColor FSUDSEditorToolkit::GetColourForCategory(const FName& Category)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Category == NAME_SpeakerLine)
|
|
|
|
|
|
{
|
|
|
|
|
|
return SpeakerColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_Choice)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ChoiceColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_Event)
|
|
|
|
|
|
{
|
|
|
|
|
|
return EventColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_VariableSet)
|
|
|
|
|
|
{
|
|
|
|
|
|
return VarSetColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_VariableEdit)
|
|
|
|
|
|
{
|
|
|
|
|
|
return VarEditColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_Start)
|
|
|
|
|
|
{
|
|
|
|
|
|
return StartColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_Finish)
|
|
|
|
|
|
{
|
|
|
|
|
|
return FinishColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_SelectEval)
|
|
|
|
|
|
{
|
|
|
|
|
|
return SelectColour;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return FSlateColor::UseForeground();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::AddDialogueStep(const FName& Category, int SourceLineNo, const FText& Description, const FText& Prefix)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Category == NAME_SpeakerLine)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddOutputRow(Prefix, Description, SpeakerColour, FSlateColor::UseForeground());
|
|
|
|
|
|
AddTraceLogRow(Category, SourceLineNo, FString::Printf(TEXT("%s: %s"), *Prefix.ToString(), *Description.ToString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_Choice)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddOutputRow(Prefix, Description, ChoiceColour, ChoiceColour);
|
|
|
|
|
|
AddTraceLogRow(Category, SourceLineNo, Description.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_Start)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddOutputRow(Prefix, Description, StartColour, StartColour);
|
|
|
|
|
|
AddTraceLogRow(Category, SourceLineNo, Description.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Category == NAME_Finish)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddOutputRow(Prefix, Description, FinishColour, FinishColour);
|
|
|
|
|
|
AddTraceLogRow(Category, SourceLineNo, Description.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
AddTraceLogRow(Category, SourceLineNo, Description.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FReply FSUDSEditorToolkit::AddVariableClicked()
|
|
|
|
|
|
{
|
|
|
|
|
|
TSharedRef<SWindow> Window = SNew(SWindow)
|
|
|
|
|
|
.Title(INVTEXT("Add Variable"))
|
|
|
|
|
|
.IsPopupWindow(true)
|
|
|
|
|
|
.SupportsMaximize(false)
|
|
|
|
|
|
.SupportsMinimize(false)
|
|
|
|
|
|
.CreateTitleBar(true)
|
|
|
|
|
|
.SizingRule(ESizingRule::Autosized)
|
|
|
|
|
|
.FocusWhenFirstShown(true)
|
|
|
|
|
|
.MinWidth(400)
|
|
|
|
|
|
.ActivationPolicy(EWindowActivationPolicy::FirstShown);
|
|
|
|
|
|
|
|
|
|
|
|
bool bConfirmed = false;
|
|
|
|
|
|
ESUDSValueType ValType = ESUDSValueType::Int;
|
|
|
|
|
|
FText VarNameText = FText::FromString("NewVar");
|
|
|
|
|
|
|
|
|
|
|
|
Window->SetContent(
|
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
|
.Padding(5)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.FillWidth(0.4)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text(INVTEXT("Name"))
|
|
|
|
|
|
]
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.FillWidth(0.6)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SEditableTextBox)
|
|
|
|
|
|
.MinDesiredWidth(150)
|
|
|
|
|
|
.SelectAllTextWhenFocused(true)
|
|
|
|
|
|
.Text(VarNameText)
|
|
|
|
|
|
.OnTextCommitted_Lambda([&VarNameText](const FText& NewText, ETextCommit::Type CommitType)
|
|
|
|
|
|
{
|
|
|
|
|
|
VarNameText = NewText;
|
|
|
|
|
|
})
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
|
.Padding(5)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.FillWidth(0.4)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text(INVTEXT("Type"))
|
|
|
|
|
|
]
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.FillWidth(0.6)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SComboButton)
|
|
|
|
|
|
.OnGetMenuContent_Lambda([&ValType]
|
|
|
|
|
|
{
|
|
|
|
|
|
FMenuBuilder MenuBuilder(true, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
// NumEnums() - 1 because the last value is the autogen _MAX value
|
|
|
|
|
|
for (int i = 0; i < StaticEnum<ESUDSValueType>()->NumEnums() - 1; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
ESUDSValueType Val = (ESUDSValueType)StaticEnum<ESUDSValueType>()->GetValueByIndex(i);
|
|
|
|
|
|
if (Val != ESUDSValueType::Empty && Val != ESUDSValueType::Variable)
|
|
|
|
|
|
{
|
|
|
|
|
|
const FUIAction Action(FExecuteAction::CreateLambda([Val, &ValType]()
|
|
|
|
|
|
{
|
|
|
|
|
|
ValType = Val;
|
|
|
|
|
|
}));
|
|
|
|
|
|
MenuBuilder.AddMenuEntry(FText::FromString(StaticEnum<ESUDSValueType>()->GetNameStringByIndex(i)),
|
|
|
|
|
|
FText(),
|
|
|
|
|
|
FSlateIcon(),
|
|
|
|
|
|
Action);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
.ButtonContent()
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text_Lambda([&ValType]
|
|
|
|
|
|
{
|
|
|
|
|
|
return FText::FromString(StaticEnum<ESUDSValueType>()->GetNameStringByValue((int64)ValType));
|
|
|
|
|
|
})
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
|
.Padding(0, 5, 0, 3)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.HAlign(HAlign_Right)
|
|
|
|
|
|
.Padding(2)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
|
.Text(INVTEXT("OK"))
|
|
|
|
|
|
.OnClicked_Lambda([&bConfirmed, Window, VarNameText]()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!VarNameText.IsEmptyOrWhitespace())
|
|
|
|
|
|
{
|
|
|
|
|
|
bConfirmed = true;
|
|
|
|
|
|
Window->RequestDestroyWindow();
|
|
|
|
|
|
}
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
|
})
|
|
|
|
|
|
]
|
|
|
|
|
|
+SHorizontalBox::Slot()
|
|
|
|
|
|
.HAlign(HAlign_Right)
|
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
|
.Padding(2)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
|
.Text(INVTEXT("Cancel"))
|
|
|
|
|
|
.OnClicked_Lambda([&bConfirmed, Window]()
|
|
|
|
|
|
{
|
|
|
|
|
|
bConfirmed = false;
|
|
|
|
|
|
Window->RequestDestroyWindow();
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
|
})
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
FVector2D Pos = FSlateApplication::Get().GetCursorPos();
|
|
|
|
|
|
Pos.X -= 230;
|
|
|
|
|
|
Pos.Y += 100;
|
|
|
|
|
|
Window->MoveWindowTo(Pos);
|
|
|
|
|
|
|
|
|
|
|
|
// This doesn't return until closed
|
|
|
|
|
|
GEditor->EditorAddModalWindow(Window);
|
|
|
|
|
|
|
|
|
|
|
|
if (bConfirmed)
|
|
|
|
|
|
{
|
|
|
|
|
|
FName VarName(VarNameText.ToString().TrimStartAndEnd());
|
|
|
|
|
|
const FSUDSValue Val(ValType);
|
|
|
|
|
|
FName GlobalVarName;
|
|
|
|
|
|
if (USUDSLibrary::IsDialogueVariableGlobal(VarName, GlobalVarName))
|
|
|
|
|
|
{
|
|
|
|
|
|
USUDSSubsystem::Test_DummyGlobalVariables.Add(GlobalVarName, Val);
|
|
|
|
|
|
OnDialogueUserEditedVar(nullptr, VarName, Val);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ManualOverrideVariables.Add(VarName, Val);
|
|
|
|
|
|
if (Dialogue)
|
|
|
|
|
|
{
|
|
|
|
|
|
// This will cause a refresh
|
|
|
|
|
|
Dialogue->SetVariable(VarName, Val);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// This won't get called if dialogue is not initialised, so call it to get log & refresh
|
|
|
|
|
|
OnDialogueUserEditedVar(nullptr, VarName, Val);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::UpdateVariables()
|
|
|
|
|
|
{
|
|
|
|
|
|
VariableRows.Empty();
|
|
|
|
|
|
// From dialogue (will contain manual overrides already)
|
|
|
|
|
|
if (Dialogue)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (auto& Pair : Dialogue->GetVariables())
|
|
|
|
|
|
{
|
|
|
|
|
|
VariableRows.Add(MakeShareable(
|
|
|
|
|
|
new FSUDSEditorVariableRow(Pair.Key, Pair.Value, ManualOverrideVariables.Contains(Pair.Key))));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Manual overrides can occur before dialogue is started
|
|
|
|
|
|
// Otherwise, they will be in the dialogue
|
|
|
|
|
|
for (auto& Pair : ManualOverrideVariables)
|
|
|
|
|
|
{
|
|
|
|
|
|
VariableRows.Add(MakeShareable(new FSUDSEditorVariableRow(Pair.Key, Pair.Value, true)));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Also do global vars
|
|
|
|
|
|
for (auto& Pair : USUDSSubsystem::Test_DummyGlobalVariables)
|
|
|
|
|
|
{
|
|
|
|
|
|
VariableRows.Add(MakeShareable(
|
|
|
|
|
|
new FSUDSEditorVariableRow(
|
|
|
|
|
|
// We need to insert the "global." prefix back in since it's removed in the global var list
|
|
|
|
|
|
FName(FString::Printf(TEXT("global.%s"), *Pair.Key.ToString())),
|
|
|
|
|
|
Pair.Value,
|
|
|
|
|
|
true)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VariableRows.Sort();
|
|
|
|
|
|
|
|
|
|
|
|
if (VariablesListView.IsValid())
|
|
|
|
|
|
{
|
|
|
|
|
|
VariablesListView->RequestListRefresh();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnPostReimport(UObject* Object, bool bSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Object == Script)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Destroy any dialogue instance, will not be valid post-import
|
|
|
|
|
|
DestroyDialogue();
|
|
|
|
|
|
Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueSetVar(USUDSDialogue* D,
|
|
|
|
|
|
FName VariableName,
|
|
|
|
|
|
const FSUDSValue& ToValue,
|
|
|
|
|
|
const FString& ExpressionStr,
|
|
|
|
|
|
int LineNo)
|
|
|
|
|
|
{
|
|
|
|
|
|
FText Description;
|
|
|
|
|
|
if (ExpressionStr.Len() > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = FText::Format(INVTEXT("{0} = {1} = {2}"),
|
|
|
|
|
|
FText::FromName(VariableName),
|
|
|
|
|
|
FText::FromString(ExpressionStr),
|
|
|
|
|
|
FText::FromString(ToValue.ToString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = FText::Format(INVTEXT("{0} = {1}"),
|
|
|
|
|
|
FText::FromName(VariableName),
|
|
|
|
|
|
FText::FromString(ToValue.ToString()));
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
AddDialogueStep(NAME_VariableSet,
|
|
|
|
|
|
LineNo,
|
|
|
|
|
|
Description,
|
|
|
|
|
|
INVTEXT("Set Variable"));
|
|
|
|
|
|
UpdateVariables();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueUserEditedVar(USUDSDialogue* D, FName VariableName, const FSUDSValue& ToValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
// User-edited value
|
|
|
|
|
|
const FText Description = FText::Format(INVTEXT("{0} = {1}"),
|
|
|
|
|
|
FText::FromName(VariableName),
|
|
|
|
|
|
FText::FromString(ToValue.ToString()));
|
|
|
|
|
|
AddDialogueStep(NAME_VariableEdit,
|
|
|
|
|
|
0,
|
|
|
|
|
|
Description,
|
|
|
|
|
|
INVTEXT("Edit Variable"));
|
|
|
|
|
|
UpdateVariables();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueSelectEval(USUDSDialogue* D,
|
|
|
|
|
|
const FString& ExpressionStr,
|
|
|
|
|
|
bool bSuccess,
|
|
|
|
|
|
int LineNo)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddDialogueStep(NAME_SelectEval,
|
|
|
|
|
|
LineNo,
|
|
|
|
|
|
FText::Format(INVTEXT("{0} = {1}"),
|
|
|
|
|
|
FText::FromString(ExpressionStr),
|
|
|
|
|
|
bSuccess ? INVTEXT("true") : INVTEXT("false")),
|
|
|
|
|
|
INVTEXT("Conditional"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::OnDialogueRandomEval(USUDSDialogue* D,
|
|
|
|
|
|
const int RandomOutcome,
|
|
|
|
|
|
int LineNo)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddDialogueStep(NAME_RandomEval,
|
|
|
|
|
|
LineNo,
|
|
|
|
|
|
FText::Format(INVTEXT(": {0}"),
|
|
|
|
|
|
RandomOutcome),
|
|
|
|
|
|
INVTEXT("Random"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<ITableRow> FSUDSEditorToolkit::OnGenerateRowForOutput(
|
|
|
|
|
|
TSharedPtr<FSUDSEditorOutputRow> Row,
|
|
|
|
|
|
const TSharedRef<STableViewBase>& OwnerTable)
|
|
|
|
|
|
{
|
|
|
|
|
|
return SNew( SSUDSEditorOutputItem, OwnerTable )
|
|
|
|
|
|
.Prefix(Row->Prefix)
|
|
|
|
|
|
.PrefixColour(Row->PrefixColour)
|
|
|
|
|
|
.Line(Row->Line)
|
|
|
|
|
|
.LineColour(Row->LineColour)
|
|
|
|
|
|
.BgColour(Row->BgColour)
|
|
|
|
|
|
.InitialWidth( PrefixColumnWidth );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<ITableRow> FSUDSEditorToolkit::OnGenerateRowForVariable(TSharedPtr<FSUDSEditorVariableRow> Row,
|
|
|
|
|
|
const TSharedRef<STableViewBase>& Table)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
return SNew( SSUDSEditorVariableItem, Table )
|
|
|
|
|
|
.VariableName(Row->Name)
|
|
|
|
|
|
.VariableValue(Row->Value)
|
|
|
|
|
|
.bIsManualOverride(Row->bIsManualOverride)
|
|
|
|
|
|
.Parent(this)
|
|
|
|
|
|
.InitialWidth( VarColumnWidth );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::WriteBackTextIDs()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (FMessageDialog::Open(EAppMsgType::YesNo,
|
|
|
|
|
|
FText::FromString(
|
|
|
|
|
|
"Are you sure you want to write string keys back to this script?"))
|
|
|
|
|
|
== EAppReturnType::Yes)
|
|
|
|
|
|
{
|
|
|
|
|
|
FSUDSMessageLogger::ClearMessages();
|
|
|
|
|
|
FSUDSMessageLogger Logger;
|
|
|
|
|
|
FSUDSEditorScriptTools::WriteBackTextIDs(Script, Logger);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSEditorToolkit::GenerateVOAssets()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (FMessageDialog::Open(EAppMsgType::YesNo,
|
|
|
|
|
|
FText::FromString(
|
|
|
|
|
|
"Are you sure you want to generate Dialogue Voice / Dialogue Wave assets for this script?"))
|
|
|
|
|
|
== EAppReturnType::Yes)
|
|
|
|
|
|
{
|
|
|
|
|
|
EObjectFlags Flags = RF_Public | RF_Standalone | RF_Transactional;
|
|
|
|
|
|
FSUDSMessageLogger::ClearMessages();
|
|
|
|
|
|
FSUDSMessageLogger Logger;
|
|
|
|
|
|
FSUDSEditorVoiceOverTools::GenerateAssets(Script, Flags, &Logger);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSEditorVariableItem::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView)
|
|
|
|
|
|
{
|
|
|
|
|
|
InitialWidth = InArgs._InitialWidth;
|
|
|
|
|
|
VariableName = InArgs._VariableName;
|
|
|
|
|
|
VariableValue = InArgs._VariableValue;
|
|
|
|
|
|
bIsManualOverride = InArgs._bIsManualOverride;
|
|
|
|
|
|
Parent = InArgs._Parent;
|
|
|
|
|
|
|
|
|
|
|
|
SMultiColumnTableRow< TSharedPtr< FString > >::Construct( SMultiColumnTableRow< TSharedPtr< FString > >::FArguments(), InOwnerTableView );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<SWidget> SSUDSEditorVariableItem::GenerateWidgetForColumn(const FName& ColumnName)
|
|
|
|
|
|
{
|
|
|
|
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 0
|
|
|
|
|
|
const FSlateFontInfo PropertyFont = FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"));
|
|
|
|
|
|
const FSlateBrush* BorderBrush = FAppStyle::GetBrush("ToolPanel.GroupBorder");
|
|
|
|
|
|
#else
|
|
|
|
|
|
const FSlateFontInfo PropertyFont = FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"));
|
|
|
|
|
|
const FSlateBrush* BorderBrush = FEditorStyle::GetBrush("ToolPanel.GroupBorder");
|
|
|
|
|
|
#endif
|
|
|
|
|
|
const FSlateColor NormalBgColour = FSlateColor(FLinearColor::White);
|
|
|
|
|
|
const FSlateColor ManualOverrideBgColour = FSlateColor(FLinearColor(0.9f,0.7f,1,1));
|
|
|
|
|
|
|
|
|
|
|
|
if (ColumnName == "NameHeader")
|
|
|
|
|
|
{
|
|
|
|
|
|
const FMargin BoxMargin(0, 2, 0, 1.333f);
|
|
|
|
|
|
return SNew(SBorder)
|
|
|
|
|
|
.BorderImage(BorderBrush)
|
|
|
|
|
|
.BorderBackgroundColor(bIsManualOverride ? ManualOverrideBgColour : NormalBgColour)
|
|
|
|
|
|
.Padding(BoxMargin)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.FillWidth(1.0)
|
|
|
|
|
|
.Padding(10, 5, 5, 5)
|
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Font(PropertyFont)
|
|
|
|
|
|
.Text(FText::FromName(VariableName))
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
TSharedPtr<SWidget> ValueWidget;
|
|
|
|
|
|
|
|
|
|
|
|
FMargin InnerPadding = FMargin(5, 2, 5, 2);
|
|
|
|
|
|
switch (VariableValue.GetType())
|
|
|
|
|
|
{
|
|
|
|
|
|
case ESUDSValueType::Int:
|
|
|
|
|
|
ValueWidget = SNew(SNumericEntryBox<int32>)
|
|
|
|
|
|
.Value(VariableValue.GetIntValue())
|
|
|
|
|
|
.OnValueCommitted_Lambda([this] (const int32 InValue, ETextCommit::Type)
|
|
|
|
|
|
{
|
|
|
|
|
|
Parent->UserEditVariable(VariableName, InValue);
|
|
|
|
|
|
})
|
|
|
|
|
|
.MinDesiredValueWidth(60)
|
|
|
|
|
|
.Font(PropertyFont);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ESUDSValueType::Float:
|
|
|
|
|
|
ValueWidget = SNew(SNumericEntryBox<float>)
|
|
|
|
|
|
.Value(VariableValue.GetFloatValue())
|
|
|
|
|
|
.OnValueCommitted_Lambda([this] (const float InValue, ETextCommit::Type)
|
|
|
|
|
|
{
|
|
|
|
|
|
Parent->UserEditVariable(VariableName, InValue);
|
|
|
|
|
|
})
|
|
|
|
|
|
.MinDesiredValueWidth(60)
|
|
|
|
|
|
.Font(PropertyFont);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ESUDSValueType::Boolean:
|
|
|
|
|
|
ValueWidget = SNew(SCheckBox)
|
|
|
|
|
|
.IsChecked(VariableValue.GetBooleanValue() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked)
|
|
|
|
|
|
.OnCheckStateChanged_Lambda([this] (ECheckBoxState NewState)
|
|
|
|
|
|
{
|
|
|
|
|
|
Parent->UserEditVariable(VariableName, NewState == ECheckBoxState::Checked);
|
|
|
|
|
|
});
|
|
|
|
|
|
InnerPadding.Top = InnerPadding.Bottom = 2;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ESUDSValueType::Gender:
|
|
|
|
|
|
ValueWidget = SNew(SComboButton)
|
|
|
|
|
|
.OnGetMenuContent(this, &SSUDSEditorVariableItem::GetGenderMenu)
|
|
|
|
|
|
.ButtonContent()
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text(FText::FromString(VariableValue.ToString()))
|
|
|
|
|
|
.Font(PropertyFont)
|
|
|
|
|
|
];
|
|
|
|
|
|
InnerPadding.Top = InnerPadding.Bottom = 1;
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ESUDSValueType::Text:
|
|
|
|
|
|
ValueWidget = SNew(SEditableTextBox)
|
|
|
|
|
|
.IsReadOnly(false)
|
|
|
|
|
|
.Text(VariableValue.GetTextValue())
|
|
|
|
|
|
.Font(PropertyFont)
|
|
|
|
|
|
.OnTextCommitted_Lambda([this] (const FText& InValue, ETextCommit::Type)
|
|
|
|
|
|
{
|
|
|
|
|
|
Parent->UserEditVariable(VariableName, InValue);
|
|
|
|
|
|
});
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ESUDSValueType::Name:
|
|
|
|
|
|
ValueWidget = SNew(SEditableTextBox)
|
|
|
|
|
|
.IsReadOnly(false)
|
|
|
|
|
|
.Text(FText::FromString(VariableValue.GetNameValue().ToString()))
|
|
|
|
|
|
.Font(PropertyFont)
|
|
|
|
|
|
.OnTextCommitted_Lambda([this] (const FText& InValue, ETextCommit::Type)
|
|
|
|
|
|
{
|
|
|
|
|
|
Parent->UserEditVariable(VariableName, FSUDSValue(FName(InValue.ToString()), false));
|
|
|
|
|
|
});
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ESUDSValueType::Variable:
|
|
|
|
|
|
case ESUDSValueType::Empty:
|
|
|
|
|
|
default:
|
|
|
|
|
|
ValueWidget = SNew(STextBlock);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const FMargin BoxMargin(2, 1, 0, 1);
|
|
|
|
|
|
return SNew(SBox)
|
|
|
|
|
|
.HeightOverride(26)
|
|
|
|
|
|
.Padding(BoxMargin)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SBorder)
|
|
|
|
|
|
.BorderImage(BorderBrush)
|
|
|
|
|
|
.BorderBackgroundColor(bIsManualOverride ? ManualOverrideBgColour : NormalBgColour)
|
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
|
[
|
|
|
|
|
|
|
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.Padding(InnerPadding)
|
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
|
[
|
|
|
|
|
|
ValueWidget.ToSharedRef()
|
|
|
|
|
|
]
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.Padding(5,2,5,2)
|
|
|
|
|
|
.FillWidth(1.0)
|
|
|
|
|
|
.HAlign(HAlign_Right)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
|
.ButtonStyle( FAppStyle::Get(), "SimpleButton" )
|
|
|
|
|
|
.OnClicked_Lambda([this]
|
|
|
|
|
|
{
|
|
|
|
|
|
// Delete click
|
|
|
|
|
|
Parent->DeleteVariable(VariableName);
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
|
} )
|
|
|
|
|
|
.ContentPadding(0)
|
|
|
|
|
|
.IsFocusable(false)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew( SImage )
|
|
|
|
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 0
|
|
|
|
|
|
.Image( FAppStyle::GetBrush("Icons.Delete") )
|
|
|
|
|
|
#else
|
|
|
|
|
|
.Image( FEditorStyle::GetBrush("Icons.Delete") )
|
|
|
|
|
|
#endif
|
|
|
|
|
|
.ColorAndOpacity( FSlateColor::UseForeground() )
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<SWidget> SSUDSEditorVariableItem::GetGenderMenu()
|
|
|
|
|
|
{
|
|
|
|
|
|
FMenuBuilder MenuBuilder(true, NULL);
|
|
|
|
|
|
|
2026-07-04 21:15:59 +02:00
|
|
|
|
#if ENGINE_MAJOR_VERSION ==5 && ENGINE_MINOR_VERSION >= 8
|
|
|
|
|
|
// StaticEnum<ETextGender>() doesn't exist anymore so we have to do this manually
|
|
|
|
|
|
const FUIAction Action0(FExecuteAction::CreateSP(this, &SSUDSEditorVariableItem::OnGenderSelected, ETextGender::Feminine));
|
|
|
|
|
|
MenuBuilder.AddMenuEntry(FText::FromString(LexToString(ETextGender::Feminine)),
|
|
|
|
|
|
FText(),
|
|
|
|
|
|
FSlateIcon(),
|
|
|
|
|
|
Action0);
|
|
|
|
|
|
const FUIAction Action1(FExecuteAction::CreateSP(this, &SSUDSEditorVariableItem::OnGenderSelected, ETextGender::Masculine));
|
|
|
|
|
|
MenuBuilder.AddMenuEntry(FText::FromString(LexToString(ETextGender::Masculine)),
|
|
|
|
|
|
FText(),
|
|
|
|
|
|
FSlateIcon(),
|
|
|
|
|
|
Action1);
|
|
|
|
|
|
const FUIAction Action2(FExecuteAction::CreateSP(this, &SSUDSEditorVariableItem::OnGenderSelected, ETextGender::Neuter));
|
|
|
|
|
|
MenuBuilder.AddMenuEntry(FText::FromString(LexToString(ETextGender::Neuter)),
|
|
|
|
|
|
FText(),
|
|
|
|
|
|
FSlateIcon(),
|
|
|
|
|
|
Action2);
|
|
|
|
|
|
#else
|
2026-07-03 19:56:31 +02:00
|
|
|
|
// NumEnums() - 1 because the last value is the autogen _MAX value
|
|
|
|
|
|
for (int i = 0; i < StaticEnum<ETextGender>()->NumEnums() - 1; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
ETextGender Val = (ETextGender)StaticEnum<ETextGender>()->GetValueByIndex(i);
|
|
|
|
|
|
const FUIAction Action(FExecuteAction::CreateSP(this, &SSUDSEditorVariableItem::OnGenderSelected, Val));
|
|
|
|
|
|
MenuBuilder.AddMenuEntry(FText::FromString(StaticEnum<ETextGender>()->GetNameStringByIndex(i)),
|
|
|
|
|
|
FText(),
|
|
|
|
|
|
FSlateIcon(),
|
|
|
|
|
|
Action);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2026-07-04 21:15:59 +02:00
|
|
|
|
#endif
|
2026-07-03 19:56:31 +02:00
|
|
|
|
|
|
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSEditorVariableItem::OnGenderSelected(ETextGender NewGender)
|
|
|
|
|
|
{
|
|
|
|
|
|
Parent->UserEditVariable(VariableName, NewGender);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FVector2D SSUDSEditorVariableItem::ComputeDesiredSize(float X) const
|
|
|
|
|
|
{
|
|
|
|
|
|
// ItemHeight(28) at table level is totally not working for some reason, so fudge it here
|
|
|
|
|
|
FVector2D Desired = SMultiColumnTableRow<TSharedPtr<FString, ESPMode::ThreadSafe>>::ComputeDesiredSize(X);
|
|
|
|
|
|
Desired.Y = 28;
|
|
|
|
|
|
return Desired;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSEditorOutputItem::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView)
|
|
|
|
|
|
{
|
|
|
|
|
|
InitialWidth = InArgs._InitialWidth;
|
|
|
|
|
|
Prefix = InArgs._Prefix;
|
|
|
|
|
|
PrefixColour = InArgs._PrefixColour;
|
|
|
|
|
|
Line = InArgs._Line;
|
|
|
|
|
|
LineColour = InArgs._LineColour;
|
|
|
|
|
|
|
|
|
|
|
|
SetBorderBackgroundColor(InArgs._BgColour);
|
|
|
|
|
|
|
|
|
|
|
|
SMultiColumnTableRow< TSharedPtr< FString > >::Construct( SMultiColumnTableRow< TSharedPtr< FString > >::FArguments(), InOwnerTableView );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TSharedRef<SWidget> SSUDSEditorOutputItem::GenerateWidgetForColumn(const FName& ColumnName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ColumnName == "PrefixHeader")
|
|
|
|
|
|
{
|
|
|
|
|
|
return SNew(SHorizontalBox)
|
|
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.FillWidth(1.0)
|
|
|
|
|
|
.Padding(10, 5, 10, 5)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text(Prefix)
|
|
|
|
|
|
.ColorAndOpacity(PrefixColour)
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return SNew(SHorizontalBox)
|
|
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
|
.FillWidth(1.0)
|
|
|
|
|
|
.Padding(10, 5, 10, 5)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
|
.Text(Line)
|
|
|
|
|
|
.AutoWrapText(true)
|
|
|
|
|
|
.ColorAndOpacity(LineColour)
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FSUDSTraceLogMarshaller::FSUDSTraceLogMarshaller()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSTraceLogMarshaller::SetText(const FString& SourceString, FTextLayout& TargetTextLayout)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
static const FName LogNormalStyle(TEXT("Log.Normal"));
|
|
|
|
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 0
|
|
|
|
|
|
const FTextBlockStyle& OrigStyle = FAppStyle::Get().GetWidgetStyle<FTextBlockStyle>(LogNormalStyle);
|
|
|
|
|
|
#else
|
|
|
|
|
|
const FTextBlockStyle& OrigStyle = FEditorStyle::Get().GetWidgetStyle<FTextBlockStyle>(LogNormalStyle);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& Msg : Messages)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Get base style & copy
|
|
|
|
|
|
FTextBlockStyle Style = OrigStyle;
|
|
|
|
|
|
Style.ColorAndOpacity = Msg->Colour;
|
|
|
|
|
|
|
|
|
|
|
|
TArray<TSharedRef<IRun>> Runs;
|
|
|
|
|
|
Runs.Add(FSlateTextRun::Create(FRunInfo(), Msg->Message, Style));
|
|
|
|
|
|
|
|
|
|
|
|
TargetTextLayout.AddLine(FTextLayout::FNewLineData(Msg->Message, Runs));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSTraceLogMarshaller::GetText(FString& TargetString, const FTextLayout& SourceTextLayout)
|
|
|
|
|
|
{
|
|
|
|
|
|
SourceTextLayout.GetAsText(TargetString);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSTraceLogMarshaller::AppendMessage(FName InCategory, int LineNo, const FString& Message, const FSlateColor& Colour)
|
|
|
|
|
|
{
|
|
|
|
|
|
const int MinCategorySize = 12;
|
|
|
|
|
|
const FString CatStr = InCategory.ToString();
|
|
|
|
|
|
int CatLen = CatStr.Len();
|
|
|
|
|
|
const FString CatPadding = CatLen < MinCategorySize ? FString::ChrN(MinCategorySize - CatLen, ' ') : "";
|
|
|
|
|
|
const FString LineNoStr = LineNo > 0 ? FString::Printf(TEXT("L%04d"), LineNo) : " ";
|
|
|
|
|
|
const FString ConcatLine = FString::Printf(TEXT("%s[%s] %s %s"), *CatPadding, *CatStr, *LineNoStr, *Message);
|
|
|
|
|
|
Messages.Add(MakeShareable(new FSUDSTraceLogMessage(InCategory, ConcatLine, Colour)));
|
|
|
|
|
|
MakeDirty();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FSUDSTraceLogMarshaller::ClearMessages()
|
|
|
|
|
|
{
|
|
|
|
|
|
Messages.Empty();
|
|
|
|
|
|
MakeDirty();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSTraceLog::Construct(const FArguments& InArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
TraceLogMarshaller = MakeShareable(new FSUDSTraceLogMarshaller());
|
|
|
|
|
|
TraceLogTextBox = SNew(SMultiLineEditableTextBox)
|
|
|
|
|
|
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION > 0
|
|
|
|
|
|
.Style(FAppStyle::Get(), "Log.TextBox")
|
|
|
|
|
|
#else
|
|
|
|
|
|
.Style(FEditorStyle::Get(), "Log.TextBox")
|
|
|
|
|
|
.TextStyle(FEditorStyle::Get(), "Log.Normal")
|
|
|
|
|
|
#endif
|
|
|
|
|
|
.Marshaller(TraceLogMarshaller)
|
|
|
|
|
|
.IsReadOnly(true)
|
|
|
|
|
|
.AutoWrapText(true)
|
|
|
|
|
|
.OnVScrollBarUserScrolled(this, &SSUDSTraceLog::OnUserScrolled)
|
|
|
|
|
|
.AlwaysShowScrollbars(true);
|
|
|
|
|
|
|
|
|
|
|
|
ChildSlot
|
|
|
|
|
|
.Padding(3)
|
|
|
|
|
|
[
|
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
|
|
|
|
|
|
// could add a filter here like output log
|
|
|
|
|
|
|
|
|
|
|
|
// log area
|
|
|
|
|
|
+SVerticalBox::Slot()
|
|
|
|
|
|
.FillHeight(1)
|
|
|
|
|
|
[
|
|
|
|
|
|
TraceLogTextBox.ToSharedRef()
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSTraceLog::OnUserScrolled(float X)
|
|
|
|
|
|
{
|
|
|
|
|
|
// revert to auto scroll when near the bottom
|
|
|
|
|
|
bIsUserScrolled = X < (1.0 - SMALL_NUMBER);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSTraceLog::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
|
|
|
|
|
|
|
|
|
|
|
// We seem to have to do this in Tick(), trying to do it after appending never works
|
|
|
|
|
|
if (!bIsUserScrolled)
|
|
|
|
|
|
{
|
|
|
|
|
|
ScrollToEnd();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSTraceLog::AppendMessage(FName InCategory, int LineNo, const FString& Message, const FSlateColor& Colour)
|
|
|
|
|
|
{
|
|
|
|
|
|
TraceLogMarshaller->AppendMessage(InCategory, LineNo, Message, Colour);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSTraceLog::ClearMessages()
|
|
|
|
|
|
{
|
|
|
|
|
|
TraceLogMarshaller->ClearMessages();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SSUDSTraceLog::ScrollToEnd()
|
|
|
|
|
|
{
|
|
|
|
|
|
TraceLogTextBox->ScrollTo(ETextLocation::EndOfDocument);
|
|
|
|
|
|
bIsUserScrolled = false;
|
|
|
|
|
|
}
|