// Copyright Epic Games, Inc. All Rights Reserved. #include "ChooserEditorWidgets.h" #include "Chooser.h" #include "ChooserEditorStyle.h" #include "ChooserTableEditor.h" #include "DetailLayoutBuilder.h" #include "ObjectChooserClassFilter.h" #include "ObjectChooserWidgetFactories.h" #include "ObjectChooser_Asset.h" #include "ObjectChooser_Class.h" #include "PropertyCustomizationHelpers.h" #include "SClassViewer.h" #include "ScopedTransaction.h" #include "Editor/EditorEngine.h" #include "Framework/Application/SlateApplication.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SComboButton.h" #include "Widgets/Input/STextEntryPopup.h" #define LOCTEXT_NAMESPACE "ChooserEditorWidgets" namespace UE::ChooserEditor { TSharedRef CreateAssetWidget(bool bReadOnly, UObject* TransactionObject, void* Value, UClass* ResultBaseClass, FChooserWidgetValueChanged ValueChanged) { FAssetChooser* DIAsset = static_cast(Value); UObject* Asset = DIAsset->Asset; return SNew(SObjectPropertyEntryBox) .IsEnabled(!bReadOnly) .AllowedClass(ResultBaseClass ? ResultBaseClass : UObject::StaticClass()) .ObjectPath_Lambda([DIAsset](){ return DIAsset->Asset ? DIAsset->Asset.GetPath() : "";}) .bOnlyRecognizeOnDragEnter(true) .OnObjectChanged_Lambda([TransactionObject, DIAsset](const FAssetData& AssetData) { const FScopedTransaction Transaction(LOCTEXT("Edit Asset", "Edit Asset")); TransactionObject->Modify(true); DIAsset->Asset = AssetData.GetAsset(); }); } TSharedRef CreateSoftAssetWidget(bool bReadOnly, UObject* TransactionObject, void* Value, UClass* ResultBaseClass, FChooserWidgetValueChanged ValueChanged) { FSoftAssetChooser* DIAsset = static_cast(Value); return SNew(SObjectPropertyEntryBox) .IsEnabled(!bReadOnly) .AllowedClass(ResultBaseClass ? ResultBaseClass : UObject::StaticClass()) .ObjectPath_Lambda([DIAsset](){ return DIAsset->Asset.ToSoftObjectPath().ToString(); }) .OnObjectChanged_Lambda([TransactionObject, DIAsset](const FAssetData& AssetData) { const FScopedTransaction Transaction(LOCTEXT("Edit Asset", "Edit Asset")); TransactionObject->Modify(true); DIAsset->Asset = AssetData.GetAsset(); }); } TSharedRef CreateClassWidget(bool bReadOnly, UObject* TransactionObject, void* Value, UClass* ResultBaseClass, FChooserWidgetValueChanged ValueChanged) { FClassChooser* ClassChooser = static_cast(Value); UClass* Class = ClassChooser->Class; return SNew(SClassPropertyEntryBox) .IsEnabled(!bReadOnly) .MetaClass(ResultBaseClass ? ResultBaseClass : UObject::StaticClass()) .SelectedClass_Lambda([ClassChooser]() { return ClassChooser->Class; }) .OnSetClass_Lambda([TransactionObject, ClassChooser](const UClass* SelectedClass) { const FScopedTransaction Transaction(LOCTEXT("Edit Class", "Edit Class")); TransactionObject->Modify(true); ClassChooser->Class = const_cast(SelectedClass); }); } TSharedRef CreateEvaluateChooserWidget(bool bReadOnly, UObject* TransactionObject, void* Value, UClass* ResultBaseClass, FChooserWidgetValueChanged ValueChanged) { FEvaluateChooser* EvaluateChooser = static_cast(Value); return SNew(SObjectPropertyEntryBox) .IsEnabled(!bReadOnly) .AllowedClass(UChooserTable::StaticClass()) .bOnlyRecognizeOnDragEnter(true) .ObjectPath_Lambda([EvaluateChooser](){ return EvaluateChooser->Chooser ? EvaluateChooser->Chooser.GetPath() : "";}) .OnShouldFilterAsset_Lambda([ResultBaseClass](const FAssetData& InAssetData) { if (ResultBaseClass == nullptr) { return false; } if (InAssetData.IsInstanceOf(UChooserTable::StaticClass())) { if (UChooserTable* Chooser = Cast(InAssetData.GetAsset())) { UChooserTable* ContextOwner = Chooser->GetRootChooser(); return !(ContextOwner->OutputObjectType && ContextOwner->OutputObjectType->IsChildOf(ResultBaseClass)); } } return true; }) .OnObjectChanged_Lambda([TransactionObject, EvaluateChooser](const FAssetData& AssetData) { const FScopedTransaction Transaction(LOCTEXT("Edit Chooser", "Edit Chooser")); TransactionObject->Modify(true); EvaluateChooser->Chooser = Cast(AssetData.GetAsset()); }); } TSharedRef CreateNestedChooserWidget(bool bReadOnly, UObject* TransactionObject, void* Value, UClass* ResultBaseClass, FChooserWidgetValueChanged ValueChanged) { FNestedChooser* NestedChooser = static_cast(Value); TSharedRef Button = SNew(SComboButton) .ContentPadding(0) .ButtonContent() [ SNew(SHorizontalBox) + SHorizontalBox::Slot().AutoWidth() [ SNew(SImage).Image(FChooserEditorStyle::Get().GetBrush("ChooserEditor.ChooserTableIconSmall")) ] + SHorizontalBox::Slot().FillWidth(1.0).Padding(2) [ SNew(STextBlock) .Font(IDetailLayoutBuilder::GetDetailFont()) .Text_Lambda([NestedChooser]() { if (NestedChooser->Chooser) { return FText::FromString(NestedChooser->Chooser->GetName()); } else { return LOCTEXT("None", "None"); } }) ] ]; Button->SetOnGetMenuContent( FOnGetContent::CreateLambda( [ Button, NestedChooser, TransactionObject]() { FMenuBuilder MenuBuilder(true, nullptr); MenuBuilder.BeginSection(NAME_None, LOCTEXT("Nested Chooser", "Nested Chooser")); { MenuBuilder.AddMenuEntry( LOCTEXT("None", "None"), LOCTEXT("NewNestedChooser_Tooltip", "Create a new Chooser Table embedded in this asset, and reference it"), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([Button, NestedChooser, TransactionObject]() { const FScopedTransaction Transaction(LOCTEXT("Clear Nested Chooser", "Clear Nested Chooser")); TransactionObject->Modify(true); Button->SetIsOpen(false); NestedChooser->Chooser = nullptr; }))); MenuBuilder.AddMenuEntry( LOCTEXT("New Nested Chooser", "New Nested Chooser"), LOCTEXT("NewNestedChooser_Tooltip", "Create a new Chooser Table embedded in this asset, and reference it"), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([Button, NestedChooser, TransactionObject]() { Button->SetIsOpen(false); TSharedRef TextEntry = SNew(STextEntryPopup) .Label(LOCTEXT("NewNestedChooserLabel", "New Chooser Name")) .OnTextCommitted_Lambda([NestedChooser, TransactionObject](FText InText, ETextCommit::Type InCommitType) { if (InCommitType == ETextCommit::OnEnter) { FSlateApplication::Get().DismissAllMenus(); FName NewChooserName = FName(InText.ToString()); if (NewChooserName.IsNone()) { NewChooserName = "NestedChooser"; } FString NewChooserPlainName = NewChooserName.GetPlainNameString(); UChooserTable* ParentChooser = Cast(TransactionObject); UChooserTable* RootChooser = nullptr; if (ParentChooser) { RootChooser = ParentChooser->GetRootChooser(); bool bNameConflict = false; int MaxNumber = NewChooserName.GetNumber(); for(UChooserTable* NestedChooserIterator : RootChooser->NestedChoosers) { FName NestedChooserName = NestedChooserIterator->GetFName(); if (NestedChooserName.GetPlainNameString() == NewChooserPlainName) { MaxNumber = FMath::Max(MaxNumber, NestedChooserName.GetNumber()); if(NestedChooserName.GetNumber() == NewChooserName.GetNumber()) { bNameConflict = true; } } } if (bNameConflict) { NewChooserName.SetNumber(MaxNumber + 1); } } UChooserTable* NewChooser = NewObject(TransactionObject, UChooserTable::StaticClass(), NewChooserName, RF_Transactional); const FScopedTransaction Transaction(LOCTEXT("Assign New Nested Chooser", "Assign New Nested Chooser")); TransactionObject->Modify(); NestedChooser->Chooser = NewChooser; if (RootChooser) { NestedChooser->Chooser->RootChooser = RootChooser; RootChooser->Modify(); RootChooser->AddNestedChooser(NestedChooser->Chooser); } } }); FSlateApplication& SlateApp = FSlateApplication::Get(); SlateApp.PushMenu( SlateApp.GetInteractiveTopLevelWindows()[0], FWidgetPath(), TextEntry, SlateApp.GetCursorPos(), FPopupTransitionEffect::TypeInPopup ); }))); MenuBuilder.AddSubMenu(LOCTEXT("Select Existing", "Select Existing"), LOCTEXT("SelectExisting_Tooltip", "Select Existing Embedded UChooserTable from this asset"), FNewMenuDelegate::CreateLambda([Button, NestedChooser, TransactionObject](FMenuBuilder& SubMenuBuilder) { SubMenuBuilder.BeginSection("Existing", LOCTEXT("Existing Choosers", "Existing Choosers")); UChooserTable* OuterChooser = Cast(TransactionObject); UChooserTable* RootTable = nullptr; if (OuterChooser) { RootTable = OuterChooser->GetRootChooser(); for (UChooserTable* Chooser : RootTable->NestedChoosers) { // exclude the main chooser if (Chooser != RootTable) { SubMenuBuilder.AddMenuEntry( FText::FromString(Chooser->GetName()), LOCTEXT("AddExistingObjectTooltip", "Add a reference to this existing Chooser Table."), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([Chooser, Button, NestedChooser, TransactionObject]() { const FScopedTransaction Transaction(LOCTEXT("Set Nested Chooser", "Set Nested Chooser")); TransactionObject->Modify(true); Button->SetIsOpen(false); NestedChooser->Chooser = Chooser; }))); } } } })); } return MenuBuilder.MakeWidget(); }) ); return SNew(SHorizontalBox) + SHorizontalBox::Slot().FillWidth(1.0) [ Button ] + SHorizontalBox::Slot().AutoWidth() [ SNew(SButton) .Text(LOCTEXT("Edit","Edit")) .OnClicked_Lambda([NestedChooser, TransactionObject]() { if (NestedChooser->Chooser) { UObject* RootChooser = TransactionObject->GetPackage()->FindAssetInPackage(); IAssetEditorInstance* Editor = GEditor->GetEditorSubsystem()->FindEditorForAsset(RootChooser, false); if (Editor && Editor->GetEditorName() == FChooserTableEditor::EditorName) { // currently nested tables are only editable in the Chooser Table Editor FChooserTableEditor* ChooserEditor = static_cast(Editor); ChooserEditor->PushChooserTableToEdit(NestedChooser->Chooser); } } return FReply::Handled(); }) ]; } } #undef LOCTEXT_NAMESPACE