diff --git a/Resources/Slate/Editor/AdditionalDialogueData.png b/Resources/Slate/Editor/AdditionalDialogueData.png new file mode 100644 index 0000000..45ec341 Binary files /dev/null and b/Resources/Slate/Editor/AdditionalDialogueData.png differ diff --git a/Resources/Slate/Editor/AutoArrangeIcon.png b/Resources/Slate/Editor/AutoArrangeIcon.png new file mode 100644 index 0000000..bee1f86 Binary files /dev/null and b/Resources/Slate/Editor/AutoArrangeIcon.png differ diff --git a/Resources/Slate/Editor/BrowseIcon.png b/Resources/Slate/Editor/BrowseIcon.png new file mode 100644 index 0000000..b29ec2a Binary files /dev/null and b/Resources/Slate/Editor/BrowseIcon.png differ diff --git a/Resources/Slate/Editor/Bubble.png b/Resources/Slate/Editor/Bubble.png new file mode 100644 index 0000000..019f3cf Binary files /dev/null and b/Resources/Slate/Editor/Bubble.png differ diff --git a/Resources/Slate/Editor/CircleBox.png b/Resources/Slate/Editor/CircleBox.png new file mode 100644 index 0000000..41b17a2 Binary files /dev/null and b/Resources/Slate/Editor/CircleBox.png differ diff --git a/Resources/Slate/Editor/CloseIcon.png b/Resources/Slate/Editor/CloseIcon.png new file mode 100644 index 0000000..cfab072 Binary files /dev/null and b/Resources/Slate/Editor/CloseIcon.png differ diff --git a/Resources/Slate/Editor/DialogueDecorator.png b/Resources/Slate/Editor/DialogueDecorator.png new file mode 100644 index 0000000..cdf0e58 Binary files /dev/null and b/Resources/Slate/Editor/DialogueDecorator.png differ diff --git a/Resources/Slate/Editor/DialogueDecorator_Small.png b/Resources/Slate/Editor/DialogueDecorator_Small.png new file mode 100644 index 0000000..9319ef6 Binary files /dev/null and b/Resources/Slate/Editor/DialogueDecorator_Small.png differ diff --git a/Resources/Slate/Editor/DialogueManagerIcon.png b/Resources/Slate/Editor/DialogueManagerIcon.png new file mode 100644 index 0000000..900034a Binary files /dev/null and b/Resources/Slate/Editor/DialogueManagerIcon.png differ diff --git a/Resources/Slate/Editor/DialogueNodeIcon.png b/Resources/Slate/Editor/DialogueNodeIcon.png new file mode 100644 index 0000000..df13ee0 Binary files /dev/null and b/Resources/Slate/Editor/DialogueNodeIcon.png differ diff --git a/Resources/Slate/Editor/DialogueParticipantIcon.png b/Resources/Slate/Editor/DialogueParticipantIcon.png new file mode 100644 index 0000000..466a691 Binary files /dev/null and b/Resources/Slate/Editor/DialogueParticipantIcon.png differ diff --git a/Resources/Slate/Editor/DialogueTreeIcon_128.png b/Resources/Slate/Editor/DialogueTreeIcon_128.png new file mode 100644 index 0000000..9458d23 Binary files /dev/null and b/Resources/Slate/Editor/DialogueTreeIcon_128.png differ diff --git a/Resources/Slate/Editor/DialogueTreeIcon_16.png b/Resources/Slate/Editor/DialogueTreeIcon_16.png new file mode 100644 index 0000000..469d06d Binary files /dev/null and b/Resources/Slate/Editor/DialogueTreeIcon_16.png differ diff --git a/Resources/Slate/Editor/DiscordIcon.png b/Resources/Slate/Editor/DiscordIcon.png new file mode 100644 index 0000000..147a5aa Binary files /dev/null and b/Resources/Slate/Editor/DiscordIcon.png differ diff --git a/Resources/Slate/Editor/DiscordIcon.png~ b/Resources/Slate/Editor/DiscordIcon.png~ new file mode 100644 index 0000000..0caf984 Binary files /dev/null and b/Resources/Slate/Editor/DiscordIcon.png~ differ diff --git a/Resources/Slate/Editor/Documentation.png b/Resources/Slate/Editor/Documentation.png new file mode 100644 index 0000000..b4f23da Binary files /dev/null and b/Resources/Slate/Editor/Documentation.png differ diff --git a/Resources/Slate/Editor/DocumentationIcon.png b/Resources/Slate/Editor/DocumentationIcon.png new file mode 100644 index 0000000..3583bce Binary files /dev/null and b/Resources/Slate/Editor/DocumentationIcon.png differ diff --git a/Resources/Slate/Editor/EditIcon.png b/Resources/Slate/Editor/EditIcon.png new file mode 100644 index 0000000..2436887 Binary files /dev/null and b/Resources/Slate/Editor/EditIcon.png differ diff --git a/Resources/Slate/Editor/ErroIcon.png b/Resources/Slate/Editor/ErroIcon.png new file mode 100644 index 0000000..62fe91e Binary files /dev/null and b/Resources/Slate/Editor/ErroIcon.png differ diff --git a/Resources/Slate/Editor/FancyArrow.png b/Resources/Slate/Editor/FancyArrow.png new file mode 100644 index 0000000..c921dd8 Binary files /dev/null and b/Resources/Slate/Editor/FancyArrow.png differ diff --git a/Resources/Slate/Editor/GraphSettings.png b/Resources/Slate/Editor/GraphSettings.png new file mode 100644 index 0000000..f75de66 Binary files /dev/null and b/Resources/Slate/Editor/GraphSettings.png differ diff --git a/Resources/Slate/Editor/HeartIcon.png b/Resources/Slate/Editor/HeartIcon.png new file mode 100644 index 0000000..b8f90c1 Binary files /dev/null and b/Resources/Slate/Editor/HeartIcon.png differ diff --git a/Resources/Slate/Editor/HollowArrow.png b/Resources/Slate/Editor/HollowArrow.png new file mode 100644 index 0000000..bcc1a22 Binary files /dev/null and b/Resources/Slate/Editor/HollowArrow.png differ diff --git a/Resources/Slate/Editor/Icon128.png b/Resources/Slate/Editor/Icon128.png new file mode 100644 index 0000000..272c641 Binary files /dev/null and b/Resources/Slate/Editor/Icon128.png differ diff --git a/Resources/Slate/Editor/IndexIcon.png b/Resources/Slate/Editor/IndexIcon.png new file mode 100644 index 0000000..3dfe6c5 Binary files /dev/null and b/Resources/Slate/Editor/IndexIcon.png differ diff --git a/Resources/Slate/Editor/InfoIcon_Small.png b/Resources/Slate/Editor/InfoIcon_Small.png new file mode 100644 index 0000000..5a5369f Binary files /dev/null and b/Resources/Slate/Editor/InfoIcon_Small.png differ diff --git a/Resources/Slate/Editor/MoneyIcon.png b/Resources/Slate/Editor/MoneyIcon.png new file mode 100644 index 0000000..a05ea1a Binary files /dev/null and b/Resources/Slate/Editor/MoneyIcon.png differ diff --git a/Resources/Slate/Editor/Mountea_Logo.png b/Resources/Slate/Editor/Mountea_Logo.png new file mode 100644 index 0000000..8b77760 Binary files /dev/null and b/Resources/Slate/Editor/Mountea_Logo.png differ diff --git a/Resources/Slate/Editor/NodeHardCorners.png b/Resources/Slate/Editor/NodeHardCorners.png new file mode 100644 index 0000000..b677898 Binary files /dev/null and b/Resources/Slate/Editor/NodeHardCorners.png differ diff --git a/Resources/Slate/Editor/NodeOverlay.png b/Resources/Slate/Editor/NodeOverlay.png new file mode 100644 index 0000000..702c9bb Binary files /dev/null and b/Resources/Slate/Editor/NodeOverlay.png differ diff --git a/Resources/Slate/Editor/NodeSoftCorners.png b/Resources/Slate/Editor/NodeSoftCorners.png new file mode 100644 index 0000000..5f843a8 Binary files /dev/null and b/Resources/Slate/Editor/NodeSoftCorners.png differ diff --git a/Resources/Slate/Editor/OKIcon.png b/Resources/Slate/Editor/OKIcon.png new file mode 100644 index 0000000..b8ff9e1 Binary files /dev/null and b/Resources/Slate/Editor/OKIcon.png differ diff --git a/Resources/Slate/Editor/PinDocksOverlay.png b/Resources/Slate/Editor/PinDocksOverlay.png new file mode 100644 index 0000000..4ff1ef6 Binary files /dev/null and b/Resources/Slate/Editor/PinDocksOverlay.png differ diff --git a/Resources/Slate/Editor/RoundedSelection_16x.PNG b/Resources/Slate/Editor/RoundedSelection_16x.PNG new file mode 100644 index 0000000..0589dc9 Binary files /dev/null and b/Resources/Slate/Editor/RoundedSelection_16x.PNG differ diff --git a/Resources/Slate/Editor/SearchIcon.png b/Resources/Slate/Editor/SearchIcon.png new file mode 100644 index 0000000..360504f Binary files /dev/null and b/Resources/Slate/Editor/SearchIcon.png differ diff --git a/Resources/Slate/Editor/SimpleArrow.png b/Resources/Slate/Editor/SimpleArrow.png new file mode 100644 index 0000000..7d2e979 Binary files /dev/null and b/Resources/Slate/Editor/SimpleArrow.png differ diff --git a/Resources/Slate/Editor/TextBlockHighlightShape.png b/Resources/Slate/Editor/TextBlockHighlightShape.png new file mode 100644 index 0000000..01f956a Binary files /dev/null and b/Resources/Slate/Editor/TextBlockHighlightShape.png differ diff --git a/Resources/Slate/Editor/TextBox.png b/Resources/Slate/Editor/TextBox.png new file mode 100644 index 0000000..02bc70e Binary files /dev/null and b/Resources/Slate/Editor/TextBox.png differ diff --git a/Resources/Slate/Editor/TextBox_Hovered.png b/Resources/Slate/Editor/TextBox_Hovered.png new file mode 100644 index 0000000..e26ec40 Binary files /dev/null and b/Resources/Slate/Editor/TextBox_Hovered.png differ diff --git a/Resources/Slate/Editor/TextBox_ReadOnly.png b/Resources/Slate/Editor/TextBox_ReadOnly.png new file mode 100644 index 0000000..8a9139e Binary files /dev/null and b/Resources/Slate/Editor/TextBox_ReadOnly.png differ diff --git a/Resources/Slate/Editor/TextHardCorners.png b/Resources/Slate/Editor/TextHardCorners.png new file mode 100644 index 0000000..9a3e5c0 Binary files /dev/null and b/Resources/Slate/Editor/TextHardCorners.png differ diff --git a/Resources/Slate/Editor/TextSoftCorners.png b/Resources/Slate/Editor/TextSoftCorners.png new file mode 100644 index 0000000..5106562 Binary files /dev/null and b/Resources/Slate/Editor/TextSoftCorners.png differ diff --git a/Resources/Slate/Editor/UnrealBucketIcon.png b/Resources/Slate/Editor/UnrealBucketIcon.png new file mode 100644 index 0000000..a582728 Binary files /dev/null and b/Resources/Slate/Editor/UnrealBucketIcon.png differ diff --git a/Resources/Slate/Editor/ValidateGraph.png b/Resources/Slate/Editor/ValidateGraph.png new file mode 100644 index 0000000..8bae640 Binary files /dev/null and b/Resources/Slate/Editor/ValidateGraph.png differ diff --git a/Source/ComboInputEditor/ComboInputEditor.build.cs b/Source/ComboInputEditor/ComboInputEditor.build.cs index 135e492..0ff5870 100644 --- a/Source/ComboInputEditor/ComboInputEditor.build.cs +++ b/Source/ComboInputEditor/ComboInputEditor.build.cs @@ -30,8 +30,10 @@ public class ComboInputEditor : ModuleRules "ApplicationCore", "AssetTools", + "BlueprintGraph", "DeveloperSettings", "GraphEditor", + "InputCore", "Projects", "Slate", "SlateCore", diff --git a/Source/ComboInputEditor/Private/ComboInputEditor.cpp b/Source/ComboInputEditor/Private/ComboInputEditor.cpp index 0a62362..5191445 100644 --- a/Source/ComboInputEditor/Private/ComboInputEditor.cpp +++ b/Source/ComboInputEditor/Private/ComboInputEditor.cpp @@ -10,6 +10,7 @@ #include "Ed/AssetEditor_ComboActionGraph.h" #include "Ed/EdComboActionGraphEdge.h" #include "Ed/EdComboActionGraphNode.h" +#include "Ed/FComboActionGraphEditorCommands.h" #include "Ed/SEdComboActionGraphEdge.h" #include "Ed/SEdComboActionGraphNode.h" #include "Interfaces/IPluginManager.h" @@ -217,8 +218,8 @@ FComboInputSlateStyle::FComboInputSlateStyle() : FSlateStyleSet("ComboInputEdito // Combo Action Graph editor brushes { - this->SetContentRoot(IPluginManager::Get().FindPlugin("ComboInput")->GetBaseDir() / TEXT("Resources")); - this->SetCoreContentRoot(IPluginManager::Get().FindPlugin("ComboInput")->GetBaseDir() / TEXT("Content")); + this->SetContentRoot(PluginSlateDir / TEXT("Resources/Slate/Editor")); + this->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate/Editor")); this->Set("MDSStyleSet.AutoArrange.small", new IMAGE_BRUSH(TEXT("AutoArrangeIcon"), Icon16)); this->Set("MDSStyleSet.AutoArrange", new IMAGE_BRUSH(TEXT("AutoArrangeIcon"), Icon40)); @@ -330,6 +331,13 @@ void FComboInputEditorModule::StartupModule() this->ComboActionGraphAssetActions = MakeShared(); this->RegisterAssetTypeActions(AssetTools, this->ComboActionGraphAssetActions.ToSharedRef()); } + + FComboActionGraphEditorCommands::Register(); +} + +void FComboInputEditorModule::ShutdownModule() +{ + FComboActionGraphEditorCommands::Unregister(); } #undef LOCTEXT_NAMESPACE diff --git a/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.cpp b/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.cpp index 96f31bc..744688f 100644 --- a/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.cpp +++ b/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.cpp @@ -24,7 +24,8 @@ #include "Layout/ComboActionForceDirectedSolveLayoutStrategy.h" #include "Layout/ComboActionTreeSolveLayoutStrategy.h" //#include "Popups/MDSPopup_GraphValidation.h" -//#include "Search/ComboActionSearchUtils.h" +#include "Search/ComboActionSearchUtils.h" +#include "Search/SComboActionSearch.h" #include "Settings/ComboActionGraphEditorSettings.h" #include "UObject/ObjectSaveContext.h" @@ -54,14 +55,10 @@ FAssetEditor_ComboActionGraph::~FAssetEditor_ComboActionGraph() this->EditingGraph = nullptr; UPackage::PackageSavedWithContextEvent.Remove(this->OnPackageSavedDelegateHandle); - FGenericCommands::Unregister(); - FGraphEditorCommands::Unregister(); - FComboActionGraphEditorCommands::Unregister(); - this->ToolbarBuilder.Reset(); } -void FAssetEditor_ComboActionGraph::OnPackageSaved(const FString& String, UPackage* Package, FObjectPostSaveContext ObjectPostSaveContext) +void FAssetEditor_ComboActionGraph::OnPackageSaved(const FString &String, UPackage *Package, FObjectPostSaveContext ObjectPostSaveContext) { this->RebuildComboActionGraph(); } @@ -70,10 +67,6 @@ void FAssetEditor_ComboActionGraph::InitComboActionGraphAssetEditor(const EToolk { this->EditingGraph = Graph; this->CreateEdGraph(); - - FGenericCommands::Register(); - FGraphEditorCommands::Register(); - FComboActionGraphEditorCommands::Register(); if (!this->ToolbarBuilder.IsValid()) { @@ -164,13 +157,13 @@ void FAssetEditor_ComboActionGraph::RegisterTabSpawners(const TSharedRefRegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::SearchToolbarID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Search)) - // .SetDisplayName(LOCTEXT("SearchTab", "Search")) - // .SetGroup(WorkspaceMenuCategoryRef) - // .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults")); + InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::SearchToolbarID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Search)) + .SetDisplayName(LOCTEXT("SearchTab", "Search")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.Tabs.FindResults")); } -void FAssetEditor_ComboActionGraph::UnregisterTabSpawners(const TSharedRef& InTabManager) +void FAssetEditor_ComboActionGraph::UnregisterTabSpawners(const TSharedRef &InTabManager) { FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); @@ -250,12 +243,12 @@ void FAssetEditor_ComboActionGraph::UpdateToolbar() // TODO } -void FAssetEditor_ComboActionGraph::RegisterToolbarTab(const TSharedRef& InTabManager) +void FAssetEditor_ComboActionGraph::RegisterToolbarTab(const TSharedRef &InTabManager) { FAssetEditorToolkit::RegisterTabSpawners(InTabManager); } -void FAssetEditor_ComboActionGraph::AddReferencedObjects(FReferenceCollector& Collector) +void FAssetEditor_ComboActionGraph::AddReferencedObjects(FReferenceCollector &Collector) { Collector.AddReferencedObject(this->EditingGraph); Collector.AddReferencedObject(this->EditingGraph->EdGraph); @@ -268,8 +261,10 @@ FString FAssetEditor_ComboActionGraph::GetReferencerName() const void FAssetEditor_ComboActionGraph::SetDialogueBeingEdited(UComboActionGraph *NewDialogue) { - if (NewDialogue == nullptr) return; - if (NewDialogue == this->EditingGraph) return; + if (NewDialogue == nullptr || NewDialogue == this->EditingGraph) + { + return; + } UComboActionGraph *Previous = this->EditingGraph; this->EditingGraph = NewDialogue; @@ -282,7 +277,7 @@ void FAssetEditor_ComboActionGraph::CreateInternalWidgets() { this->ViewportWidget = this->CreateViewportWidget(); - FDetailsViewArgs Args; //( false, false, true, FDetailsViewArgs::HideNameArea, false ); + FDetailsViewArgs Args; Args.bUpdatesFromSelection = false; Args.bLockable = false; Args.bAllowSearch = true; @@ -292,11 +287,11 @@ void FAssetEditor_ComboActionGraph::CreateInternalWidgets() FPropertyEditorModule &PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); this->PropertyWidget = PropertyModule.CreateDetailView(Args); - this->PropertyWidget->SetObject(EditingGraph); + this->PropertyWidget->SetObject(this->EditingGraph); this->PropertyWidget->OnFinishedChangingProperties().AddSP(this, &FAssetEditor_ComboActionGraph::OnFinishedChangingProperties); - //this->FindResultsView = SNew(SComboActionSearch, SharedThis(this)); + this->FindResultsView = SNew(SComboActionSearch, SharedThis(this)); } TSharedRef FAssetEditor_ComboActionGraph::CreateViewportWidget() @@ -469,11 +464,6 @@ void FAssetEditor_ComboActionGraph::SelectAllNodes() } } -bool FAssetEditor_ComboActionGraph::CanSelectAllNodes() -{ - return true; -} - void FAssetEditor_ComboActionGraph::DeleteSelectedNodes() { TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); @@ -528,7 +518,7 @@ void FAssetEditor_ComboActionGraph::DeleteSelectedNodes() this->RebuildComboActionGraph(); } -bool FAssetEditor_ComboActionGraph::CanDeleteNodes() +bool FAssetEditor_ComboActionGraph::CanDeleteNodes() const { // If any of the nodes can be deleted then we should allow deleting const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes(); @@ -584,11 +574,6 @@ void FAssetEditor_ComboActionGraph::CutSelectedNodes() this->DeleteSelectedDuplicatableNodes(); } -bool FAssetEditor_ComboActionGraph::CanCutNodes() -{ - return this->CanCopyNodes() && this->CanDeleteNodes(); -} - void FAssetEditor_ComboActionGraph::CopySelectedNodes() { // Export the selected nodes and place the text on the clipboard @@ -624,7 +609,7 @@ void FAssetEditor_ComboActionGraph::CopySelectedNodes() FPlatformApplicationMisc::ClipboardCopy(*ExportedText); } -bool FAssetEditor_ComboActionGraph::CanCopyNodes() +bool FAssetEditor_ComboActionGraph::CanCopyNodes() const { // If any of the nodes can be duplicated then we should allow copying const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes(); @@ -645,11 +630,11 @@ void FAssetEditor_ComboActionGraph::PasteNodes() TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (CurrentGraphEditor.IsValid()) { - PasteNodesHere(CurrentGraphEditor->GetPasteLocation()); + this->PasteNodesHere(CurrentGraphEditor->GetPasteLocation()); } } -void FAssetEditor_ComboActionGraph::PasteNodesHere(const FVector2D& Location) +void FAssetEditor_ComboActionGraph::PasteNodesHere(const FVector2D &Location) { // Find the graph editor with focus TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); @@ -731,7 +716,7 @@ void FAssetEditor_ComboActionGraph::PasteNodesHere(const FVector2D& Location) this->RebuildComboActionGraph(); } -bool FAssetEditor_ComboActionGraph::CanPasteNodes() +bool FAssetEditor_ComboActionGraph::CanPasteNodes() const { const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) @@ -755,17 +740,6 @@ bool FAssetEditor_ComboActionGraph::CanPasteNodes() return FEdGraphUtilities::CanImportNodesFromText(CurrentGraphEditor->GetCurrentGraph(), ClipboardContent); } -void FAssetEditor_ComboActionGraph::DuplicateNodes() -{ - this->CopySelectedNodes(); - this->PasteNodes(); -} - -bool FAssetEditor_ComboActionGraph::CanDuplicateNodes() -{ - return this->CanCopyNodes(); -} - void FAssetEditor_ComboActionGraph::AutoArrange() { UEdComboActionGraph *EdGraph = Cast(this->EditingGraph->EdGraph); @@ -821,21 +795,16 @@ void FAssetEditor_ComboActionGraph::ValidateGraph() this->RebuildComboActionGraph(); - //TArray ValidationMessages; - //if (ComboActionGraph->ValidateGraph(ValidationMessages, true) == false) - //{ - // ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages); - //} - //else - //{ - // ValidationMessages.Empty(); - // ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages); - //} -} - -bool FAssetEditor_ComboActionGraph::CanValidateGraph() const -{ - return true; + TArray ValidationMessages; + if (ComboActionGraph->ValidateGraph(ValidationMessages, true) == false) + { + //ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages); + } + else + { + ValidationMessages.Empty(); + //ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages); + } } void FAssetEditor_ComboActionGraph::OnRenameNode() @@ -924,12 +893,26 @@ TSharedRef FAssetEditor_ComboActionGraph::SpawnTab_Details(const FSpaw auto DockTab = SNew(SDockTab) .Label(LOCTEXT("Details_Title", "Property")) [ - PropertyWidget.ToSharedRef() + this->PropertyWidget.ToSharedRef() ]; DockTab->SetTabIcon(FAppStyle::GetBrush("LevelEditor.Tabs.Details")); return DockTab; } +TSharedRef FAssetEditor_ComboActionGraph::SpawnTab_Search(const FSpawnTabArgs &Args) +{ + check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::SearchToolbarID); + + auto DockTab = SNew(SDockTab) + .Label(LOCTEXT("Search_Title", "Search")) + [ + this->FindResultsView.ToSharedRef() + ]; + + DockTab->SetTabIcon(FAppStyle::GetBrush("Kismet.Tabs.FindResults")); + return DockTab; +} + #undef LOCTEXT_NAMESPACE diff --git a/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.h b/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.h index 2813f7b..d42055f 100644 --- a/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.h +++ b/Source/ComboInputEditor/Private/Ed/AssetEditor_ComboActionGraph.h @@ -99,25 +99,25 @@ private: #pragma region GraphEditorCommands void SelectAllNodes(); - bool CanSelectAllNodes(); + bool CanSelectAllNodes() const { return true; } void DeleteSelectedNodes(); - bool CanDeleteNodes(); + bool CanDeleteNodes() const; void DeleteSelectedDuplicatableNodes(); void CutSelectedNodes(); - bool CanCutNodes(); + bool CanCutNodes() const { return this->CanCopyNodes() && this->CanDeleteNodes(); } void CopySelectedNodes(); - bool CanCopyNodes(); + bool CanCopyNodes() const; void PasteNodes(); void PasteNodesHere(const FVector2D &Location); - bool CanPasteNodes(); - void DuplicateNodes(); - bool CanDuplicateNodes(); + bool CanPasteNodes() const; + void DuplicateNodes() { this->CopySelectedNodes(); this->PasteNodes(); } + bool CanDuplicateNodes() { return this->CanCopyNodes(); } void AutoArrange(); bool CanAutoArrange() const; void ValidateGraph(); - bool CanValidateGraph() const; + bool CanValidateGraph() const { return true; } void OnRenameNode(); bool CanRenameNodes() const; @@ -142,6 +142,7 @@ private: TSharedRef SpawnTab_Viewport(const FSpawnTabArgs &Args); TSharedRef SpawnTab_Details(const FSpawnTabArgs &Args); + TSharedRef SpawnTab_Search(const FSpawnTabArgs &Args); class UComboActionGraphEditorSettings *ComboActionGraphEditorSettings; @@ -155,7 +156,7 @@ private: TSharedPtr ViewportWidget; TSharedPtr PropertyWidget; - //TSharedPtr FindResultsView; + TSharedPtr FindResultsView; /** The command list for this editor */ TSharedPtr GraphEditorCommands; diff --git a/Source/ComboInputEditor/Private/Helpers/ComboActionGraphEditorUtilities.cpp b/Source/ComboInputEditor/Private/Helpers/ComboActionGraphEditorUtilities.cpp new file mode 100644 index 0000000..2bd8a15 --- /dev/null +++ b/Source/ComboInputEditor/Private/Helpers/ComboActionGraphEditorUtilities.cpp @@ -0,0 +1,257 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#include "ComboActionGraphEditorUtilities.h" + +#include "ComboActionGraph.h" +#include "K2Node_Event.h" + +#include "Ed/AssetEditor_ComboActionGraph.h" +#include "Ed/EdComboActionGraph.h" +#include "Ed/EdComboActionGraphNode.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Kismet2/SClassPickerDialog.h" +#include "Layout/AssetEditorTabs.h" + + +bool FComboActionGraphEditorUtilities::PickChildrenOfClass(const FText &TitleText, UClass *&OutChosenClass, UClass *Class) +{ + // Create filter + TSharedPtr Filter = MakeShareable(new FComboActionClassViewerFilter); + Filter->AllowedChildrenOfClasses.Add(Class); + + // Fill in options + FClassViewerInitializationOptions Options; + Options.Mode = EClassViewerMode::ClassPicker; + Options.bShowUnloadedBlueprints = true; + Options.ClassFilters.Add(Filter.ToSharedRef()); + + Options.DisplayMode = EClassViewerDisplayMode::TreeView; + + Options.bShowNoneOption = false; + Options.InitiallySelectedClass = Class; + + Options.bExpandRootNodes = true; + Options.NameTypeToDisplay = EClassViewerNameTypeToDisplay::DisplayName; + + return SClassPickerDialog::PickClass(TitleText, Options, OutChosenClass, Class); +} + +bool FComboActionGraphEditorUtilities::OpenBlueprintEditor(UBlueprint *Blueprint, EComboActionBlueprintOpenType OpenType, FName FunctionNameToOpen, bool bForceFullEditor, bool bAddBlueprintFunctionIfItDoesNotExist) +{ + if (!Blueprint) + { + return false; + } + + Blueprint->bForceFullEditor = bForceFullEditor; + + // Find Function Graph + UObject *ObjectToFocusOn = nullptr; + if (OpenType != EComboActionBlueprintOpenType::None && FunctionNameToOpen != NAME_None) + { + UClass* Class = Blueprint->GeneratedClass; + check(Class); + + if (OpenType == EComboActionBlueprintOpenType::Function) + { + ObjectToFocusOn = bAddBlueprintFunctionIfItDoesNotExist + ? FComboActionGraphEditorUtilities::BlueprintGetOrAddFunction(Blueprint, FunctionNameToOpen, Class) + : FComboActionGraphEditorUtilities::BlueprintGetFunction(Blueprint, FunctionNameToOpen, Class); + } + else if (OpenType == EComboActionBlueprintOpenType::Event) + { + ObjectToFocusOn = bAddBlueprintFunctionIfItDoesNotExist + ? FComboActionGraphEditorUtilities::BlueprintGetOrAddEvent(Blueprint, FunctionNameToOpen, Class) + : FComboActionGraphEditorUtilities::BlueprintGetEvent(Blueprint, FunctionNameToOpen, Class); + } + } + + // Default to the last uber graph + if (ObjectToFocusOn == nullptr) + { + ObjectToFocusOn = Blueprint->GetLastEditedUberGraph(); + } + if (ObjectToFocusOn) + { + FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ObjectToFocusOn); + return true; + } + + return FComboActionGraphEditorUtilities::OpenEditorForAsset(Blueprint); +} + +UEdGraph *FComboActionGraphEditorUtilities::BlueprintGetOrAddFunction(UBlueprint *Blueprint, FName FunctionName, UClass *FunctionClassSignature) +{ + if (!Blueprint || Blueprint->BlueprintType != BPTYPE_Normal) + { + return nullptr; + } + + // Find existing function + if (UEdGraph *GraphFunction = BlueprintGetFunction(Blueprint, FunctionName, FunctionClassSignature)) + { + return GraphFunction; + } + + // Create a new function + UEdGraph *NewGraph = FBlueprintEditorUtils::CreateNewGraph(Blueprint, FunctionName, UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); + FBlueprintEditorUtils::AddFunctionGraph(Blueprint, NewGraph, /*bIsUserCreated=*/ false, FunctionClassSignature); + Blueprint->LastEditedDocuments.Add(NewGraph); + return NewGraph; +} + +UEdGraph *FComboActionGraphEditorUtilities::BlueprintGetFunction(UBlueprint *Blueprint, FName FunctionName, UClass *FunctionClassSignature) +{ + if (!Blueprint || Blueprint->BlueprintType != BPTYPE_Normal) + { + return nullptr; + } + + // Find existing function + for (UEdGraph *GraphFunction : Blueprint->FunctionGraphs) + { + if (FunctionName == GraphFunction->GetFName()) + { + return GraphFunction; + } + } + + // Find in the implemented Interfaces Graphs + for (const FBPInterfaceDescription &Interface : Blueprint->ImplementedInterfaces) + { + for (UEdGraph *GraphFunction : Interface.Graphs) + { + if (FunctionName == GraphFunction->GetFName()) + { + return GraphFunction; + } + } + } + + return nullptr; +} + +UK2Node_Event *FComboActionGraphEditorUtilities::BlueprintGetOrAddEvent(UBlueprint *Blueprint, FName EventName, UClass *EventClassSignature) +{ + if (!Blueprint || Blueprint->BlueprintType != EBlueprintType::BPTYPE_Normal) + { + return nullptr; + } + + // Find existing event + if (UK2Node_Event *EventNode = FComboActionGraphEditorUtilities::BlueprintGetEvent(Blueprint, EventName, EventClassSignature)) + { + return EventNode; + } + + // Create a New Event + if (Blueprint->UbergraphPages.Num()) + { + int32 NodePositionY = 0; + UK2Node_Event* NodeEvent = FKismetEditorUtilities::AddDefaultEventNode( + Blueprint, + Blueprint->UbergraphPages[0], + EventName, + EventClassSignature, + NodePositionY + ); + NodeEvent->SetEnabledState(ENodeEnabledState::Enabled); + NodeEvent->NodeComment = ""; + NodeEvent->bCommentBubbleVisible = false; + return NodeEvent; + } + + return nullptr; +} + +UK2Node_Event *FComboActionGraphEditorUtilities::BlueprintGetEvent(UBlueprint *Blueprint, FName EventName, UClass *EventClassSignature) +{ + if (!Blueprint || Blueprint->BlueprintType != EBlueprintType::BPTYPE_Normal) + { + return nullptr; + } + + TArray AllEvents; + FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, AllEvents); + for (UK2Node_Event *EventNode : AllEvents) + { + if (EventNode->bOverrideFunction && EventNode->EventReference.GetMemberName() == EventName) + { + return EventNode; + } + } + + return nullptr; +} + +bool FComboActionGraphEditorUtilities::OpenEditorForAsset(const UObject* Asset) +{ + if (!IsValid(Asset) || !GEditor) + { + return false; + } + + return GEditor->GetEditorSubsystem()->OpenEditorForAsset(const_cast(Asset)); +} + +bool FComboActionGraphEditorUtilities::OpenEditorAndJumpToGraphNode(TWeakPtr DialogueEditorPtr, const UEdGraphNode *GraphNode, bool bFocusIfOpen) +{ + if (!IsValid(GraphNode)) + { + return false; + } + + if (!DialogueEditorPtr.IsValid()) + { + return false; + } + + // Open if not already. + UComboActionGraph *Dialogue = FComboActionGraphEditorUtilities::GetActionFromGraphNode(GraphNode); + if (!FComboActionGraphEditorUtilities::OpenEditorForAsset(Dialogue)) + { + return false; + } + + // Could still fail focus on the graph node + if (IAssetEditorInstance *EditorInstance = FindEditorForAsset(Dialogue, bFocusIfOpen)) + { + EditorInstance->FocusWindow(const_cast(GraphNode)); + + UEdComboActionGraph *GraphEditor = Cast(Dialogue->EdGraph); + if (GraphEditor) + { + TSet SelectedNodes; + SelectedNodes.Add(GraphNode); + + GraphEditor->SelectNodeSet(SelectedNodes); + DialogueEditorPtr.Pin()->JumpToNode(GraphNode); + } + return true; + } + return false; +} + +UComboActionGraph *FComboActionGraphEditorUtilities::GetActionFromGraphNode(const UEdGraphNode *GraphNode) +{ + if (const UEdComboActionGraphNode *ComboActionBaseNode = Cast(GraphNode)) + { + return ComboActionBaseNode->GetEdComboActionGraph()->GetComboActionGraph(); + } + if (const UEdComboActionGraph *ComboActionGraph = Cast(GraphNode->GetGraph())) + { + return Cast(ComboActionGraph->GetComboActionGraph()); + } + return nullptr; +} + +IAssetEditorInstance *FComboActionGraphEditorUtilities::FindEditorForAsset(UObject* Asset, bool bFocusIfOpen) +{ + if (!IsValid(Asset) || !GEditor) + { + return nullptr; + } + return GEditor->GetEditorSubsystem()->FindEditorForAsset(Asset, bFocusIfOpen); +} + diff --git a/Source/ComboInputEditor/Private/Helpers/ComboActionGraphEditorUtilities.h b/Source/ComboInputEditor/Private/Helpers/ComboActionGraphEditorUtilities.h new file mode 100644 index 0000000..be129ac --- /dev/null +++ b/Source/ComboInputEditor/Private/Helpers/ComboActionGraphEditorUtilities.h @@ -0,0 +1,103 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#pragma once + +#include "ClassViewerFilter.h" + + +enum class EComboActionBlueprintOpenType : uint8 +{ + None = 0, + Function, + Event +}; + +class FComboActionClassViewerFilter : public IClassViewerFilter +{ +public: + // All children of these classes will be included unless filtered out by another setting. + TSet AllowedChildrenOfClasses; + + virtual bool IsClassAllowed(const FClassViewerInitializationOptions &InInitOptions, const UClass *InClass, TSharedRef InFilterFuncs) override + { + return !InClass->HasAnyClassFlags(this->DisallowedClassFlags) + && InFilterFuncs->IfInChildOfClassesSet(this->AllowedChildrenOfClasses, InClass) != EFilterReturn::Failed; + } + + virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions &InInitOptions, const TSharedRef InUnloadedClassData, TSharedRef InFilterFuncs) override + { + return !InUnloadedClassData->HasAnyClassFlags(this->DisallowedClassFlags) + && InFilterFuncs->IfInChildOfClassesSet(this->AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed; + } + +private: + // Disallowed class flags. + EClassFlags DisallowedClassFlags = CLASS_Deprecated; +}; + +class FComboActionGraphEditorUtilities +{ +public: + static bool PickChildrenOfClass(const FText &TitleText, UClass *&OutChosenClass, UClass *Class); + static bool OpenBlueprintEditor + ( + UBlueprint *Blueprint, + EComboActionBlueprintOpenType OpenType = EComboActionBlueprintOpenType::None, + FName FunctionNameToOpen = NAME_None, + bool bForceFullEditor = true, + bool bAddBlueprintFunctionIfItDoesNotExist = false + ); + + static UEdGraph *BlueprintGetOrAddFunction(UBlueprint *Blueprint, FName FunctionName, UClass *FunctionClassSignature); + + static UEdGraph *BlueprintGetFunction(UBlueprint *Blueprint, FName FunctionName, UClass *FunctionClassSignature); + + static class UK2Node_Event *BlueprintGetOrAddEvent(UBlueprint *Blueprint, FName EventName, UClass *EventClassSignature); + + static class UK2Node_Event *BlueprintGetEvent(UBlueprint *Blueprint, FName EventName, UClass *EventClassSignature); + + static bool OpenEditorForAsset(const UObject *Asset); + + static bool IsABlueprintClass(const UClass *Class) { return Cast(Class) != nullptr; } + + static bool GetAllChildClassesOf(const UClass *ParentClass, TArray &OutNativeClasses, TArray &OutBlueprintClasses) + { + // Iterate over UClass, this might be heavy on performance + for (TObjectIterator It; It; ++It) + { + UClass *ChildClass = *It; + if (!ChildClass->IsChildOf(ParentClass)) + { + continue; + } + + // It is a child of the Parent Class + // make sure we don't include our parent class in the array + if (ChildClass == ParentClass) + { + continue; + } + + if (FComboActionGraphEditorUtilities::IsABlueprintClass(ChildClass)) + { + // Blueprint + OutBlueprintClasses.Add(ChildClass); + } + else + { + // Native + OutNativeClasses.Add(ChildClass); + } + } + + return OutNativeClasses.Num() > 0 || OutBlueprintClasses.Num() > 0; + } + + static bool OpenEditorAndJumpToGraphNode(TWeakPtr DialogueEditorPtr, const UEdGraphNode *GraphNode, bool bFocusIfOpen = false); + + static class UComboActionGraph *GetActionFromGraphNode(const UEdGraphNode *GraphNode); + + static IAssetEditorInstance *FindEditorForAsset(UObject *Asset, bool bFocusIfOpen); +}; + + diff --git a/Source/ComboInputEditor/Private/Search/ComboActionSearchFilter.h b/Source/ComboInputEditor/Private/Search/ComboActionSearchFilter.h new file mode 100644 index 0000000..aee8b64 --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/ComboActionSearchFilter.h @@ -0,0 +1,28 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#pragma once + + +struct FComboActionSearchFilter +{ +public: + bool IsEmptyFilter() const + { + return SearchString.IsEmpty() + && bIncludeNodeTitle == false + && bIncludeNodeType == false + && bIncludeNodeDecoratorsTypes == false + && bIncludeNodeData == true + && bIncludeNodeGUID == false; + } + +public: + // Search term that the search items must match + FString SearchString; + + bool bIncludeNodeTitle = true; + bool bIncludeNodeType = true; + bool bIncludeNodeDecoratorsTypes = true; + bool bIncludeNodeData = true; + bool bIncludeNodeGUID = false; +}; diff --git a/Source/ComboInputEditor/Private/Search/ComboActionSearchManager.cpp b/Source/ComboInputEditor/Private/Search/ComboActionSearchManager.cpp new file mode 100644 index 0000000..6058f8d --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/ComboActionSearchManager.cpp @@ -0,0 +1,275 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#include "ComboActionSearchManager.h" + +#include "ComboActionGraph.h" + +#include "AssetRegistry/AssetRegistryModule.h" +#include "Ed/EdComboActionGraph.h" +#include "Nodes/ComboActionGraphNode.h" +#include "Nodes/ComboActionGraphNode_ActionNodeBase.h" +#include "Search/ComboActionSearchFilter.h" + +#define LOCTEXT_NAMESPACE "ComboActionSearchManager" + +FComboActionSearchManager *FComboActionSearchManager::Instance = nullptr; + + +bool FComboActionSearchManager::QueryGraphNode(const FComboActionSearchFilter &SearchFilter, const UEdComboActionGraphNode *InGraphNode, const TSharedPtr &OutParentNode) const +{ + if (SearchFilter.SearchString.IsEmpty() || !OutParentNode.IsValid() || !IsValid(InGraphNode)) + { + return false; + } + + bool bContainsSearchString = false; + const UComboActionGraphNode *Node = InGraphNode->ComboActionGraphNode; + + if (Node == nullptr) + { + return false; + } + + const FString NodeType = Node->NodeTypeName.ToString(); + + const FText DisplayText = FText::Format(LOCTEXT("ComboActionNodeCategory", "Found results in {0}"), FText::FromString(NodeType)); + + const TSharedPtr TreeGraphNode = MakeShared(DisplayText, OutParentNode); + TreeGraphNode->SetCategory(FText::FromString(NodeType)); + TreeGraphNode->SetGraphNode(InGraphNode); + if (bContainsSearchString) + { + OutParentNode->AddChild(TreeGraphNode); + } + + // Search by Title + if (SearchFilter.bIncludeNodeTitle) + { + if (Node->NodeTitle.ToString().Contains(SearchFilter.SearchString)) + { + bContainsSearchString = true; + MakeChildTextNode + ( + TreeGraphNode, + FText::FromName(FName(Node->NodeTitle.ToString() )), + LOCTEXT("NodeTitleKey", "Node Title"), + TEXT("Node Title") + ); + } + } + + // Search by NodeTypeName + if (SearchFilter.bIncludeNodeType) + { + if (Node->NodeTypeName.ToString().Contains(SearchFilter.SearchString)) + { + bContainsSearchString = true; + this->MakeChildTextNode( + TreeGraphNode, + FText::FromName(FName(Node->NodeTypeName.ToString() )), + LOCTEXT("NodeTypeKey", "Node Type"), + TEXT("Node Type") + ); + } + } + + // Search by Decorators + if (SearchFilter.bIncludeNodeDecoratorsTypes) + { + const TArray &NodeDecorators = Node->GetNodeDecorators(); + for (int32 Index = 0, Num = NodeDecorators.Num(); Index < Num; Index++) + { + bContainsSearchString = this->QueryNodeDecorators( + SearchFilter, + NodeDecorators[Index], + TreeGraphNode, + Index, + TEXT("DecoratorType") + ) + || bContainsSearchString; + } + } + + // Search by Node Data + if (SearchFilter.bIncludeNodeData) + { + if (const UComboActionGraphNode_ActionNodeBase *ActionNodeBase = Cast(Node)) + { + if (ActionNodeBase->GetRowName().ToString().Contains(SearchFilter.SearchString)) + { + bContainsSearchString = true; + this->MakeChildTextNode( + TreeGraphNode, + FText::FromName(FName(Node->NodeTypeName.ToString() )), + LOCTEXT("NodeDataRowKey", "Node Data"), + TEXT("Node Data") + ); + } + } + } + + // Search by GUID + if (SearchFilter.bIncludeNodeGUID) + { + const FString FoundGUID = Node->GetNodeGUID().ToString(); + if (FoundGUID.Contains(SearchFilter.SearchString)) + { + bContainsSearchString = true; + MakeChildTextNode + ( + TreeGraphNode, + FText::FromString(FoundGUID), + LOCTEXT("NodeGUID", "Node GUID"), + TEXT("Node GUID") + ); + } + } + + if (bContainsSearchString) + { + OutParentNode->AddChild(TreeGraphNode); + } + + return bContainsSearchString; +} + +bool FComboActionSearchManager::QueryNodeDecorators(const FComboActionSearchFilter &SearchFilter, const FComboActionDecorator &InDecorator, const TSharedPtr &OutParentNode, int32 DecoratorIndex, FName DecoratorMemberName) const +{ + if (SearchFilter.SearchString.IsEmpty() || !OutParentNode.IsValid()) + { + return false; + } + bool bContainsSearchString = false; + + if (InDecorator.DecoratorType == nullptr) return false; + + // Search by Decorator Name + if (InDecorator.DecoratorType->GetName().Contains(SearchFilter.SearchString)) + { + bContainsSearchString = true; + + FString DecoratorName = InDecorator.DecoratorType->GetClass()->GetName(); + // Format Name + { + if (DecoratorName.Contains(TEXT("_GEN_VARIABLE"))) + { + DecoratorName.ReplaceInline(TEXT("_GEN_VARIABLE"), TEXT("")); + } + if(DecoratorName.EndsWith(TEXT("_C")) && DecoratorName.StartsWith(TEXT("Default__"))) + { + DecoratorName.RightChopInline(9); + DecoratorName.LeftChopInline(2); + } + if (DecoratorName.EndsWith(TEXT("_C"))) + { + DecoratorName.LeftChopInline(2); + } + } + + const FText Category = FText::Format + ( + LOCTEXT("DecoratorName", "Node Decorator: {0} at Index: {1}"), + FText::FromString(DecoratorName), FText::AsNumber(DecoratorIndex) + ); + MakeChildTextNode + ( + OutParentNode, + FText::FromString(DecoratorName), + Category, + Category.ToString() + ); + } + + return bContainsSearchString; +} + +bool FComboActionSearchManager::QuerySingleAction(const FComboActionSearchFilter &SearchFilter, const UComboActionGraph *InAction, TSharedPtr &OutParentNode) +{ + if (SearchFilter.SearchString.IsEmpty() || !OutParentNode.IsValid() || !IsValid(InAction)) + { + return false; + } + + const UEdComboActionGraph *Graph = CastChecked(InAction->EdGraph); + + const TSharedPtr TreeActionNode = MakeShared(FText::FromString(InAction->GetPathName()), OutParentNode); + TreeActionNode->SetActionGraph(Graph); + + // Find in GraphNodes + bool bFoundInAction = false; + const TArray &AllGraphNodes = Graph->Nodes; + for (UEdGraphNode *Node : AllGraphNodes) + { + bool bFoundInNode = false; + if (const UEdComboActionGraphNode *GraphNode = Cast(Node)) + { + bFoundInNode = this->QueryGraphNode(SearchFilter, GraphNode, TreeActionNode); + } + + // Found at least one match in one of the nodes. + bFoundInAction = bFoundInNode || bFoundInAction; + } + + if (bFoundInAction) + { + OutParentNode->AddChild(TreeActionNode); + } + + return bFoundInAction; +} + +void FComboActionSearchManager::Initialize(TSharedPtr ParentTabCategory) +{ + // Must ensure we do not attempt to load the AssetRegistry Module while saving a package, however, if it is loaded already we can safely obtain it + this->AssetRegistry = &FModuleManager::LoadModuleChecked("AssetRegistry").Get(); + + this->OnAssetAddedHandle = this->AssetRegistry->OnAssetAdded().AddRaw(this, &FComboActionSearchManager::HandleOnAssetAdded); + this->OnAssetRemovedHandle = this->AssetRegistry->OnAssetRemoved().AddRaw(this, &FComboActionSearchManager::HandleOnAssetRemoved); + this->OnAssetRenamedHandle = this->AssetRegistry->OnAssetRenamed().AddRaw(this, &FComboActionSearchManager::HandleOnAssetRenamed); + + if (this->AssetRegistry->IsLoadingAssets()) + { + this->OnFilesLoadedHandle = this->AssetRegistry->OnFilesLoaded().AddRaw(this, &FComboActionSearchManager::HandleOnAssetRegistryFilesLoaded); + } + else + { + this->HandleOnAssetRegistryFilesLoaded(); + } + this->OnAssetLoadedHandle = FCoreUObjectDelegates::OnAssetLoaded.AddRaw(this, &FComboActionSearchManager::HandleOnAssetLoaded); +} + +void FComboActionSearchManager::UnInitialize() +{ + if (this->AssetRegistry) + { + if (this->OnAssetAddedHandle.IsValid()) + { + this->AssetRegistry->OnAssetAdded().Remove(this->OnAssetAddedHandle); + this->OnAssetAddedHandle.Reset(); + } + if (this->OnAssetRemovedHandle.IsValid()) + { + this->AssetRegistry->OnAssetRemoved().Remove(this->OnAssetRemovedHandle); + this->OnAssetRemovedHandle.Reset(); + } + if (this->OnFilesLoadedHandle.IsValid()) + { + this->AssetRegistry->OnFilesLoaded().Remove(this->OnFilesLoadedHandle); + this->OnFilesLoadedHandle.Reset(); + } + if (this->OnAssetRenamedHandle.IsValid()) + { + this->AssetRegistry->OnAssetRenamed().Remove(this->OnAssetRenamedHandle); + this->OnAssetRenamedHandle.Reset(); + } + } + + if (this->OnAssetLoadedHandle.IsValid()) + { + FCoreUObjectDelegates::OnAssetLoaded.Remove(this->OnAssetLoadedHandle); + this->OnAssetLoadedHandle.Reset(); + } +} + + +#undef LOCTEXT_NAMESPACE diff --git a/Source/ComboInputEditor/Private/Search/ComboActionSearchManager.h b/Source/ComboInputEditor/Private/Search/ComboActionSearchManager.h new file mode 100644 index 0000000..9a156fd --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/ComboActionSearchManager.h @@ -0,0 +1,113 @@ +// Copyright Dominik Pavlicek 2023. All Rights Reserved. + +#pragma once + +#include "Search/ComboActionSearchResult.h" + +#include "ComboActionGraph.h" +#include "AssetRegistry/IAssetRegistry.h" +#include "Ed/EdComboActionGraphNode.h" + + +struct FDialogueSearchData +{ + TWeakObjectPtr Dialogue; +}; + +class FComboActionSearchManager +{ +public: + static FComboActionSearchManager *Get() + { + if (Instance == nullptr) + { + Instance = new FComboActionSearchManager(); + } + return Instance; + } + +public: + FComboActionSearchManager(){} + ~FComboActionSearchManager() { this->UnInitialize(); } + + /** + * Searches for InSearchString in the InGraphNode. Adds the result as a child in OutParentNode. + * @return True if found anything matching the InSearchString + */ + bool QueryGraphNode(const FComboActionSearchFilter &SearchFilter, const UEdComboActionGraphNode *InGraphNode, const TSharedPtr &OutParentNode) const; + + bool QueryNodeDecorators + ( + const FComboActionSearchFilter &SearchFilter, + const FComboActionDecorator &InDecorator, + const TSharedPtr &OutParentNode, + int32 DecoratorIndex, + FName DecoratorMemberName + ) const; + + /** + * Searches for InSearchString in the InAction. Adds the result as a child of OutParentNode. + * @return True if found anything matching the InSearchString + */ + bool QuerySingleAction + ( + const FComboActionSearchFilter &SearchFilter, + const UComboActionGraph *InAction, + TSharedPtr &OutParentNode + ); + + void Initialize(TSharedPtr ParentTabCategory = nullptr); + + void UnInitialize(); + + private: + + // Helper method to make a Text Node and add it as a child to ParentNode + TSharedPtr MakeChildTextNode + ( + const TSharedPtr &ParentNode, + const FText &DisplayName, const FText &Category, + const FString &CommentString + ) const + { + TSharedPtr TextNode = MakeShared(DisplayName, ParentNode); + TextNode->SetCategory(Category); + if (!CommentString.IsEmpty()) + { + TextNode->SetCommentString(CommentString); + } + ParentNode->AddChild(TextNode); + return TextNode; + } + + // Callback hook from the Asset Registry when an asset is added + void HandleOnAssetAdded(const FAssetData &InAssetData){} + + // Callback hook from the Asset Registry, marks the asset for deletion from the cache + void HandleOnAssetRemoved(const FAssetData &InAssetData){} + + // Callback hook from the Asset Registry, marks the asset for deletion from the cache + void HandleOnAssetRenamed(const FAssetData &InAssetData, const FString &InOldName){} + + // Callback hook from the Asset Registry when an asset is loaded + void HandleOnAssetLoaded(UObject *InAsset){} + + // Callback when the Asset Registry loads all its assets + void HandleOnAssetRegistryFilesLoaded(){} + +private: + static FComboActionSearchManager *Instance; + + // Maps the Dialogue path => SearchData. + TMap SearchMap; + + // Because we are unable to query for the module on another thread, cache it for use later + IAssetRegistry *AssetRegistry = nullptr; + + // Handlers + FDelegateHandle OnAssetAddedHandle; + FDelegateHandle OnAssetRemovedHandle; + FDelegateHandle OnAssetRenamedHandle; + FDelegateHandle OnFilesLoadedHandle; + FDelegateHandle OnAssetLoadedHandle; +}; diff --git a/Source/ComboInputEditor/Private/Search/ComboActionSearchResult.cpp b/Source/ComboInputEditor/Private/Search/ComboActionSearchResult.cpp new file mode 100644 index 0000000..8d40135 --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/ComboActionSearchResult.cpp @@ -0,0 +1,73 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#include "ComboActionSearchResult.h" + +#include "ComboActionGraph.h" +#include "ComboActionSearchUtils.h" +#include "Helpers/ComboActionGraphEditorUtilities.h" + +#define LOCTEXT_NAMESPACE "ComboActionSearchResult" + + +TSharedRef FComboActionSearchResult::CreateIcon() const +{ + const FLinearColor IconColor = FLinearColor::White; + const FSlateBrush *Brush = nullptr; + + return SNew(SImage) + .Image(Brush) + .ColorAndOpacity(IconColor) + .ToolTipText(GetCategory()); +} + +TWeakObjectPtr FComboActionSearchResult::GetParentDialogue() const +{ + if (this->Parent.IsValid()) + { + return this->Parent.Pin()->GetParentDialogue(); + } + return nullptr; +} + +#pragma region Search_Actions + +TWeakObjectPtr FComboActionSearchResult_ActionNode::GetParentDialogue() const +{ + if (this->ComboActionGraph.IsValid()) + { + return this->ComboActionGraph; + } + return FComboActionSearchResult::GetParentDialogue(); +} + +#pragma endregion + +#pragma region Search_Nodes + +FReply FComboActionSearchResult_GraphNode::OnClick(TWeakPtr ActionEditorPtr) +{ + if (this->GraphNode.IsValid()) + { + return FComboActionGraphEditorUtilities::OpenEditorAndJumpToGraphNode(ActionEditorPtr, this->GraphNode.Get()) ? FReply::Handled() : FReply::Unhandled(); + } + return FReply::Unhandled(); +} + +TSharedRef FComboActionSearchResult_GraphNode::CreateIcon() const +{ + if (this->GraphNode.IsValid()) + { + FLinearColor Color; + const FSlateIcon Icon = this->GraphNode.Get()->GetIconAndTint(Color); + return SNew(SImage) + .Image(Icon.GetOptionalIcon()) + .ColorAndOpacity(Color) + .ToolTipText(GetCategory()); + } + + return FComboActionSearchResult::CreateIcon(); +} + +#pragma endregion + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/ComboInputEditor/Private/Search/ComboActionSearchResult.h b/Source/ComboInputEditor/Private/Search/ComboActionSearchResult.h new file mode 100644 index 0000000..560afde --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/ComboActionSearchResult.h @@ -0,0 +1,211 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +#include "Ed/EdComboActionGraph.h" +#include "Ed/EdComboActionGraphNode.h" +#include "Widgets/Views/STreeView.h" + +#define LOCTEXT_NAMESPACE "ComboActionSearchResult" + + +/** + * Template Tree Item Node class. + */ +template +class FComboActionTreeItemNode : public TSharedFromThis +{ + +public: + FComboActionTreeItemNode(const FText &InDisplayText, const TSharedPtr &InParent) : Parent(InParent), DisplayText(InDisplayText){}; + virtual ~FComboActionTreeItemNode(){}; + +#pragma region Click_Functions + + virtual FReply OnClick(TWeakPtr DialogueEditorPtr) + { + // If there is a parent, handle it using the parent's functionality + if (Parent.IsValid()) + { + return Parent.Pin()->OnClick(DialogueEditorPtr); + } + + return FReply::Unhandled(); + } + +#pragma endregion + +#pragma region DisplayText_Functions + + FText GetDisplayText() const { return this->DisplayText; } + FName GetDisplayTextAsFName() const { return FName(*this->DisplayText.ToString()); } + void SetDisplayText(const FText &InText) { this->DisplayText = InText; } + bool DoesDisplayTextContains(const FString &InSearch, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase) const + { + return this->DisplayText.ToString().Contains(InSearch, SearchCase); + } + +#pragma endregion + +#pragma region Parent_Functions + + bool HasParent() const { return this->Parent.IsValid(); } + TWeakPtr GetParent() const { return this->Parent; } + void SetParent(TWeakPtr InParentNode) { this->Parent = InParentNode; } + void ClearParent() { this->Parent.Reset(); } + +#pragma endregion + +#pragma region Children_Functions + +public: + + bool HasChildren() const { return this->Children.Num() > 0; } + const TArray> &GetChildren() const { return this->Children; } + void GetVisibleChildren(TArray> &OutChildren) + { + for (const TSharedPtr &Child : this->Children) + { + if (Child->IsVisible()) + { + OutChildren.Add(Child); + } + } + } + virtual void AddChild(const TSharedPtr &ChildNode) + { + ensure(!ChildNode->IsRoot()); + ChildNode->SetParent(this->AsShared()); + this->Children.Add(ChildNode); + } + virtual void SetChildren(const TArray> &InChildren) + { + this->Children = InChildren; + for (const TSharedPtr &Child : this->Children) + { + ensure(!Child->IsRoot()); + Child->SetParent(this->AsShared()); + } + } + virtual void ClearChildren() + { + this->Children.Empty(); + } + + void ExpandAllChildren(const TSharedPtr>> &TreeView, bool bRecursive = true) + { + static constexpr bool bShouldExpandItem = true; + if (!HasChildren()) + { + return; + } + + TreeView->SetItemExpansion(this->AsShared(), bShouldExpandItem); + for (const TSharedPtr &ChildNode : this->Children) + { + if (bRecursive) + { + // recursive on all children. + ChildNode->ExpandAllChildren(TreeView, bRecursive); + } + else + { + // Only direct children + TreeView->SetItemExpansion(ChildNode, bShouldExpandItem); + } + } + } + +#pragma endregion + +#pragma region Helper_Functions + +public: + bool IsRoot() const { return !this->Parent.IsValid(); } + +#pragma endregion + +protected: + /** Any children listed under this node. */ + TArray> Children; + + /** The node that this is a direct child of (empty if this is a root node) */ + TWeakPtr Parent; + + /** The displayed text for this item. */ + FText DisplayText; + + /** Is this node displayed? */ + bool bIsVisible = true; +}; + + +class FComboActionSearchResult : public FComboActionTreeItemNode +{ +public: + FComboActionSearchResult(const FText &InDisplayText, const TSharedPtr &InParent) : FComboActionTreeItemNode(InDisplayText, InParent){} + +public: + // Create an icon to represent the result + virtual TSharedRef CreateIcon() const; + + // Gets the Dialogue housing all these search results. Aka the Dialogue this search result belongs to. + virtual TWeakObjectPtr GetParentDialogue() const; + + // Category: + FText GetCategory() const { return this->Category; } + void SetCategory(const FText &InCategory) { this->Category = InCategory; } + + // CommentString + FString GetCommentString() const { return this->CommentString; } + void SetCommentString(const FString &InCommentString) { this->CommentString = InCommentString; } + +protected: + // The category of this node. + FText Category; + + // Display text for comment information + FString CommentString; +}; + +// Root Node, should not be displayed. +class FComboActionSearchResult_RootNode : public FComboActionSearchResult +{ +public: + FComboActionSearchResult_RootNode() : FComboActionSearchResult(FText::FromString(TEXT("INVALID")), nullptr) { this->Category = LOCTEXT("ComboActionSearchResult_RootNodeCategory", "Root"); } +}; + +// Tree Node result that represents the Node +class FComboActionSearchResult_ActionNode : public FComboActionSearchResult +{ +public: + FComboActionSearchResult_ActionNode(const FText &InDisplayText, const TSharedPtr &InParent) : FComboActionSearchResult(InDisplayText, InParent) { this->Category = LOCTEXT("ComboAcitonSearchResult_ActionNodeCategory", "Action Node"); } + + virtual FReply OnClick(TWeakPtr DialogueEditorPtr) override { return FReply::Unhandled(); } + virtual TSharedRef CreateIcon() const override { return FComboActionSearchResult::CreateIcon(); } + virtual TWeakObjectPtr GetParentDialogue() const override; + + void SetActionGraph(TWeakObjectPtr InDialogueGraph) { this->ComboActionGraph = InDialogueGraph->GetComboActionGraph(); } + +protected: + TWeakObjectPtr ComboActionGraph; +}; + +// Tree Node result that represents the GraphNode +class FComboActionSearchResult_GraphNode : public FComboActionSearchResult +{ +public: + FComboActionSearchResult_GraphNode(const FText &InDisplayText, const TSharedPtr &InParent) : FComboActionSearchResult(InDisplayText, InParent){} + + virtual FReply OnClick(TWeakPtr ComboActionEditorPtr) override; + virtual TSharedRef CreateIcon() const override; + + void SetGraphNode(TWeakObjectPtr InGraphNode) { this->GraphNode = InGraphNode; } + +protected: + TWeakObjectPtr GraphNode; +}; + +#undef LOCTEXT_NAMESPACE diff --git a/Source/ComboInputEditor/Private/Search/ComboActionSearchUtils.cpp b/Source/ComboInputEditor/Private/Search/ComboActionSearchUtils.cpp new file mode 100644 index 0000000..707c15d --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/ComboActionSearchUtils.cpp @@ -0,0 +1,13 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#include "ComboActionSearchUtils.h" + + +TSharedPtr FComboActionSearchHelpers::InvokeTab(TSharedPtr TabManager, const FTabId &TabID) +{ + if (!TabManager.IsValid()) + { + return nullptr; + } + return TabManager->TryInvokeTab(TabID); +} \ No newline at end of file diff --git a/Source/ComboInputEditor/Private/Search/ComboActionSearchUtils.h b/Source/ComboInputEditor/Private/Search/ComboActionSearchUtils.h new file mode 100644 index 0000000..e3cafe8 --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/ComboActionSearchUtils.h @@ -0,0 +1,10 @@ +// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + +#pragma once + + +class COMBOINPUTEDITOR_API FComboActionSearchHelpers +{ +public: + static TSharedPtr InvokeTab(TSharedPtr TabManager, const FTabId &TabID); +}; \ No newline at end of file diff --git a/Source/ComboInputEditor/Private/Search/SComboActionSearch.cpp b/Source/ComboInputEditor/Private/Search/SComboActionSearch.cpp new file mode 100644 index 0000000..d912acc --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/SComboActionSearch.cpp @@ -0,0 +1,405 @@ +// Copyright Dominik Pavlicek 2023. All Rights Reserved. + +#include "SComboActionSearch.h" + +#include "Ed/AssetEditor_ComboActionGraph.h" +#include "Framework/Commands/GenericCommands.h" +#include "Search/ComboActionSearchManager.h" +#include "Widgets/Input/SSearchBox.h" + +#define LOCTEXT_NAMESPACE "SComboActionSearch" + + +void SComboActionSearch::Construct(const FArguments &InArgs, const TSharedPtr &InDialogueEditor) +{ + this->ComboActionEditorPtr = InDialogueEditor; + this->HostTab = InArgs._ContainingTab; + + if (this->HostTab.IsValid()) + { + this->HostTab.Pin()->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateSP(this, &SComboActionSearch::HandleHostTabClosed)); + } + + this->ChildSlot + [ + SAssignNew(this->MainVerticalBoxWidget, SVerticalBox) + + // Top bar, search + +SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SHorizontalBox) + + // Search field + +SHorizontalBox::Slot() + .FillWidth(1) + [ + SAssignNew(this->SearchTextBoxWidget, SSearchBox) + .HintText(LOCTEXT("ActionSearchHint", "Enter searched text...")) + .OnTextChanged(this, &SComboActionSearch::HandleSearchTextChanged) + .OnTextCommitted(this, &SComboActionSearch::HandleSearchTextCommitted) + .Visibility(EVisibility::Visible) + ] + + // Filter Options + +SHorizontalBox::Slot() + .AutoWidth() + .Padding(2.0f, 2.0f) + [ + SNew(SComboButton) + .ComboButtonStyle(FAppStyle::Get(), "GenericFilters.ComboButtonStyle") + .ForegroundColor(FLinearColor::White) + .ContentPadding(0) + .ToolTipText(LOCTEXT("Filters_Tooltip", "Filter options")) + .OnGetMenuContent(this, &SComboActionSearch::FillFilterEntries) + .HasDownArrow(true) + .ContentPadding(FMargin(1, 0)) + .ButtonContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .TextStyle(FAppStyle::Get(), "GenericFilters.TextStyle") + .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) + .Text(FText::FromString(FString(TEXT("\xf0b0"))) ) + ] + +SHorizontalBox::Slot() + .AutoWidth() + .Padding(2, 0, 0, 0) + [ + SNew(STextBlock) + .TextStyle(FAppStyle::Get(), "GenericFilters.TextStyle") + .Text(LOCTEXT("Filters", "Filters")) + ] + ] + ] + ] + + // Results tree + +SVerticalBox::Slot() + .FillHeight(1.0f) + .Padding(0.0f, 4.0f, 0.0f, 0.0f) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("Menu.Background")) + [ + SAssignNew(this->TreeView, STreeView>) + .ItemHeight(24) + .TreeItemsSource(&this->ItemsFound) + .OnGenerateRow(this, &SComboActionSearch::HandleGenerateRow) + .OnGetChildren(this, &SComboActionSearch::HandleGetChildren) + .OnMouseButtonDoubleClick(this, &SComboActionSearch::HandleTreeSelectionDoubleClicked) + .SelectionMode(ESelectionMode::Multi) + .OnContextMenuOpening(this, &SComboActionSearch::HandleContextMenuOpening) + ] + ] + ]; +} + +void SComboActionSearch::FocusForUse(const FComboActionSearchFilter &SearchFilter, bool bSelectFirstResult) +{ + FWidgetPath FilterTextBoxWidgetPath; + FSlateApplication::Get().GeneratePathToWidgetUnchecked(SearchTextBoxWidget.ToSharedRef(), FilterTextBoxWidgetPath); + + FSlateApplication::Get().SetKeyboardFocus(FilterTextBoxWidgetPath, EFocusCause::SetDirectly); + + if (!SearchFilter.SearchString.IsEmpty()) + { + SearchTextBoxWidget->SetText(FText::FromString(SearchFilter.SearchString)); + MakeSearchQuery(SearchFilter); + + if (bSelectFirstResult && this->ItemsFound.Num()) + { + auto ItemToFocusOn = this->ItemsFound[0]; + + while (ItemToFocusOn->HasChildren()) + { + ItemToFocusOn = ItemToFocusOn->GetChildren()[0]; + } + this->TreeView->SetSelection(ItemToFocusOn); + ItemToFocusOn->OnClick(this->ComboActionEditorPtr); + } + } +} + +void SComboActionSearch::MakeSearchQuery(const FComboActionSearchFilter &SearchFilter) +{ + this->SearchTextBoxWidget->SetText(FText::FromString(SearchFilter.SearchString)); + + if (this->ItemsFound.Num()) + { + this->TreeView->RequestScrollIntoView(this->ItemsFound[0]); + } + this->ItemsFound.Empty(); + + if (SearchFilter.SearchString.IsEmpty()) + { + return; + } + + this->HighlightText = FText::FromString(SearchFilter.SearchString); + this->RootSearchResult = MakeShared(); + + if (this->ComboActionEditorPtr.IsValid()) + { + FComboActionSearchManager::Get()->QuerySingleAction(SearchFilter, this->ComboActionEditorPtr.Pin()->GetEditingGraphSafe(), this->RootSearchResult); + + const TArray> &Children = this->RootSearchResult->GetChildren(); + if (Children.Num() == 1 && Children[0].IsValid()) + { + // we must ensure reference is created so its not garbage collected, usually resulting in crash! + TSharedPtr TempChild = Children[0]; + this->RootSearchResult = TempChild; + this->RootSearchResult->ClearParent(); + } + } + + this->ItemsFound = this->RootSearchResult->GetChildren(); + if (this->ItemsFound.Num() == 0) + { + this->ItemsFound.Add(MakeShared(LOCTEXT("ActionSearchNoResults", "No Results found"), this->RootSearchResult)); + this->HighlightText = FText::GetEmpty();\ + } + else + { + this->RootSearchResult->ExpandAllChildren(this->TreeView); + } + + this->TreeView->RequestTreeRefresh(); +} + +FName SComboActionSearch::GetHostTabId() const +{ + const TSharedPtr HostTabPtr = this->HostTab.Pin(); + if (HostTabPtr.IsValid()) + { + return HostTabPtr->GetLayoutIdentifier().TabType; + } + + return NAME_None; +} + +void SComboActionSearch::CloseHostTab() +{ + const TSharedPtr HostTabPtr = this->HostTab.Pin(); + if (HostTabPtr.IsValid()) + { + HostTabPtr->RequestCloseTab(); + } +} + +void SComboActionSearch::HandleHostTabClosed(TSharedRef DockTab) +{ + // Clear cache +} + +void SComboActionSearch::HandleSearchTextChanged(const FText& Text) +{ + this->CurrentFilter.SearchString = Text.ToString(); +} + +void SComboActionSearch::HandleSearchTextCommitted(const FText& Text, ETextCommit::Type CommitType) +{ + if (Text.IsEmpty()) + { + this->TreeView->RequestTreeRefresh(); + } + + if (CommitType == ETextCommit::OnEnter) + { + this->CurrentFilter.SearchString = Text.ToString(); + MakeSearchQuery(this->CurrentFilter); + } +} + +void SComboActionSearch::HandleGetChildren(TSharedPtr InItem, TArray> &OutChildren) +{ + OutChildren += InItem->GetChildren(); +} + +void SComboActionSearch::HandleTreeSelectionDoubleClicked(TSharedPtr Item) +{ + if (Item.IsValid()) + { + Item->OnClick(this->ComboActionEditorPtr); + } +} + +TSharedRef SComboActionSearch::HandleGenerateRow(TSharedPtr InItem, const TSharedRef &OwnerTable) +{ + // Normal entry + FText CommentText = FText::GetEmpty(); + if (!InItem->GetCommentString().IsEmpty()) + { + FFormatNamedArguments Args; + Args.Add(TEXT("Comment"), FText::FromString(InItem->GetCommentString())); + CommentText = FText::Format(LOCTEXT("NodeComment", "{Comment}"), Args); + } + + FFormatNamedArguments Args; + Args.Add(TEXT("Category"), InItem->GetCategory()); + Args.Add(TEXT("DisplayTitle"), InItem->GetDisplayText()); + const FText Tooltip = FText::Format(LOCTEXT("DialogueResultSearchToolTip", "{Category} : {DisplayTitle}"), Args); + + return SNew(STableRow>, OwnerTable) + [ + SNew(SHorizontalBox) + + // Icon + +SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + [ + InItem->CreateIcon() + ] + + // Display text + +SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(2,0) + [ + SNew(STextBlock) + .Text(InItem.Get(), &FComboActionSearchResult::GetDisplayText) + .HighlightText(HighlightText) + .ToolTipText(Tooltip) + ] + + // Comment Block + +SHorizontalBox::Slot() + .FillWidth(1) + .HAlign(HAlign_Right) + .VAlign(VAlign_Center) + .Padding(2,0) + [ + SNew(STextBlock) + .Text(CommentText) + .ColorAndOpacity(FLinearColor::Yellow) + ] + ]; +} + +TSharedPtr SComboActionSearch::HandleContextMenuOpening() +{ + const bool bShouldCloseWindowAfterMenuSelection = true; + FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, this->CommandList); + + MenuBuilder.BeginSection("BasicOperations"); + { + MenuBuilder.AddMenuEntry(FGenericCommands::Get().SelectAll); + MenuBuilder.AddMenuEntry(FGenericCommands::Get().Copy); + } + + return MenuBuilder.MakeWidget(); +} + +TSharedRef SComboActionSearch::FillFilterEntries() +{ + FMenuBuilder MenuBuilder(true, nullptr); + MenuBuilder.AddMenuEntry + ( + LOCTEXT("IncludeNodeTitle", "Include Node Title"), + LOCTEXT("IncludeNodeTitle_ToolTip", "Include Node Titles in the search result"), + FSlateIcon(), + FUIAction( + FExecuteAction::CreateLambda([this]() + { + this->CurrentFilter.bIncludeNodeTitle = !this->CurrentFilter.bIncludeNodeTitle; + MakeSearchQuery(this->CurrentFilter); + }), + FCanExecuteAction(), + FIsActionChecked::CreateLambda([this]() -> bool + { + return this->CurrentFilter.bIncludeNodeTitle; + }) + ), + NAME_None, + EUserInterfaceActionType::ToggleButton + ); + MenuBuilder.AddMenuEntry + ( + LOCTEXT("IncludeNodeType", "Include Node Type"), + LOCTEXT("IncludeNodeType_ToolTip", "Include Node Type in the search result"), + FSlateIcon(), + FUIAction( + FExecuteAction::CreateLambda([this]() + { + this->CurrentFilter.bIncludeNodeType = !this->CurrentFilter.bIncludeNodeType; + MakeSearchQuery(this->CurrentFilter); + }), + FCanExecuteAction(), + FIsActionChecked::CreateLambda([this]() -> bool + { + return this->CurrentFilter.bIncludeNodeType; + }) + ), + NAME_None, + EUserInterfaceActionType::ToggleButton + ); + MenuBuilder.AddMenuEntry + ( + LOCTEXT("IncludeNodeDecoratorsTypes", "Include Node Decorators"), + LOCTEXT("IncludeNodeDecoratorsTypes_ToolTip", "Include Node Decorators Types (by name) in the search result"), + FSlateIcon(), + FUIAction( + FExecuteAction::CreateLambda([this]() + { + this->CurrentFilter.bIncludeNodeDecoratorsTypes = !this->CurrentFilter.bIncludeNodeDecoratorsTypes; + MakeSearchQuery(this->CurrentFilter); + }), + FCanExecuteAction(), + FIsActionChecked::CreateLambda([this]() -> bool + { + return this->CurrentFilter.bIncludeNodeDecoratorsTypes; + }) + ), + NAME_None, + EUserInterfaceActionType::ToggleButton + ); + MenuBuilder.AddMenuEntry + ( + LOCTEXT("IncludeNodeData", "Include Node Data Row"), + LOCTEXT("IncludeNodeDecoratorsTypes_ToolTip", "Include Node Data Row in the search result"), + FSlateIcon(), + FUIAction( + FExecuteAction::CreateLambda([this]() + { + this->CurrentFilter.bIncludeNodeData = !this->CurrentFilter.bIncludeNodeData; + MakeSearchQuery(this->CurrentFilter); + }), + FCanExecuteAction(), + FIsActionChecked::CreateLambda([this]() -> bool + { + return this->CurrentFilter.bIncludeNodeData; + }) + ), + NAME_None, + EUserInterfaceActionType::ToggleButton + ); + MenuBuilder.AddMenuEntry + ( + LOCTEXT("IncludeNodeGUID", "Include Node GUID"), + LOCTEXT("IncludeNodeGUID_ToolTip", "Include Node GUID in the search result"), + FSlateIcon(), + FUIAction( + FExecuteAction::CreateLambda([this]() + { + this->CurrentFilter.bIncludeNodeGUID = !this->CurrentFilter.bIncludeNodeGUID; + MakeSearchQuery(this->CurrentFilter); + }), + FCanExecuteAction(), + FIsActionChecked::CreateLambda([this]() -> bool + { + return this->CurrentFilter.bIncludeNodeGUID; + }) + ), + NAME_None, + EUserInterfaceActionType::ToggleButton + ); + + return MenuBuilder.MakeWidget(); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/ComboInputEditor/Private/Search/SComboActionSearch.h b/Source/ComboInputEditor/Private/Search/SComboActionSearch.h new file mode 100644 index 0000000..58a096b --- /dev/null +++ b/Source/ComboInputEditor/Private/Search/SComboActionSearch.h @@ -0,0 +1,106 @@ +// Copyright Dominik Pavlicek 2023. All Rights Reserved. + +#pragma once + +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SCompoundWidget.h" +#include "Widgets/Views/STreeView.h" + +#include "Framework/Commands/UICommandList.h" + +#include "Search/ComboActionSearchFilter.h" +#include "Search/ComboActionSearchResult.h" + + +/** + * Widget handling Search in Combo Action Graph + */ +class SComboActionSearch : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SComboActionSearch) + : _bIsSearchWindow(true) + , _bHideSearchBar(false) + , _ContainingTab() + {} + SLATE_ARGUMENT(bool, bIsSearchWindow) + SLATE_ARGUMENT(bool, bHideSearchBar) + SLATE_ARGUMENT(TSharedPtr, ContainingTab) + SLATE_END_ARGS() + + void Construct(const FArguments &InArgs, const TSharedPtr &InDialogueEditor = nullptr); + + /** Focuses this widget's search box, and changes the mode as well, and optionally the search terms */ + void FocusForUse(const FComboActionSearchFilter &SearchFilter = FComboActionSearchFilter(), bool bSelectFirstResult = false); + + /** + * Submits a search query + * + * @param SearchFilter Filter for search + * @param bInIsFindWithinDialogue TRUE if searching within the current Dialogue only + */ + void MakeSearchQuery(const FComboActionSearchFilter &SearchFilter); + + /** If this is a global find results widget, returns the host tab's unique ID. Otherwise, returns NAME_None. */ + FName GetHostTabId() const; + + /** If this is a global find results widget, ask the host tab to close */ + void CloseHostTab(); + +private: + /** Called when the host tab is closed (if valid) */ + void HandleHostTabClosed(TSharedRef DockTab); + + /** Called when user changes the text they are searching for */ + void HandleSearchTextChanged(const FText &Text); + + /** Called when user changes commits text to the search box */ + void HandleSearchTextCommitted(const FText &Text, ETextCommit::Type CommitType); + + /* Get the children of a row */ + void HandleGetChildren(TSharedPtr InItem, TArray> &OutChildren); + + /* Called when user double clicks on a new result */ + void HandleTreeSelectionDoubleClicked(TSharedPtr Item); + + /* Called when a new row is being generated */ + TSharedRef HandleGenerateRow(TSharedPtr InItem, const TSharedRef &OwnerTable); + + /** Callback to build the context menu when right clicking in the tree */ + TSharedPtr HandleContextMenuOpening(); + + /** Fills in the filter menu. */ + TSharedRef FillFilterEntries(); + +private: + /** Pointer back to the ComboAction editor that owns us */ + TWeakPtr ComboActionEditorPtr; + + /* The tree view displays the results */ + TSharedPtr>> TreeView; + + /** The search text box */ + TSharedPtr SearchTextBoxWidget; + + /** Vertical box, used to add and remove widgets dynamically */ + TWeakPtr MainVerticalBoxWidget; + + /** In Find Within Action mode, we need to keep a handle on the root result, because it won't show up in the tree. */ + TSharedPtr RootSearchResult; + + /* This buffer stores the currently displayed results */ + TArray> ItemsFound; + + /* The string to highlight in the results */ + FText HighlightText; + + /** The current searach filter */ + FComboActionSearchFilter CurrentFilter; + + /** Tab hosting this widget. May be invalid. */ + TWeakPtr HostTab; + + /** Commands handled by this widget */ + TSharedPtr CommandList; + +}; diff --git a/Source/ComboInputEditor/Public/ComboInputEditor.h b/Source/ComboInputEditor/Public/ComboInputEditor.h index 795debe..80d0f41 100644 --- a/Source/ComboInputEditor/Public/ComboInputEditor.h +++ b/Source/ComboInputEditor/Public/ComboInputEditor.h @@ -90,7 +90,7 @@ public: static bool IsAvailable() { return FModuleManager::Get().IsModuleLoaded("ComboInputEditor"); } virtual void StartupModule() override; - virtual void ShutdownModule() override{} + virtual void ShutdownModule() override; TSharedPtr GetComboInputEditorStyleSet() const { return this->ComboInputEditorStyleSet; }