You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
884 lines
38 KiB
C++
884 lines
38 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
#include "precompiled.h"
|
|
|
|
#include <QLineEdit>
|
|
#include <QMenu>
|
|
#include <QSignalBlocker>
|
|
#include <QScrollBar>
|
|
#include <QBoxLayout>
|
|
#include <QPainter>
|
|
#include <QEvent>
|
|
#include <QCoreApplication>
|
|
#include <QCompleter>
|
|
#include <QHeaderView>
|
|
#include <QScopedValueRollback>
|
|
|
|
#include <AzCore/Asset/AssetManager.h>
|
|
#include <AzCore/Component/ComponentApplication.h>
|
|
#include <AzCore/Interface/Interface.h>
|
|
#include <AzCore/RTTI/BehaviorContext.h>
|
|
#include <AzCore/Serialization/EditContext.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/UserSettings/UserSettings.h>
|
|
#include <AzCore/std/containers/map.h>
|
|
#include <AzFramework/StringFunc/StringFunc.h>
|
|
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
|
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
|
|
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
|
#include <AzToolsFramework/AssetEditor/AssetEditorUtils.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
|
#include <Editor/Assets/ScriptCanvasAssetHelpers.h>
|
|
#include <Editor/Components/IconComponent.h>
|
|
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
|
|
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
|
|
#include <Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h>
|
|
#include <Editor/Include/ScriptCanvas/Bus/RequestBus.h>
|
|
#include <Editor/Nodes/NodeUtils.h>
|
|
#include <Editor/Settings.h>
|
|
#include <Editor/Translation/TranslationHelper.h>
|
|
#include <Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h>
|
|
#include <Editor/View/Widgets/NodePalette/FunctionNodePaletteTreeItemTypes.h>
|
|
#include <Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.h>
|
|
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
|
|
#include <Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.h>
|
|
#include <Editor/View/Widgets/NodePalette/SpecializedNodePaletteTreeItemTypes.h>
|
|
#include <Editor/View/Widgets/NodePalette/VariableNodePaletteTreeItemTypes.h>
|
|
#include <Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h>
|
|
#include <Editor/View/Widgets/ui_ScriptCanvasNodePaletteToolbar.h>
|
|
#include <GraphCanvas/Widgets/NodePalette/NodePaletteTreeView.h>
|
|
#include <GraphCanvas/Widgets/NodePalette/NodePaletteWidget.h>
|
|
#include <GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h>
|
|
#include <ScriptCanvas/Asset/RuntimeAsset.h>
|
|
#include <ScriptCanvas/Core/Attributes.h>
|
|
#include <ScriptCanvas/Core/SubgraphInterfaceUtility.h>
|
|
#include <ScriptCanvas/Data/DataRegistry.h>
|
|
#include <ScriptCanvas/Libraries/Core/GetVariable.h>
|
|
#include <ScriptCanvas/Libraries/Core/Method.h>
|
|
#include <ScriptCanvas/Libraries/Core/SetVariable.h>
|
|
#include <ScriptCanvas/Libraries/Libraries.h>
|
|
#include <ScriptCanvas/Utils/NodeUtils.h>
|
|
#include <ScriptEvents/ScriptEventsAsset.h>
|
|
|
|
namespace ScriptCanvasEditor
|
|
{
|
|
namespace Widget
|
|
{
|
|
//////////////////////
|
|
// NodePaletteWidget
|
|
//////////////////////
|
|
|
|
GraphCanvas::NodePaletteTreeItem* NodePaletteWidget::ExternalCreateNodePaletteRoot(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel)
|
|
{
|
|
ScriptCanvasRootPaletteTreeItem* root = aznew ScriptCanvasRootPaletteTreeItem(nodePaletteModel, assetModel);
|
|
|
|
{
|
|
GraphCanvas::NodePaletteTreeItem* variablesRoot = root->CreateChildNode<LocalVariablesListNodePaletteTreeItem>("Variables");
|
|
root->RegisterCategoryNode(variablesRoot, "Variables");
|
|
|
|
// We always want to keep these around as place holders
|
|
GraphCanvas::NodePaletteTreeItem* customEventRoot = root->GetCategoryNode("Script Events");
|
|
customEventRoot->SetAllowPruneOnEmpty(false);
|
|
|
|
GraphCanvas::NodePaletteTreeItem* globalFunctionRoot = root->GetCategoryNode("User Functions");
|
|
globalFunctionRoot->SetAllowPruneOnEmpty(false);
|
|
|
|
}
|
|
|
|
const NodePaletteModel::NodePaletteRegistry& nodeRegistry = nodePaletteModel.GetNodeRegistry();
|
|
|
|
for (const auto& registryPair : nodeRegistry)
|
|
{
|
|
const NodePaletteModelInformation* modelInformation = registryPair.second;
|
|
|
|
GraphCanvas::GraphCanvasTreeItem* parentItem = root->GetCategoryNode(modelInformation->m_categoryPath.c_str());
|
|
GraphCanvas::NodePaletteTreeItem* createdItem = nullptr;
|
|
|
|
if (auto customModelInformation = azrtti_cast<const CustomNodeModelInformation*>(modelInformation))
|
|
{
|
|
createdItem = parentItem->CreateChildNode<CustomNodePaletteTreeItem>(customModelInformation->m_typeId, customModelInformation->m_displayName);
|
|
createdItem->SetToolTip(QString(customModelInformation->m_toolTip.c_str()));
|
|
}
|
|
else if (auto methodNodeModelInformation = azrtti_cast<const MethodNodeModelInformation*>(modelInformation))
|
|
{
|
|
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::ClassMethodEventPaletteTreeItem>(methodNodeModelInformation->m_classMethod, methodNodeModelInformation->m_methodName, methodNodeModelInformation->m_isOverload, methodNodeModelInformation->m_propertyStatus);
|
|
}
|
|
else if (auto globalMethodNodeModelInformation = azrtti_cast<const GlobalMethodNodeModelInformation*>(modelInformation);
|
|
globalMethodNodeModelInformation != nullptr)
|
|
{
|
|
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::GlobalMethodEventPaletteTreeItem>(
|
|
*globalMethodNodeModelInformation);
|
|
}
|
|
else if (auto ebusHandlerNodeModelInformation = azrtti_cast<const EBusHandlerNodeModelInformation*>(modelInformation))
|
|
{
|
|
if (!azrtti_istypeof<const ScriptEventHandlerNodeModelInformation*>(ebusHandlerNodeModelInformation))
|
|
{
|
|
createdItem = parentItem->CreateChildNode<EBusHandleEventPaletteTreeItem>(ebusHandlerNodeModelInformation->m_busName, ebusHandlerNodeModelInformation->m_eventName, ebusHandlerNodeModelInformation->m_busId, ebusHandlerNodeModelInformation->m_eventId);
|
|
}
|
|
}
|
|
else if (auto ebusSenderNodeModelInformation = azrtti_cast<const EBusSenderNodeModelInformation*>(modelInformation))
|
|
{
|
|
if (!azrtti_istypeof<const ScriptEventSenderNodeModelInformation*>(ebusSenderNodeModelInformation))
|
|
{
|
|
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::EBusSendEventPaletteTreeItem>(ebusSenderNodeModelInformation->m_busName, ebusSenderNodeModelInformation->m_eventName, ebusSenderNodeModelInformation->m_busId, ebusSenderNodeModelInformation->m_eventId, ebusSenderNodeModelInformation->m_isOverload, ebusSenderNodeModelInformation->m_propertyStatus);
|
|
}
|
|
}
|
|
|
|
if (createdItem)
|
|
{
|
|
modelInformation->PopulateTreeItem((*createdItem));
|
|
}
|
|
}
|
|
|
|
root->PruneEmptyNodes();
|
|
|
|
return root;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// ScriptCanvasRootPaletteTreeItem
|
|
////////////////////////////////////
|
|
|
|
ScriptCanvasRootPaletteTreeItem::ScriptCanvasRootPaletteTreeItem(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel)
|
|
: GraphCanvas::NodePaletteTreeItem("root", ScriptCanvasEditor::AssetEditorId)
|
|
, m_nodePaletteModel(nodePaletteModel)
|
|
, m_assetModel(assetModel)
|
|
, m_categorizer(nodePaletteModel)
|
|
{
|
|
UpgradeNotifications::Bus::Handler::BusConnect();
|
|
|
|
if (m_assetModel)
|
|
{
|
|
TraverseTree();
|
|
|
|
ConnectLambdas();
|
|
|
|
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
|
}
|
|
}
|
|
|
|
ScriptCanvasRootPaletteTreeItem::~ScriptCanvasRootPaletteTreeItem()
|
|
{
|
|
DisconnectLambdas();
|
|
|
|
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
|
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
|
|
UpgradeNotifications::Bus::Handler::BusDisconnect();
|
|
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::ConnectLambdas()
|
|
{
|
|
{
|
|
auto connection = QObject::connect(m_assetModel, &QAbstractItemModel::rowsInserted, [this](const QModelIndex& parentIndex, int first, int last) { this->OnRowsInserted(parentIndex, first, last); });
|
|
m_lambdaConnections.emplace_back(connection);
|
|
}
|
|
|
|
{
|
|
auto connection = QObject::connect(m_assetModel, &QAbstractItemModel::rowsAboutToBeRemoved, [this](const QModelIndex& parentIndex, int first, int last) { this->OnRowsAboutToBeRemoved(parentIndex, first, last); });
|
|
m_lambdaConnections.emplace_back(connection);
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::DisconnectLambdas()
|
|
{
|
|
for (auto connection : m_lambdaConnections)
|
|
{
|
|
QObject::disconnect(connection);
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::RegisterCategoryNode(GraphCanvas::GraphCanvasTreeItem* treeItem, const char* subCategory, GraphCanvas::NodePaletteTreeItem* parentRoot)
|
|
{
|
|
if (parentRoot == nullptr)
|
|
{
|
|
parentRoot = this;
|
|
}
|
|
|
|
m_categorizer.RegisterCategoryNode(treeItem, subCategory, parentRoot);
|
|
}
|
|
|
|
// Given a category path (e.g. "My/Category") and a parent node, creates the necessary intermediate
|
|
// nodes under the given parent and returns the leaf tree item under the given category path.
|
|
GraphCanvas::NodePaletteTreeItem* ScriptCanvasRootPaletteTreeItem::GetCategoryNode(const char* categoryPath, GraphCanvas::NodePaletteTreeItem* parentRoot)
|
|
{
|
|
if (parentRoot)
|
|
{
|
|
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, parentRoot));
|
|
}
|
|
else
|
|
{
|
|
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, this));
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::PruneEmptyNodes()
|
|
{
|
|
m_categorizer.PruneEmptyNodes();
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::SetActiveScriptCanvasId(const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
|
|
{
|
|
ScriptCanvas::GraphRequestBus::EventResult(m_previousAssetId, scriptCanvasId, &ScriptCanvas::GraphRequests::GetAssetId);
|
|
|
|
for (auto functionTreePair : m_globalFunctionTreeItems)
|
|
{
|
|
functionTreePair.second->SetEnabled(true);
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnRowsInserted(const QModelIndex& parentIndex, int first, int last)
|
|
{
|
|
for (int i = first; i <= last; ++i)
|
|
{
|
|
QModelIndex modelIndex = m_assetModel->index(i, 0, parentIndex);
|
|
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
|
|
|
|
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
|
|
ProcessAsset(entry);
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnRowsAboutToBeRemoved(const QModelIndex& parentIndex, int first, int last)
|
|
{
|
|
for (int i = first; i <= last; ++i)
|
|
{
|
|
QModelIndex modelIndex = m_assetModel->index(first, 0, parentIndex);
|
|
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
|
|
|
|
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
|
|
|
|
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
|
|
{
|
|
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
|
|
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
|
|
|
|
auto scriptEventElementIter = m_scriptEventElementTreeItems.find(assetId);
|
|
if (scriptEventElementIter != m_scriptEventElementTreeItems.end())
|
|
{
|
|
scriptEventElementIter->second->DetachItem();
|
|
delete scriptEventElementIter->second;
|
|
m_scriptEventElementTreeItems.erase(scriptEventElementIter);
|
|
}
|
|
else
|
|
{
|
|
auto globalFunctionElementIter = m_globalFunctionTreeItems.find(assetId);
|
|
if (globalFunctionElementIter != m_globalFunctionTreeItems.end())
|
|
{
|
|
globalFunctionElementIter->second->SetError("Graph has errors or has been deleted");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PruneEmptyNodes();
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::TraverseTree(QModelIndex index)
|
|
{
|
|
QModelIndex sourceIndex = m_assetModel->mapToSource(index);
|
|
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
|
|
|
|
ProcessAsset(entry);
|
|
|
|
int rowCount = m_assetModel->rowCount(index);
|
|
|
|
for (int i = 0; i < rowCount; ++i)
|
|
{
|
|
QModelIndex nextIndex = m_assetModel->index(i, 0, index);
|
|
TraverseTree(nextIndex);
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::ProcessAsset(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
|
{
|
|
if (entry)
|
|
{
|
|
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
|
|
{
|
|
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
|
|
|
|
const auto entryType = productEntry->GetAssetType();
|
|
|
|
if (entryType == azrtti_typeid<ScriptCanvas::SubgraphInterfaceAsset>())
|
|
{
|
|
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
|
|
|
|
auto elementIter = m_globalFunctionTreeItems.find(assetId);
|
|
if (elementIter == m_globalFunctionTreeItems.end())
|
|
{
|
|
RequestAssetLoad(assetId, productEntry->GetAssetType());
|
|
}
|
|
else
|
|
{
|
|
if (elementIter->second->HasError())
|
|
{
|
|
m_pendingAssets.erase(assetId);
|
|
RequestAssetLoad(assetId, productEntry->GetAssetType());
|
|
}
|
|
}
|
|
}
|
|
else if (entryType == azrtti_typeid<ScriptEvents::ScriptEventsAsset>())
|
|
{
|
|
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
|
|
|
|
auto elementIter = m_scriptEventElementTreeItems.find(assetId.m_guid);
|
|
if (elementIter == m_scriptEventElementTreeItems.end())
|
|
{
|
|
RequestAssetLoad(assetId, productEntry->GetAssetType());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnSystemTick()
|
|
{
|
|
AZ::SystemTickBus::Handler::BusDisconnect();
|
|
|
|
while (!m_requestQueue.empty())
|
|
{
|
|
const auto entry = m_requestQueue.front();
|
|
m_requestQueue.pop();
|
|
|
|
if (m_pendingAssets.find(entry.first) == m_pendingAssets.end())
|
|
{
|
|
AZ::Data::AssetBus::MultiHandler::BusConnect(entry.first);
|
|
m_pendingAssets[entry.first] = AZ::Data::AssetManager::Instance().GetAsset(entry.first, entry.second, AZ::Data::AssetLoadBehavior::Default);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
|
|
{
|
|
auto scriptEventIter = m_scriptEventElementTreeItems.find(assetId.m_guid);
|
|
if (scriptEventIter != m_scriptEventElementTreeItems.end())
|
|
{
|
|
RequestAssetLoad(assetId, azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
|
|
}
|
|
else
|
|
{
|
|
auto functionIter = m_globalFunctionTreeItems.find(assetId);
|
|
if (functionIter != m_globalFunctionTreeItems.end())
|
|
{
|
|
RequestAssetLoad(assetId, azrtti_typeid<ScriptCanvas::SubgraphInterfaceAsset>());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnCatalogAssetAdded(const AZ::Data::AssetId&)
|
|
{
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnCatalogAssetRemoved(const AZ::Data::AssetId&, const AZ::Data::AssetInfo&)
|
|
{
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnUpgradeStart()
|
|
{
|
|
DisconnectLambdas();
|
|
|
|
// Disconnect from the AssetCatalogEventBus during the upgrade to avoid overlap
|
|
// in asset processing
|
|
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnUpgradeComplete()
|
|
{
|
|
ConnectLambdas();
|
|
|
|
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
|
|
|
TraverseTree();
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnUpgradeCancelled()
|
|
{
|
|
if (!AzFramework::AssetCatalogEventBus::Handler::BusIsConnected())
|
|
{
|
|
ConnectLambdas();
|
|
|
|
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
|
|
|
TraverseTree();
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::RequestAssetLoad(AZ::Data::AssetId assetId, AZ::Data::AssetType assetType)
|
|
{
|
|
// Delay handling loads until the top of the next tick in case we are handling this on an asset callback thread to avoid potential deadlocks.
|
|
AZ::SystemTickBus::Handler::BusConnect();
|
|
m_requestQueue.emplace(AZStd::make_pair(assetId, assetType));
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
|
{
|
|
// mark the function on error... if possible
|
|
AZ::Data::AssetId assetId = asset.GetId();
|
|
m_pendingAssets.erase(assetId);
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
|
{
|
|
AZ::Data::AssetId assetId = asset.GetId();
|
|
m_pendingAssets.erase(assetId);
|
|
|
|
if (asset.GetType() == azrtti_typeid<ScriptEvents::ScriptEventsAsset>())
|
|
{
|
|
if (m_scriptEventElementTreeItems.find(assetId) == m_scriptEventElementTreeItems.end())
|
|
{
|
|
ScriptEvents::ScriptEventsAsset* data = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
|
|
|
|
if (data)
|
|
{
|
|
GraphCanvas::NodePaletteTreeItem* categoryRoot = GetCategoryNode(data->m_definition.GetCategory().c_str());
|
|
ScriptEventsPaletteTreeItem* treeItem = categoryRoot->CreateChildNode<ScriptEventsPaletteTreeItem>(asset);
|
|
|
|
if (treeItem)
|
|
{
|
|
m_scriptEventElementTreeItems[assetId] = treeItem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (asset.GetType() == azrtti_typeid<ScriptCanvas::SubgraphInterfaceAsset>())
|
|
{
|
|
// We only need to add
|
|
auto treePaletteIter = m_globalFunctionTreeItems.find(asset->GetId());
|
|
|
|
if (treePaletteIter == m_globalFunctionTreeItems.end())
|
|
{
|
|
ScriptCanvas::SubgraphInterfaceAsset* data = asset.GetAs<ScriptCanvas::SubgraphInterfaceAsset>();
|
|
if (!data)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!data->m_runtimeData.m_interface.HasAnyFunctionality())
|
|
{
|
|
// check for deleting the old entry
|
|
return;
|
|
}
|
|
|
|
AZStd::string rootPath, absolutePath;
|
|
AZ::Data::AssetInfo assetInfo = AssetHelpers::GetAssetInfo(assetId, rootPath);
|
|
AzFramework::StringFunc::Path::Join(rootPath.c_str(), assetInfo.m_relativePath.c_str(), absolutePath);
|
|
|
|
AZStd::string normPath = absolutePath;
|
|
AzFramework::StringFunc::Path::Normalize(normPath);
|
|
|
|
AZStd::string watchFolder;
|
|
bool sourceInfoFound{};
|
|
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, normPath.c_str(), assetInfo, watchFolder);
|
|
|
|
if (!sourceInfoFound)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CreateFunctionPaletteItem(asset, assetInfo);
|
|
|
|
treePaletteIter = m_globalFunctionTreeItems.find(asset->GetId());
|
|
|
|
if (treePaletteIter != m_globalFunctionTreeItems.end())
|
|
{
|
|
treePaletteIter->second->ClearError();
|
|
}
|
|
|
|
m_monitoredAssets.emplace(asset->GetId(), asset);
|
|
}
|
|
else
|
|
{
|
|
RequestBuildChildrenFromSubgraphInterface(treePaletteIter->second, asset);
|
|
treePaletteIter->second->ClearError();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
|
{
|
|
OnAssetReady(asset);
|
|
}
|
|
|
|
bool ScriptCanvasRootPaletteTreeItem::HasAssetTreeItem(AZ::Data::AssetId assetId) const
|
|
{
|
|
return m_scriptEventElementTreeItems.find(assetId) != m_scriptEventElementTreeItems.end()
|
|
|| m_globalFunctionTreeItems.find(assetId) != m_globalFunctionTreeItems.end();
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::CreateFunctionPaletteItem(AZ::Data::Asset<AZ::Data::AssetData> asset, const AZ::Data::AssetInfo& assetInfo)
|
|
{
|
|
ScriptCanvas::SubgraphInterfaceAsset* data = asset.GetAs<ScriptCanvas::SubgraphInterfaceAsset>();
|
|
if (!data)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const ScriptCanvas::Grammar::SubgraphInterface& graphInterface = data->m_runtimeData.m_interface;
|
|
if (!graphInterface.HasAnyFunctionality())
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZStd::string name;
|
|
AzFramework::StringFunc::Path::GetFileName(assetInfo.m_relativePath.c_str(), name);
|
|
|
|
AZStd::string category = "User Functions";
|
|
|
|
AZStd::string relativePath;
|
|
if (AzFramework::StringFunc::Path::GetFolderPath(assetInfo.m_relativePath.c_str(), relativePath))
|
|
{
|
|
AZStd::to_lower(relativePath.begin(), relativePath.end());
|
|
|
|
auto stripPathStart = [&](const AZStd::string root)
|
|
{
|
|
if (relativePath.starts_with(root))
|
|
{
|
|
relativePath = relativePath.substr(root.size(), relativePath.size() - root.size());
|
|
}
|
|
};
|
|
|
|
stripPathStart("scriptcanvas/functions");
|
|
stripPathStart("scriptcanvas");
|
|
stripPathStart("/");
|
|
|
|
category.append("/");
|
|
category.append(relativePath);
|
|
}
|
|
|
|
NodePaletteTreeItem* categoryRoot = GetCategoryNode(category.c_str());
|
|
auto functionCategory = categoryRoot->CreateChildNode<NodePaletteTreeItem>(name.c_str(), ScriptCanvasEditor::AssetEditorId);
|
|
RequestBuildChildrenFromSubgraphInterface(functionCategory, asset);
|
|
m_globalFunctionTreeItems[asset->GetId()] = functionCategory;
|
|
categoryRoot->SetEnabled(true);
|
|
}
|
|
|
|
void ScriptCanvasRootPaletteTreeItem::RequestBuildChildrenFromSubgraphInterface(NodePaletteTreeItem* functionCategory, AZ::Data::Asset<AZ::Data::AssetData> asset)
|
|
{
|
|
functionCategory->ClearChildren();
|
|
|
|
ScriptCanvas::SubgraphInterfaceAsset* data = asset.GetAs<ScriptCanvas::SubgraphInterfaceAsset>();
|
|
if (!data)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const ScriptCanvas::Grammar::SubgraphInterface& graphInterface = data->m_runtimeData.m_interface;
|
|
if (!graphInterface.HasAnyFunctionality())
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto parent = functionCategory;
|
|
parent->SetEnabled(true);
|
|
|
|
if (graphInterface.IsUserNodeable())
|
|
{
|
|
auto name = functionCategory->GetName().toUtf8().constData();
|
|
parent = parent->CreateChildNode<FunctionPaletteTreeItem>(AZStd::string::format("%s Node", name).c_str(), ScriptCanvas::Grammar::MakeFunctionSourceIdNodeable(), asset);
|
|
parent->SetEnabled(true);
|
|
}
|
|
|
|
if (graphInterface.IsMarkedPure())
|
|
{
|
|
for (auto& in : graphInterface.GetIns())
|
|
{
|
|
auto childNode = parent->CreateChildNode<FunctionPaletteTreeItem>(in.displayName.c_str(), in.sourceID, asset);
|
|
childNode->SetEnabled(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto& ins = graphInterface.GetIns();
|
|
AZStd::vector<const ScriptCanvas::Grammar::In*> onNodeIns;
|
|
AZStd::vector<const ScriptCanvas::Grammar::In*> pureIns;
|
|
|
|
auto pureParent = functionCategory;
|
|
|
|
for (auto& in : ins)
|
|
{
|
|
if (in.isPure)
|
|
{
|
|
pureIns.push_back(&in);
|
|
}
|
|
else
|
|
{
|
|
onNodeIns.push_back(&in);
|
|
}
|
|
}
|
|
|
|
if (!onNodeIns.empty())
|
|
{
|
|
parent->SetEnabled(true);
|
|
|
|
for (auto in : onNodeIns)
|
|
{
|
|
auto childNode = parent->CreateChildNode<NodePaletteTreeItem>(in->displayName.c_str(), ScriptCanvasEditor::AssetEditorId);
|
|
childNode->SetEnabled(true);
|
|
}
|
|
}
|
|
|
|
if (!pureIns.empty())
|
|
{
|
|
parent = pureParent->CreateChildNode<NodePaletteTreeItem>("Pure Functions", ScriptCanvasEditor::AssetEditorId);
|
|
parent->SetEnabled(true);
|
|
|
|
for (auto in : pureIns)
|
|
{
|
|
auto childNode = parent->CreateChildNode<FunctionPaletteTreeItem>(in->displayName.c_str(), in->sourceID, asset);
|
|
childNode->SetEnabled(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////
|
|
// ScriptCanvasNodePaletteConfig
|
|
//////////////////////////////////
|
|
|
|
ScriptCanvasNodePaletteConfig::ScriptCanvasNodePaletteConfig(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel, bool isInContextMenu)
|
|
: m_nodePaletteModel(nodePaletteModel)
|
|
, m_assetModel(assetModel)
|
|
{
|
|
m_editorId = ScriptCanvasEditor::AssetEditorId;
|
|
m_mimeType = NodePaletteDockWidget::GetMimeType();
|
|
m_isInContextMenu = isInContextMenu;
|
|
m_allowArrowKeyNavigation = isInContextMenu;
|
|
m_saveIdentifier = m_isInContextMenu ? "ScriptCanvas" : "ScriptCnavas_ContextMenu";
|
|
|
|
m_rootTreeItem = Widget::NodePaletteWidget::ExternalCreateNodePaletteRoot(nodePaletteModel, assetModel);
|
|
}
|
|
|
|
ScriptCanvasNodePaletteConfig::~ScriptCanvasNodePaletteConfig()
|
|
{
|
|
|
|
}
|
|
|
|
//////////////////////////
|
|
// NodePaletteDockWidget
|
|
//////////////////////////
|
|
|
|
NodePaletteDockWidget::NodePaletteDockWidget(const QString& windowLabel, QWidget* parent, const ScriptCanvasNodePaletteConfig& paletteConfig)
|
|
: GraphCanvas::NodePaletteDockWidget(parent, windowLabel, paletteConfig)
|
|
, m_assetModel(paletteConfig.m_assetModel)
|
|
, m_nodePaletteModel(paletteConfig.m_nodePaletteModel)
|
|
, m_nextCycleAction(nullptr)
|
|
, m_previousCycleAction(nullptr)
|
|
, m_ignoreSelectionChanged(false)
|
|
{
|
|
if (!paletteConfig.m_isInContextMenu)
|
|
{
|
|
QMenu* creationMenu = new QMenu();
|
|
|
|
auto scriptEventAction = creationMenu->addAction("New Script Event");
|
|
QObject::connect(scriptEventAction, &QAction::triggered, this, &NodePaletteDockWidget::OnNewCustomEvent);
|
|
|
|
m_newCustomEvent = new QToolButton(this);
|
|
m_newCustomEvent->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/add.png"));
|
|
m_newCustomEvent->setToolTip("Click to create a new Script Event or Function");
|
|
m_newCustomEvent->setPopupMode(QToolButton::ToolButtonPopupMode::InstantPopup);
|
|
m_newCustomEvent->setMenu(creationMenu);
|
|
|
|
//
|
|
|
|
AddSearchCustomizationWidget(m_newCustomEvent);
|
|
|
|
GraphCanvas::NodePaletteTreeView* treeView = GetTreeView();
|
|
|
|
{
|
|
m_nextCycleAction = new QAction(treeView);
|
|
|
|
m_nextCycleAction->setShortcut(QKeySequence(Qt::Key_F8));
|
|
treeView->addAction(m_nextCycleAction);
|
|
|
|
QObject::connect(m_nextCycleAction, &QAction::triggered, this, &NodePaletteDockWidget::CycleToNextNode);
|
|
}
|
|
|
|
{
|
|
m_previousCycleAction = new QAction(treeView);
|
|
|
|
m_previousCycleAction->setShortcut(QKeySequence(Qt::Key_F7));
|
|
treeView->addAction(m_previousCycleAction);
|
|
|
|
QObject::connect(m_previousCycleAction, &QAction::triggered, this, &NodePaletteDockWidget::CycleToPreviousNode);
|
|
}
|
|
|
|
QObject::connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &NodePaletteDockWidget::OnTreeSelectionChanged);
|
|
QObject::connect(treeView, &GraphCanvas::NodePaletteTreeView::OnTreeItemDoubleClicked, this, &NodePaletteDockWidget::HandleTreeItemDoubleClicked);
|
|
}
|
|
|
|
ConfigureSearchCustomizationMargins(QMargins(0, 0, 0, 0), 0);
|
|
|
|
GraphCanvas::AssetEditorNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
|
|
}
|
|
|
|
NodePaletteDockWidget::~NodePaletteDockWidget()
|
|
{
|
|
GraphCanvas::AssetEditorNotificationBus::Handler::BusDisconnect();
|
|
GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void NodePaletteDockWidget::OnNewCustomEvent()
|
|
{
|
|
AzToolsFramework::AssetEditor::AssetEditorRequestsBus::Broadcast(&AzToolsFramework::AssetEditor::AssetEditorRequests::CreateNewAsset, azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
|
|
}
|
|
|
|
void NodePaletteDockWidget::OnActiveGraphChanged(const GraphCanvas::GraphId& graphCanvasGraphId)
|
|
{
|
|
GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
|
|
GraphCanvas::SceneNotificationBus::Handler::BusConnect(graphCanvasGraphId);
|
|
|
|
ScriptCanvas::ScriptCanvasId scriptCanvasId;
|
|
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
|
|
static_cast<ScriptCanvasRootPaletteTreeItem*>(ModTreeRoot())->SetActiveScriptCanvasId(scriptCanvasId);
|
|
}
|
|
|
|
void NodePaletteDockWidget::OnSelectionChanged()
|
|
{
|
|
if (m_ignoreSelectionChanged)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_cyclingHelper.Clear();
|
|
|
|
GetTreeView()->selectionModel()->clearSelection();
|
|
}
|
|
|
|
GraphCanvas::GraphCanvasTreeItem* NodePaletteDockWidget::CreatePaletteRoot() const
|
|
{
|
|
return NodePaletteWidget::ExternalCreateNodePaletteRoot(m_nodePaletteModel, m_assetModel);
|
|
}
|
|
|
|
void NodePaletteDockWidget::OnTreeSelectionChanged(const QItemSelection&, const QItemSelection&)
|
|
{
|
|
ClearCycleTarget();
|
|
|
|
AZStd::unordered_set< ScriptCanvas::VariableId > variableSet;
|
|
|
|
QModelIndexList indexList = GetTreeView()->selectionModel()->selectedRows();
|
|
|
|
if (indexList.size() == 1)
|
|
{
|
|
QSortFilterProxyModel* filterModel = static_cast<QSortFilterProxyModel*>(GetTreeView()->model());
|
|
|
|
for (const QModelIndex& index : indexList)
|
|
{
|
|
QModelIndex sourceIndex = filterModel->mapToSource(index);
|
|
|
|
GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast<GraphCanvas::NodePaletteTreeItem*>(sourceIndex.internalPointer());
|
|
|
|
ParseCycleTargets(nodePaletteItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NodePaletteDockWidget::AddCycleTarget(ScriptCanvas::NodeTypeIdentifier cyclingIdentifier)
|
|
{
|
|
if (cyclingIdentifier == ScriptCanvas::NodeTypeIdentifier(0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_cyclingIdentifiers.insert(cyclingIdentifier);
|
|
m_cyclingHelper.Clear();
|
|
|
|
if (m_nextCycleAction)
|
|
{
|
|
m_nextCycleAction->setEnabled(true);
|
|
m_previousCycleAction->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
void NodePaletteDockWidget::ClearCycleTarget()
|
|
{
|
|
m_cyclingIdentifiers.clear();
|
|
m_cyclingHelper.Clear();
|
|
|
|
if (m_nextCycleAction)
|
|
{
|
|
m_nextCycleAction->setEnabled(false);
|
|
m_previousCycleAction->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void NodePaletteDockWidget::CycleToNextNode()
|
|
{
|
|
ConfigureHelper();
|
|
|
|
m_cyclingHelper.CycleToNextNode();
|
|
}
|
|
|
|
void NodePaletteDockWidget::CycleToPreviousNode()
|
|
{
|
|
ConfigureHelper();
|
|
|
|
m_cyclingHelper.CycleToPreviousNode();
|
|
}
|
|
|
|
void NodePaletteDockWidget::HandleTreeItemDoubleClicked(GraphCanvas::GraphCanvasTreeItem* treeItem)
|
|
{
|
|
ParseCycleTargets(treeItem);
|
|
CycleToNextNode();
|
|
}
|
|
|
|
void NodePaletteDockWidget::ConfigureHelper()
|
|
{
|
|
if (!m_cyclingHelper.IsConfigured() && !m_cyclingIdentifiers.empty())
|
|
{
|
|
ScriptCanvas::ScriptCanvasId scriptCanvasId;
|
|
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetActiveScriptCanvasId);
|
|
|
|
AZ::EntityId graphCanvasGraphId;
|
|
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::GetActiveGraphCanvasGraphId);
|
|
|
|
m_cyclingHelper.SetActiveGraph(graphCanvasGraphId);
|
|
|
|
AZStd::vector<GraphCanvas::NodeId> cyclingNodes;
|
|
AZStd::vector<NodeIdPair> completeNodePairs;
|
|
|
|
for (ScriptCanvas::NodeTypeIdentifier nodeTypeIdentifier : m_cyclingIdentifiers)
|
|
{
|
|
AZStd::vector<NodeIdPair> nodePairs;
|
|
EditorGraphRequestBus::EventResult(nodePairs, scriptCanvasId, &EditorGraphRequests::GetNodesOfType, nodeTypeIdentifier);
|
|
|
|
cyclingNodes.reserve(cyclingNodes.size() + nodePairs.size());
|
|
completeNodePairs.reserve(completeNodePairs.size() + nodePairs.size());
|
|
|
|
for (const auto& nodeIdPair : nodePairs)
|
|
{
|
|
cyclingNodes.emplace_back(nodeIdPair.m_graphCanvasId);
|
|
completeNodePairs.emplace_back(nodeIdPair);
|
|
}
|
|
}
|
|
|
|
m_cyclingHelper.SetNodes(cyclingNodes);
|
|
|
|
{
|
|
// Clean-up Selection to maintain the 'single' selection state throughout the editor
|
|
QScopedValueRollback<bool> ignoreSelection(m_ignoreSelectionChanged, true);
|
|
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection);
|
|
}
|
|
|
|
EditorGraphRequestBus::Event(scriptCanvasId, &EditorGraphRequests::HighlightNodes, completeNodePairs);
|
|
}
|
|
}
|
|
|
|
void NodePaletteDockWidget::ParseCycleTargets(GraphCanvas::GraphCanvasTreeItem* treeItem)
|
|
{
|
|
AZStd::vector< ScriptCanvas::NodeTypeIdentifier > nodeTypeIdentifiers = NodeIdentifierFactory::ConstructNodeIdentifiers(treeItem);
|
|
|
|
for (auto nodeTypeIdentifier : nodeTypeIdentifiers)
|
|
{
|
|
AddCycleTarget(nodeTypeIdentifier);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#include <Editor/View/Widgets/moc_ScriptCanvasNodePaletteDockWidget.cpp>
|