// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. #include "AssetEditor_ComboActionGraph.h" #include "ComboActionGraph.h" #include "ComboActionGraphSchema.h" #include "ComboInputEditor.h" #include "EdGraphUtilities.h" #include "GraphEditorActions.h" #include "Ed/EdComboActionGraph.h" #include "Ed/EdComboActionGraphEdge.h" #include "Ed/EdComboActionGraphNode.h" #include "Ed/FAssetEditorToolbarComboActionGraph.h" #include "Ed/FComboActionGraphEditorCommands.h" #include "Framework/Commands/GenericCommands.h" #include "HAL/PlatformApplicationMisc.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Layout/AssetEditorTabs.h" #include "Layout/ComboActionGraphLayoutStrategy.h" #include "Layout/ComboActionForceDirectedSolveLayoutStrategy.h" #include "Layout/ComboActionTreeSolveLayoutStrategy.h" #include "Nodes/ComboActionGraphNode.h" #include "Popups/ComboInputPopup_GraphValidation.h" #include "Search/ComboActionSearchUtils.h" #include "Search/SComboActionSearch.h" #include "Settings/ComboActionGraphEditorSettings.h" #include "UObject/ObjectSaveContext.h" #define LOCTEXT_NAMESPACE "AssetEditor_ComboActionGraph" DEFINE_LOG_CATEGORY(LogAssetEditorComboActionGraph); #pragma region ConstantNames const FName ComboActionGraphEditorAppName = FName(TEXT("ComboActionGraphEditorApp")); const FName FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID(TEXT("ComboActionGraphProperty")); const FName FAssetEditorTabs_ComboActionGraph::ViewportID(TEXT("Viewport")); const FName FAssetEditorTabs_ComboActionGraph::SearchToolbarID(TEXT("Search")); #pragma endregion FAssetEditor_ComboActionGraph::FAssetEditor_ComboActionGraph() { this->EditingGraph = nullptr; this->ComboActionGraphEditorSettings = GetMutableDefault(); this->OnPackageSavedDelegateHandle = UPackage::PackageSavedWithContextEvent.AddRaw(this, &FAssetEditor_ComboActionGraph::OnPackageSaved); } FAssetEditor_ComboActionGraph::~FAssetEditor_ComboActionGraph() { this->EditingGraph = nullptr; UPackage::PackageSavedWithContextEvent.Remove(this->OnPackageSavedDelegateHandle); this->ToolbarBuilder.Reset(); } void FAssetEditor_ComboActionGraph::OnPackageSaved(const FString &String, UPackage *Package, FObjectPostSaveContext ObjectPostSaveContext) { this->RebuildComboActionGraph(); } void FAssetEditor_ComboActionGraph::InitComboActionGraphAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UComboActionGraph *Graph) { this->EditingGraph = Graph; this->CreateEdGraph(); if (!this->ToolbarBuilder.IsValid()) { this->ToolbarBuilder = MakeShareable(new FAssetEditorToolbarComboActionGraph(SharedThis(this))); } this->BindCommands(); this->CreateInternalWidgets(); TSharedPtr ToolbarExtender = MakeShareable(new FExtender); this->ToolbarBuilder->AddComboActionGraphToolbar(ToolbarExtender); // Layout const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_ComboActionGraphEditor_LayoutV0.3") ->AddArea ( FTabManager::NewPrimaryArea()->SetOrientation(EOrientation::Orient_Vertical) ->Split ( FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Horizontal)->SetSizeCoefficient(0.9f) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(3.f) ->AddTab(FAssetEditorTabs_ComboActionGraph::ViewportID, ETabState::OpenedTab)->SetHideTabWell(true) ) ->Split ( FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Vertical) ->Split ( FTabManager::NewSplitter()->SetOrientation(EOrientation::Orient_Vertical) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.9f) ->AddTab(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID, ETabState::OpenedTab)->SetHideTabWell(true) ) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.3f) ->AddTab(FAssetEditorTabs_ComboActionGraph::SearchToolbarID, ETabState::OpenedTab) ) ) ) ) ); const bool bCreateDefaultStandaloneMenu = true; const bool bCreateDefaultToolbar = true; FAssetEditorToolkit::InitAssetEditor ( Mode, InitToolkitHost, ComboActionGraphEditorAppName, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, EditingGraph, false ); this->RegenerateMenusAndToolbars(); } UComboActionGraphEditorSettings *FAssetEditor_ComboActionGraph::GetSettings() const { return this->ComboActionGraphEditorSettings; } void FAssetEditor_ComboActionGraph::RegisterTabSpawners(const TSharedRef &InTabManager) { this->WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_ComboActionTreeEditor", "Combo Action Tree Editor")); auto WorkspaceMenuCategoryRef = this->WorkspaceMenuCategory.ToSharedRef(); FAssetEditorToolkit::RegisterTabSpawners(InTabManager); InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ViewportID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Viewport)) .SetDisplayName(LOCTEXT("GraphCanvasTab", "Viewport")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.EventGraph_16x")); InTabManager->RegisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID, FOnSpawnTab::CreateSP(this, &FAssetEditor_ComboActionGraph::SpawnTab_Details)) .SetDisplayName(LOCTEXT("DetailsTab", "Property")) .SetGroup(WorkspaceMenuCategoryRef) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); 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) { FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ViewportID); InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID); InTabManager->UnregisterTabSpawner(FAssetEditorTabs_ComboActionGraph::SearchToolbarID); } bool FAssetEditor_ComboActionGraph::CloseWindow(EAssetEditorCloseReason InCloseReason) { const bool bSatisfied = FAssetEditorToolkit::CloseWindow(InCloseReason); if (this->EditingGraph) { if (this->EditingGraph->EdGraph) { UEdComboActionGraph *EdGraph = Cast(this->EditingGraph->EdGraph); if (EdGraph->GetComboActionEditorPtr().HasSameObject(this)) EdGraph->ResetDialogueEditorPtr(); } } return bSatisfied; } FName FAssetEditor_ComboActionGraph::GetToolkitFName() const { return FName("FComboActionGraphEditor"); } FText FAssetEditor_ComboActionGraph::GetBaseToolkitName() const { return LOCTEXT("ComboActionGraphEditorAppLabel", "Combo Action Graph Editor"); } FText FAssetEditor_ComboActionGraph::GetToolkitName() const { const bool bDirtyState = this->EditingGraph->GetOutermost()->IsDirty(); FFormatNamedArguments Args; Args.Add(TEXT("ComboActionGraphName"), FText::FromString(this->EditingGraph->GetName())); Args.Add(TEXT("DirtyState"), bDirtyState ? FText::FromString(TEXT("*")) : FText::GetEmpty()); return FText::Format(LOCTEXT("ComboActionGraphEditorToolkitName", "{ComboActionGraphName}{DirtyState}"), Args); } FText FAssetEditor_ComboActionGraph::GetToolkitToolTipText() const { return FAssetEditorToolkit::GetToolTipTextForObject(EditingGraph); } FLinearColor FAssetEditor_ComboActionGraph::GetWorldCentricTabColorScale() const { return FLinearColor::Gray; } FString FAssetEditor_ComboActionGraph::GetWorldCentricTabPrefix() const { return TEXT("ComboActionGraphEditor"); } FString FAssetEditor_ComboActionGraph::GetDocumentationLink() const { return TEXT("There should be a link to the documentation here someday."); } void FAssetEditor_ComboActionGraph::SaveAsset_Execute() { if (this->EditingGraph != nullptr) { this->RebuildComboActionGraph(); } FAssetEditorToolkit::SaveAsset_Execute(); } void FAssetEditor_ComboActionGraph::UpdateToolbar() { // TODO } void FAssetEditor_ComboActionGraph::RegisterToolbarTab(const TSharedRef &InTabManager) { FAssetEditorToolkit::RegisterTabSpawners(InTabManager); } void FAssetEditor_ComboActionGraph::AddReferencedObjects(FReferenceCollector &Collector) { Collector.AddReferencedObject(this->EditingGraph); Collector.AddReferencedObject(this->EditingGraph->EdGraph); } FString FAssetEditor_ComboActionGraph::GetReferencerName() const { return TEXT("FAssetEditor_ComboActionGraph"); } void FAssetEditor_ComboActionGraph::SetDialogueBeingEdited(UComboActionGraph *NewDialogue) { if (NewDialogue == nullptr || NewDialogue == this->EditingGraph) { return; } UComboActionGraph *Previous = this->EditingGraph; this->EditingGraph = NewDialogue; this->RemoveEditingObject(Previous); this->AddEditingObject(NewDialogue); } void FAssetEditor_ComboActionGraph::CreateInternalWidgets() { this->ViewportWidget = this->CreateViewportWidget(); FDetailsViewArgs Args; Args.bUpdatesFromSelection = false; Args.bLockable = false; Args.bAllowSearch = true; Args.NameAreaSettings = FDetailsViewArgs::HideNameArea; Args.bHideSelectionTip = false; Args.bShowObjectLabel = false; FPropertyEditorModule &PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); this->PropertyWidget = PropertyModule.CreateDetailView(Args); this->PropertyWidget->SetObject(this->EditingGraph); this->PropertyWidget->OnFinishedChangingProperties().AddSP(this, &FAssetEditor_ComboActionGraph::OnFinishedChangingProperties); this->FindResultsView = SNew(SComboActionSearch, SharedThis(this)); } TSharedRef FAssetEditor_ComboActionGraph::CreateViewportWidget() { const FComboInputEditorModule &Module = FModuleManager::GetModuleChecked("ComboInputEditor"); FGraphAppearanceInfo AppearanceInfo; AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_ComboActionGraph", "Combo Action Tree"); AppearanceInfo.CornerImage = Module.Get().GetComboInputEditorStyleSet()->GetBrush(TEXT("MDSStyleSet.Graph.CornerImage")); AppearanceInfo.InstructionText = LOCTEXT("InstructionText_ComboActionGraph", "Place action nodes by right clicking."); this->CreateCommandList(); SGraphEditor::FGraphEditorEvents InEvents; InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FAssetEditor_ComboActionGraph::OnSelectedNodesChanged); InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FAssetEditor_ComboActionGraph::OnNodeDoubleClicked); return SNew(SGraphEditor) .AdditionalCommands(GraphEditorCommands) .IsEditable(true) .Appearance(AppearanceInfo) .GraphToEdit(EditingGraph->EdGraph) .GraphEvents(InEvents) .AutoExpandActionMenu(true) .ShowGraphStateOverlay(false); } void FAssetEditor_ComboActionGraph::BindCommands() { this->ToolkitCommands->MapAction ( FComboActionGraphEditorCommands::Get().AutoArrange, FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::AutoArrange), FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanAutoArrange) ); this->ToolkitCommands->MapAction ( FComboActionGraphEditorCommands::Get().ValidateGraph, FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::ValidateGraph), FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanValidateGraph) ); } void FAssetEditor_ComboActionGraph::CreateEdGraph() { if (this->EditingGraph->EdGraph == nullptr) { this->EditingGraph->EdGraph = CastChecked(FBlueprintEditorUtils::CreateNewGraph(EditingGraph, NAME_None, UEdComboActionGraph::StaticClass(), UComboActionGraphSchema::StaticClass())); this->EditingGraph->EdGraph->bAllowDeletion = false; // Give the schema a chance to fill out any required nodes (like the results node) const UEdGraphSchema *Schema = this->EditingGraph->EdGraph->GetSchema(); Schema->CreateDefaultNodesForGraph(*this->EditingGraph->EdGraph); UEdComboActionGraph *ComboActionGraph = Cast(this->EditingGraph->EdGraph); UEdComboActionGraphNode *NewNode = ComboActionGraph->CreateIntermediateNode(); NewNode->SetComboActionGraphNode(this->EditingGraph->StartNode); NewNode->CreateNewGuid(); NewNode->PostPlacedNewNode(); NewNode->AllocateDefaultPins(); NewNode->AutowireNewNode(nullptr); NewNode->NodePosX = 0; NewNode->NodePosY = 0; NewNode->ComboActionGraphNode->SetFlags(EObjectFlags::RF_Transactional); NewNode->SetFlags(EObjectFlags::RF_Transactional); ComboActionGraph->RebuildComboActionGraph(); } if (UEdComboActionGraph *EdComboActionGraph = Cast(this->EditingGraph->EdGraph)) { EdComboActionGraph->SetDialogueEditorPtr(SharedThis(this)); EdComboActionGraph->RebuildComboActionGraph(); } } void FAssetEditor_ComboActionGraph::CreateCommandList() { if (this->GraphEditorCommands.IsValid()) { return; } this->GraphEditorCommands = MakeShareable(new FUICommandList); this->GraphEditorCommands->MapAction(FComboActionGraphEditorCommands::Get().AutoArrange, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::AutoArrange), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanAutoArrange)); this->GraphEditorCommands->MapAction(FComboActionGraphEditorCommands::Get().ValidateGraph, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::ValidateGraph), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanValidateGraph)); this->GraphEditorCommands->MapAction(FGenericCommands::Get().SelectAll, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::SelectAllNodes), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanSelectAllNodes) ); this->GraphEditorCommands->MapAction(FGenericCommands::Get().Delete, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::DeleteSelectedNodes), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanDeleteNodes) ); this->GraphEditorCommands->MapAction(FGenericCommands::Get().Copy, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CopySelectedNodes), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanCopyNodes) ); this->GraphEditorCommands->MapAction(FGenericCommands::Get().Cut, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CutSelectedNodes), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanCutNodes) ); this->GraphEditorCommands->MapAction(FGenericCommands::Get().Paste, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::PasteNodes), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanPasteNodes) ); this->GraphEditorCommands->MapAction(FGenericCommands::Get().Duplicate, FExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::DuplicateNodes), FCanExecuteAction::CreateRaw(this, &FAssetEditor_ComboActionGraph::CanDuplicateNodes) ); this->GraphEditorCommands->MapAction(FGenericCommands::Get().Rename, FExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::OnRenameNode), FCanExecuteAction::CreateSP(this, &FAssetEditor_ComboActionGraph::CanRenameNodes) ); } TSharedPtr FAssetEditor_ComboActionGraph::GetCurrentGraphEditor() const { return this->ViewportWidget; } FGraphPanelSelectionSet FAssetEditor_ComboActionGraph::GetSelectedNodes() const { FGraphPanelSelectionSet CurrentSelection; TSharedPtr FocusedGraphEd = this->GetCurrentGraphEditor(); if (FocusedGraphEd.IsValid()) { CurrentSelection = FocusedGraphEd->GetSelectedNodes(); } return CurrentSelection; } void FAssetEditor_ComboActionGraph::RebuildComboActionGraph() { if (this->EditingGraph == nullptr) { UE_LOG(LogAssetEditorComboActionGraph, Warning, TEXT("[RebuildComboActionGraph] EditingGraph is nullptr")); return; } UEdComboActionGraph *EdGraph = Cast(this->EditingGraph->EdGraph); check(EdGraph != nullptr); EdGraph->RebuildComboActionGraph(); } void FAssetEditor_ComboActionGraph::SelectAllNodes() { TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (CurrentGraphEditor.IsValid()) { CurrentGraphEditor->SelectAllNodes(); } } void FAssetEditor_ComboActionGraph::DeleteSelectedNodes() { TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (!CurrentGraphEditor.IsValid()) { return; } const FScopedTransaction Transaction(FGenericCommands::Get().Delete->GetDescription()); CurrentGraphEditor->GetCurrentGraph()->Modify(); const FGraphPanelSelectionSet SelectedNodes = CurrentGraphEditor->GetSelectedNodes(); CurrentGraphEditor->ClearSelectionSet(); for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) { UEdGraphNode* EdNode = Cast(*NodeIt); if (EdNode == nullptr || !EdNode->CanUserDeleteNode()) continue;; if (UEdComboActionGraphNode *EdNode_Node = Cast(EdNode)) { EdNode_Node->Modify(); const UEdGraphSchema *Schema = EdNode_Node->GetSchema(); if (Schema != nullptr) { Schema->BreakNodeLinks(*EdNode_Node); } EdNode_Node->DestroyNode(); } else { EdNode->Modify(); EdNode->DestroyNode(); } } // Update UI CurrentGraphEditor->NotifyGraphChanged(); UEdGraph *EdGraph = CurrentGraphEditor->GetCurrentGraph(); UObject *GraphOwner = EdGraph->GetOuter(); if (GraphOwner) { GraphOwner->PostEditChange(); GraphOwner->MarkPackageDirty(); } this->RebuildComboActionGraph(); } bool FAssetEditor_ComboActionGraph::CanDeleteNodes() const { // If any of the nodes can be deleted then we should allow deleting const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { UEdGraphNode* Node = Cast(*SelectedIter); if (Node != nullptr && Node->CanUserDeleteNode()) { return true; } } return false; } void FAssetEditor_ComboActionGraph::DeleteSelectedDuplicatableNodes() { TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (!CurrentGraphEditor.IsValid()) { return; } const FGraphPanelSelectionSet OldSelectedNodes = CurrentGraphEditor->GetSelectedNodes(); CurrentGraphEditor->ClearSelectionSet(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter) { UEdGraphNode *Node = Cast(*SelectedIter); if (Node && Node->CanDuplicateNode()) { CurrentGraphEditor->SetNodeSelection(Node, true); } } // Delete the duplicatable nodes this->DeleteSelectedNodes(); CurrentGraphEditor->ClearSelectionSet(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(OldSelectedNodes); SelectedIter; ++SelectedIter) { if (UEdGraphNode* Node = Cast(*SelectedIter)) { CurrentGraphEditor->SetNodeSelection(Node, true); } } } void FAssetEditor_ComboActionGraph::CutSelectedNodes() { this->CopySelectedNodes(); this->DeleteSelectedDuplicatableNodes(); } void FAssetEditor_ComboActionGraph::CopySelectedNodes() { // Export the selected nodes and place the text on the clipboard FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes(); FString ExportedText; for (FGraphPanelSelectionSet::TIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { UEdGraphNode *Node = Cast(*SelectedIter); if (Node == nullptr) { SelectedIter.RemoveCurrent(); continue; } if (UEdComboActionGraphEdge *EdNode_Edge = Cast(*SelectedIter)) { UEdComboActionGraphNode *StartNode = EdNode_Edge->GetStartNode(); UEdComboActionGraphNode *EndNode = EdNode_Edge->GetEndNode(); if (!SelectedNodes.Contains(StartNode) || !SelectedNodes.Contains(EndNode)) { SelectedIter.RemoveCurrent(); continue; } } Node->PrepareForCopying(); } FEdGraphUtilities::ExportNodesToText(SelectedNodes, ExportedText); FPlatformApplicationMisc::ClipboardCopy(*ExportedText); } bool FAssetEditor_ComboActionGraph::CanCopyNodes() const { // If any of the nodes can be duplicated then we should allow copying const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { UEdGraphNode *Node = Cast(*SelectedIter); if (Node && Node->CanDuplicateNode() == false) { return false; } } return true; } void FAssetEditor_ComboActionGraph::PasteNodes() { TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (CurrentGraphEditor.IsValid()) { this->PasteNodesHere(CurrentGraphEditor->GetPasteLocation()); } } void FAssetEditor_ComboActionGraph::PasteNodesHere(const FVector2D &Location) { // Find the graph editor with focus TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (!CurrentGraphEditor.IsValid()) { return; } // Select the newly pasted stuff UEdGraph *EdGraph = CurrentGraphEditor->GetCurrentGraph(); { const FScopedTransaction Transaction(FGenericCommands::Get().Paste->GetDescription()); EdGraph->Modify(); // Clear the selection set (newly pasted stuff will be selected) CurrentGraphEditor->ClearSelectionSet(); // Grab the text to paste from the clipboard. FString TextToImport; FPlatformApplicationMisc::ClipboardPaste(TextToImport); // Import the nodes TSet PastedNodes; FEdGraphUtilities::ImportNodesFromText(EdGraph, TextToImport, PastedNodes); //Average position of nodes so we can move them while still maintaining relative distances to each other FVector2D AvgNodePosition(0.0f, 0.0f); for (TSet::TIterator It(PastedNodes); It; ++It) { UEdGraphNode* Node = *It; AvgNodePosition.X += Node->NodePosX; AvgNodePosition.Y += Node->NodePosY; } float InvNumNodes = 1.0f / float(PastedNodes.Num()); AvgNodePosition.X *= InvNumNodes; AvgNodePosition.Y *= InvNumNodes; // 0 is always Start Node! int32 SharedIndex = EditingGraph->GetAllNodes().Num(); for (TSet::TIterator It(PastedNodes); It; ++It) { UEdGraphNode* Node = *It; CurrentGraphEditor->SetNodeSelection(Node, true); Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X; Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y; Node->SnapToGrid(16); // Give new node a different Guid from the old one Node->CreateNewGuid(); if (UEdComboActionGraphNode *MounteaNode = Cast(Node)) { if (MounteaNode->ComboActionGraphNode) { MounteaNode->ComboActionGraphNode->OnPasted(); //MounteaNode->SetDialogueNodeIndex(SharedIndex); } SharedIndex++; } } } // Update UI CurrentGraphEditor->NotifyGraphChanged(); UObject *GraphOwner = EdGraph->GetOuter(); if (GraphOwner) { GraphOwner->PostEditChange(); GraphOwner->MarkPackageDirty(); } this->RebuildComboActionGraph(); } bool FAssetEditor_ComboActionGraph::CanPasteNodes() const { const FGraphPanelSelectionSet SelectedNodes = this->GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { const UEdComboActionGraphNode *Node = Cast(*SelectedIter); if (Node && Node->CanUserPasteNodes() == false) { return false; } } TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (!CurrentGraphEditor.IsValid()) { return false; } FString ClipboardContent; FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); return FEdGraphUtilities::CanImportNodesFromText(CurrentGraphEditor->GetCurrentGraph(), ClipboardContent); } void FAssetEditor_ComboActionGraph::AutoArrange() { // For now, auto-arrange is broken. Just in case it can be activated by accident, // show a popup saying as much if an auto-arrange is attempted. TArray ValidationMessages; ValidationMessages.Add(LOCTEXT("AssetEditor_ComboActionGraph_AutoArrangeDisabled", "Auto-arrange is currently broken.")); ComboInputPopup_GraphValidation::Open(ValidationMessages); //UEdComboActionGraph *EdGraph = Cast(this->EditingGraph->EdGraph); //check(EdGraph != nullptr); //const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorAutoArrange", "Combo Action Graph Editor: Auto Arrange all Nodes")); //EdGraph->Modify(true); //UComboActionGraphLayoutStrategy *LayoutStrategy = nullptr; //switch (this->ComboActionGraphEditorSettings->GetAutoLayoutStrategy()) //{ // case EComboActionAutoLayoutStrategyType::Tree: // LayoutStrategy = NewObject(EdGraph, UComboActionTreeSolveLayoutStrategy::StaticClass()); // break; // case EComboActionAutoLayoutStrategyType::ForceDirected: // LayoutStrategy = NewObject(EdGraph, UComboActionForceDirectedSolveLayoutStrategy::StaticClass()); // break; // default: // break; //} //if (LayoutStrategy != nullptr) //{ // LayoutStrategy->Layout(EdGraph); // LayoutStrategy->ConditionalBeginDestroy(); //} //else //{ // UE_LOG(LogAssetEditorComboActionGraph, Error, TEXT("[AutoArrange] LayoutStrategy is null.")); //} } bool FAssetEditor_ComboActionGraph::CanAutoArrange() const { return this->EditingGraph != nullptr && Cast(this->EditingGraph->EdGraph) != nullptr; } void FAssetEditor_ComboActionGraph::ValidateGraph() { if (this->ValidationWindow.IsValid()) { this->ValidationWindow->RequestDestroyWindow(); } UEdComboActionGraph *EdGraph = Cast(this->EditingGraph->EdGraph); check(EdGraph != nullptr); const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorValidateGraph", "Combo Action Graph Editor: Validate Graph.")); UComboActionGraph *ComboActionGraph = EdGraph->GetComboActionGraph(); check(ComboActionGraph != nullptr); this->RebuildComboActionGraph(); TArray ValidationMessages; if (ComboActionGraph->ValidateGraph(ValidationMessages, true) == false) { this->ValidationWindow = ComboInputPopup_GraphValidation::Open(ValidationMessages); } else { ValidationMessages.Empty(); this->ValidationWindow = ComboInputPopup_GraphValidation::Open(ValidationMessages); } } void FAssetEditor_ComboActionGraph::OnRenameNode() { TSharedPtr CurrentGraphEditor = this->GetCurrentGraphEditor(); if (CurrentGraphEditor.IsValid()) { const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) { UEdGraphNode *SelectedNode = Cast(*NodeIt); if (SelectedNode != nullptr && SelectedNode->bCanRenameNode) { CurrentGraphEditor->IsNodeTitleVisible(SelectedNode, true); break; } } } } bool FAssetEditor_ComboActionGraph::CanRenameNodes() const { check(this->GetSettings() != nullptr); return this->GetSettings()->AllowRenameNodes() == true && this->GetSelectedNodes().Num() == 1; } void FAssetEditor_ComboActionGraph::OnSelectedNodesChanged(const TSet &NewSelection) { TArray Selection; for (UObject* SelectionEntry : NewSelection) { Selection.Add(SelectionEntry); } if (Selection.Num() == 1) { // When just one node is selected, add it to PropertyWidget this->PropertyWidget->SetObjects(Selection); this->PropertyWidget->ShowAllAdvancedProperties(); //UComboActionEditorBFC::TriggerPreviewRefresh(Selection); } else { this->PropertyWidget->SetObject(this->EditingGraph); } this->RebuildComboActionGraph(); } void FAssetEditor_ComboActionGraph::OnNodeDoubleClicked(UEdGraphNode *Node) { this->GraphEditorCommands->TryExecuteAction(FGenericCommands::Get().Rename.ToSharedRef()); } void FAssetEditor_ComboActionGraph::OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent) { if (this->EditingGraph == nullptr) return; this->EditingGraph->EdGraph->GetSchema()->ForceVisualizationCacheClear(); this->RebuildComboActionGraph(); } TSharedRef FAssetEditor_ComboActionGraph::SpawnTab_Viewport(const FSpawnTabArgs &Args) { check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::ViewportID); TSharedRef SpawnedTab = SNew(SDockTab) .Label(LOCTEXT("ViewportTab_Title", "Viewport")); if (this->ViewportWidget.IsValid()) { SpawnedTab->SetContent(this->ViewportWidget.ToSharedRef()); } return SpawnedTab; } TSharedRef FAssetEditor_ComboActionGraph::SpawnTab_Details(const FSpawnTabArgs& Args) { check(Args.GetTabId() == FAssetEditorTabs_ComboActionGraph::ComboActionGraphPropertyID); auto DockTab = SNew(SDockTab) .Label(LOCTEXT("Details_Title", "Property")) [ 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