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.
o3de/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp

1530 lines
67 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 <AzCore/Interface/Interface.h>
#include <AzCore/RTTI/BehaviorContextUtilities.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <Editor/Assets/ScriptCanvasAssetHelpers.h>
#include <Editor/Include/ScriptCanvas/Bus/RequestBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/Nodes/NodeUtils.h>
#include <Editor/Settings.h>
#include <Editor/Translation/TranslationHelper.h>
#include <ScriptCanvas/Data/DataRegistry.h>
#include <ScriptCanvas/Libraries/Libraries.h>
#include <ScriptCanvas/Libraries/Core/GetVariable.h>
#include <ScriptCanvas/Libraries/Core/Method.h>
#include <ScriptCanvas/Libraries/Core/SetVariable.h>
#include <ScriptCanvas/Utils/NodeUtils.h>
#include <ScriptCanvas/Data/Traits.h>
namespace
{
// Various Helper Methods
bool IsDeprecated(const AZ::AttributeArray& attributes)
{
bool isDeprecated{};
if (auto isDeprecatedAttributePtr = AZ::FindAttribute(AZ::Script::Attributes::Deprecated, attributes))
{
AZ::AttributeReader(nullptr, isDeprecatedAttributePtr).Read<bool>(isDeprecated);
}
return isDeprecated;
}
bool ShouldExcludeFromNodeList(const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>* excludeAttributeData, const AZ::Uuid& typeId)
{
if (excludeAttributeData)
{
AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List | AZ::Script::Attributes::ExcludeFlags::ListOnly;
if (typeId == AzToolsFramework::Components::EditorComponentBase::TYPEINFO_Uuid())
{
return true;
}
return (static_cast<AZ::u64>(excludeAttributeData->Get(nullptr)) & exclusionFlags) != 0; // warning C4800: 'AZ::u64': forcing value to bool 'true' or 'false' (performance warning)
}
return false;
}
bool HasExcludeFromNodeListAttribute(const AZ::SerializeContext* serializeContext, const AZ::Uuid& typeId)
{
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId);
if (classData && classData->m_editData)
{
if (auto editorElementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
{
if (auto excludeAttribute = editorElementData->FindAttribute(AZ::Script::Attributes::ExcludeFrom))
{
auto excludeAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(excludeAttribute);
return excludeAttributeData && ShouldExcludeFromNodeList(excludeAttributeData, typeId);
}
}
}
return false;
}
bool MethodHasAttribute(const AZ::BehaviorMethod* method, AZ::Crc32 attribute)
{
return AZ::FindAttribute(attribute, method->m_attributes) != nullptr; // warning C4800: 'AZ::Attribute *': forcing value to bool 'true' or 'false' (performance warning)
}
// Checks for and returns the Category attribute from an AZ::AttributeArray
AZStd::string GetCategoryPath(const AZ::AttributeArray& attributes, const AZ::BehaviorContext& behaviorContext)
{
AZStd::string retVal;
AZ::Attribute* categoryAttribute = AZ::FindAttribute(AZ::Script::Attributes::Category, attributes);
if (categoryAttribute)
{
AZ::AttributeReader(nullptr, categoryAttribute).Read<AZStd::string>(retVal, behaviorContext);
}
return retVal;
}
bool IsExplicitOverload(const AZ::BehaviorMethod& method)
{
return AZ::FindAttribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, method.m_attributes) != nullptr;
}
void RegisterMethod
( ScriptCanvasEditor::NodePaletteModel& nodePaletteModel
, const AZ::BehaviorContext& behaviorContext
, const AZStd::string& categoryPath
, const AZ::BehaviorClass* behaviorClass
, const AZStd::string& name
, const AZ::BehaviorMethod& method
, ScriptCanvas::PropertyStatus propertyStatus
, bool isOverloaded)
{
if (IsDeprecated(method.m_attributes))
{
return;
}
if (behaviorClass && !isOverloaded)
{
auto excludeMethodAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, method.m_attributes));
if (ShouldExcludeFromNodeList(excludeMethodAttributeData, behaviorClass->m_azRtti ? behaviorClass->m_azRtti->GetTypeId() : behaviorClass->m_typeId))
{
return;
}
}
const auto isExposableOutcome = ScriptCanvas::IsExposable(method);
if (!isExposableOutcome.IsSuccess())
{
AZ_Warning("ScriptCanvas", false, "Unable to expose method: %s to ScriptCanvas because: %s", method.m_name.data(), isExposableOutcome.GetError().data());
return;
}
// If the reflected method returns an AZ::Event, reflect it to the SerializeContext
if (AZ::MethodReturnsAzEventByReferenceOrPointer(method))
{
AZ::SerializeContext* serializeContext{};
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
const AZ::BehaviorParameter* resultParameter = method.GetResult();
AZ::SerializeContext::ClassData classData;
classData.m_name = resultParameter->m_name;
classData.m_typeId = resultParameter->m_typeId;
classData.m_azRtti = resultParameter->m_azRtti;
auto EventPlaceholderAnyCreator = [](AZ::SerializeContext*) -> AZStd::any
{
return AZStd::make_any<AZStd::monostate>();
};
serializeContext->RegisterType(resultParameter->m_typeId, AZStd::move(classData), EventPlaceholderAnyCreator);
}
nodePaletteModel.RegisterClassNode(categoryPath, behaviorClass ? behaviorClass->m_name : "", name, &method, &behaviorContext, propertyStatus, isOverloaded);
}
void RegisterGlobalMethod(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext,
const AZ::BehaviorMethod& behaviorMethod)
{
const auto isExposableOutcome = ScriptCanvas::IsExposable(behaviorMethod);
if (!isExposableOutcome.IsSuccess())
{
AZ_Warning("ScriptCanvas", false, "Unable to expose method: %s to ScriptCanvas because: %s",
behaviorMethod.m_name.c_str(), isExposableOutcome.GetError().data());
return;
}
if (!AZ::Internal::IsInScope(behaviorMethod.m_attributes, AZ::Script::Attributes::ScopeFlags::Common))
{
return; // skip this method
}
// If the reflected method returns an AZ::Event, reflect it to the SerializeContext
if (AZ::MethodReturnsAzEventByReferenceOrPointer(behaviorMethod))
{
AZ::SerializeContext* serializeContext{};
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
const AZ::BehaviorParameter* resultParameter = behaviorMethod.GetResult();
AZ::SerializeContext::ClassData classData;
classData.m_name = resultParameter->m_name;
classData.m_typeId = resultParameter->m_typeId;
classData.m_azRtti = resultParameter->m_azRtti;
auto EventPlaceholderAnyCreator = [](AZ::SerializeContext*) -> AZStd::any
{
return AZStd::make_any<AZStd::monostate>();
};
serializeContext->RegisterType(resultParameter->m_typeId, AZStd::move(classData), EventPlaceholderAnyCreator);
}
nodePaletteModel.RegisterMethodNode(behaviorContext, behaviorMethod);
}
//! Retrieve the list of EBuses t hat should not be exposed in the ScriptCanvasEditor Node Palette
AZStd::unordered_set<AZ::Crc32> GetEBusExcludeSet(const AZ::BehaviorContext& behaviorContext)
{
// We will skip buses that are ONLY registered on classes that derive from EditorComponentBase,
// because they don't have a runtime implementation. Buses such as the TransformComponent which
// is implemented by both an EditorComponentBase derived class and a Component derived class
// will still appear
AZStd::unordered_set<AZ::Crc32> skipBuses;
AZStd::unordered_set<AZ::Crc32> potentialSkipBuses;
AZStd::unordered_set<AZ::Crc32> nonSkipBuses;
for (const auto& classIter : behaviorContext.m_classes)
{
const AZ::BehaviorClass* behaviorClass = classIter.second;
if (IsDeprecated(behaviorClass->m_attributes))
{
continue;
}
// Only bind Behavior Classes marked with the Scope type of Launcher
if (!AZ::Internal::IsInScope(behaviorClass->m_attributes, AZ::Script::Attributes::ScopeFlags::Launcher))
{
continue; // skip this class
}
// Check for "ExcludeFrom" attribute for ScriptCanvas
auto excludeClassAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(
AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes));
// We don't want to show any components, since there isn't anything we can do with them
// from ScriptCanvas since we use buses to communicate to everything.
if (ShouldExcludeFromNodeList(excludeClassAttributeData, behaviorClass->m_azRtti ? behaviorClass->m_azRtti->GetTypeId() : behaviorClass->m_typeId))
{
for (const auto& requestBus : behaviorClass->m_requestBuses)
{
skipBuses.insert(AZ::Crc32(requestBus.c_str()));
}
continue;
}
auto baseClass = AZStd::find(behaviorClass->m_baseClasses.begin(),
behaviorClass->m_baseClasses.end(),
AzToolsFramework::Components::EditorComponentBase::TYPEINFO_Uuid());
if (baseClass != behaviorClass->m_baseClasses.end())
{
for (const auto& requestBus : behaviorClass->m_requestBuses)
{
potentialSkipBuses.insert(AZ::Crc32(requestBus.c_str()));
}
}
// If the Ebus does not inherit from EditorComponentBase then do not skip it
else
{
for (const auto& requestBus : behaviorClass->m_requestBuses)
{
nonSkipBuses.insert(AZ::Crc32(requestBus.c_str()));
}
}
}
// Add buses which are not on the non-skip list to the skipBuses set
for (auto potentialSkipBus : potentialSkipBuses)
{
if (nonSkipBuses.find(potentialSkipBus) == nonSkipBuses.end())
{
skipBuses.insert(potentialSkipBus);
}
}
return skipBuses;
}
//! Register all nodes populated into the ScriptCanvas NodeRegistry for each class derived
//! from the ScriptCanvas LibraryDefinition class
void PopulateScriptCanvasDerivedNodes(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::SerializeContext& serializeContext)
{
// Get all the types.
auto EnumerateLibraryDefintionNodes = [&nodePaletteModel, &serializeContext](
const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
{
ScriptCanvasEditor::CategoryInformation categoryInfo;
AZStd::string categoryPath = classData->m_editData ? classData->m_editData->m_name : classData->m_name;
if (classData->m_editData)
{
auto editorElementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
if (editorElementData)
{
if (auto categoryAttribute = editorElementData->FindAttribute(AZ::Edit::Attributes::Category))
{
if (auto categoryAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(categoryAttribute))
{
categoryPath = categoryAttributeData->Get(nullptr);
}
}
if (auto categoryStyleAttribute = editorElementData->FindAttribute(AZ::Edit::Attributes::CategoryStyle))
{
if (auto categoryAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(categoryStyleAttribute))
{
categoryInfo.m_styleOverride = categoryAttributeData->Get(nullptr);
}
}
if (auto titlePaletteAttribute = editorElementData->FindAttribute(ScriptCanvas::Attributes::Node::TitlePaletteOverride))
{
if (auto categoryAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(titlePaletteAttribute))
{
categoryInfo.m_paletteOverride = categoryAttributeData->Get(nullptr);
}
}
}
}
nodePaletteModel.RegisterCategoryInformation(categoryPath, categoryInfo);
// Children
for (auto& node : ScriptCanvas::Library::LibraryDefinition::GetNodes(classData->m_typeId))
{
if (HasExcludeFromNodeListAttribute(&serializeContext, node.first))
{
continue;
}
// Pass in the associated class data so we can do more intensive lookups?
const AZ::SerializeContext::ClassData* nodeClassData = serializeContext.FindClassData(node.first);
if (nodeClassData == nullptr)
{
continue;
}
// Skip over some of our more dynamic nodes that we want to populate using different means
else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf<ScriptCanvas::Nodes::Core::GetVariableNode>())
{
continue;
}
else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf<ScriptCanvas::Nodes::Core::SetVariableNode>())
{
continue;
}
else
{
nodePaletteModel.RegisterCustomNode(categoryPath, node.first, node.second, nodeClassData);
}
}
return true;
};
const AZ::TypeId& libraryDefTypeId = azrtti_typeid<ScriptCanvas::Library::LibraryDefinition>();
serializeContext.EnumerateDerived(EnumerateLibraryDefintionNodes, libraryDefTypeId, libraryDefTypeId);
}
void PopulateVariablePalette()
{
auto dataRegistry = ScriptCanvas::GetDataRegistry();
for (auto& type : dataRegistry->m_creatableTypes)
{
if (!type.second.m_isTransient)
{
ScriptCanvasEditor::VariablePaletteRequestBus::Broadcast(&ScriptCanvasEditor::VariablePaletteRequests::RegisterVariableType, type.first);
}
}
}
void PopulateBehaviorContextGlobalMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::BehaviorContext& behaviorContext)
{
// BehaviorMethods are not associated with a class
// therefore the Uuid is set to Null
const AZ::Uuid behaviorMethodUuid = AZ::Uuid::CreateNull();
for (const auto& [methodName, behaviorMethod] : behaviorContext.m_methods)
{
// Skip behavior methods that are deprecated
if (behaviorMethod == nullptr || IsDeprecated(behaviorMethod->m_attributes))
{
continue;
}
// Check for "ExcludeFrom" attribute for ScriptCanvas
auto excludeMethodAttributeData = azrtti_cast<const AZ::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(
AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorMethod->m_attributes));
if (ShouldExcludeFromNodeList(excludeMethodAttributeData, behaviorMethodUuid))
{
continue;
}
RegisterGlobalMethod(nodePaletteModel, behaviorContext, *behaviorMethod);
}
}
//! Iterates over all Properties directly reflected to the BehaviorContext instance
//! and registers there Getter/Setter methods to the NodePaletteModel
void PopulateBehaviorContextGlobalProperties(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::BehaviorContext& behaviorContext)
{
const AZ::Uuid behaviorMethodUuid = AZ::Uuid::CreateNull();
for (const auto& [propertyName, behaviorProperty] : behaviorContext.m_properties)
{
// Skip behavior properties that are deprecated
if (behaviorProperty == nullptr || IsDeprecated(behaviorProperty->m_attributes))
{
continue;
}
// Check for "ExcludeFrom" attribute for ScriptCanvas
auto excludePropertyAttributeData = azrtti_cast<const AZ::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(
AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorProperty->m_attributes));
if (ShouldExcludeFromNodeList(excludePropertyAttributeData, behaviorMethodUuid))
{
continue;
}
if (behaviorProperty->m_getter && !behaviorProperty->m_setter)
{
nodePaletteModel.RegisterGlobalConstant(behaviorContext, *behaviorProperty->m_getter);
}
else
{
if (behaviorProperty->m_getter)
{
RegisterGlobalMethod(nodePaletteModel, behaviorContext, *behaviorProperty->m_getter);
}
if (behaviorProperty->m_setter)
{
RegisterGlobalMethod(nodePaletteModel, behaviorContext, *behaviorProperty->m_setter);
}
}
}
}
void PopulateBehaviorContextClassMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::BehaviorContext& behaviorContext)
{
AZ::SerializeContext* serializeContext{};
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
for (const auto& classIter : behaviorContext.m_classes)
{
const AZ::BehaviorClass* behaviorClass = classIter.second;
if (IsDeprecated(behaviorClass->m_attributes))
{
continue;
}
if (auto excludeFromPointer = AZ::FindAttribute(AZ::ScriptCanvasAttributes::Internal::ImplementedAsNodeGeneric, behaviorClass->m_attributes))
{
continue;
}
if (auto excludeFromPointer = AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes))
{
AZ::Script::Attributes::ExcludeFlags excludeFlags{};
AZ::AttributeReader(nullptr, excludeFromPointer).Read<AZ::Script::Attributes::ExcludeFlags>(excludeFlags);
if ((excludeFlags & (AZ::Script::Attributes::ExcludeFlags::List | AZ::Script::Attributes::ExcludeFlags::ListOnly)) != 0)
{
continue;
}
}
if (!AZ::Internal::IsInScope(behaviorClass->m_attributes, AZ::Script::Attributes::ScopeFlags::Launcher))
{
continue;
}
// Objects and Object methods
{
AZStd::string categoryPath;
AZStd::string translationContext = ScriptCanvasEditor::TranslationHelper::GetContextName(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, behaviorClass->m_name);
AZStd::string translationKey = ScriptCanvasEditor::TranslationHelper::GetClassKey(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, behaviorClass->m_name, ScriptCanvasEditor::TranslationKeyId::Category);
AZStd::string translatedCategory = QCoreApplication::translate(translationContext.c_str(), translationKey.c_str()).toUtf8().data();
if (translatedCategory != translationKey)
{
categoryPath = translatedCategory;
}
else
{
AZStd::string behaviorContextCategory = GetCategoryPath(behaviorClass->m_attributes, behaviorContext);
if (!behaviorContextCategory.empty())
{
categoryPath = behaviorContextCategory;
}
}
auto dataRegistry = ScriptCanvas::GetDataRegistry();
ScriptCanvas::Data::Type type = dataRegistry->m_typeIdTraitMap[ScriptCanvas::Data::eType::BehaviorContextObject].m_dataTraits.GetSCType(behaviorClass->m_typeId);
if (type.IsValid())
{
if (dataRegistry->m_creatableTypes.contains(type))
{
ScriptCanvasEditor::VariablePaletteRequestBus::Broadcast(&ScriptCanvasEditor::VariablePaletteRequests::RegisterVariableType, type);
}
}
AZStd::string classNamePretty(classIter.first);
AZ::Attribute* prettyNameAttribute = AZ::FindAttribute(AZ::ScriptCanvasAttributes::PrettyName, behaviorClass->m_attributes);
if (prettyNameAttribute)
{
AZ::AttributeReader(nullptr, prettyNameAttribute).Read<AZStd::string>(classNamePretty, behaviorContext);
}
if (categoryPath.empty())
{
if (classNamePretty.empty())
{
categoryPath = classNamePretty;
}
else
{
categoryPath = "Other";
}
}
categoryPath.append("/");
AZStd::string displayName = ScriptCanvasEditor::TranslationHelper::GetClassKeyTranslation(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, classIter.first, ScriptCanvasEditor::TranslationKeyId::Name);
if (displayName.empty())
{
categoryPath.append(classNamePretty.c_str());
}
else
{
categoryPath.append(displayName.c_str());
}
for (auto property : behaviorClass->m_properties)
{
if (property.second->m_getter)
{
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_getter, ScriptCanvas::PropertyStatus::Getter, behaviorClass->IsMethodOverloaded(property.first));
}
if (property.second->m_setter)
{
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_setter, ScriptCanvas::PropertyStatus::Setter, behaviorClass->IsMethodOverloaded(property.first));
}
}
for (auto methodIter : behaviorClass->m_methods)
{
if (!IsExplicitOverload(*methodIter.second))
{
// Respect the exclusion flags
auto attributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, methodIter.second->m_attributes));
if (ShouldExcludeFromNodeList(attributeData , {}))
{
continue;
}
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, methodIter.first, *methodIter.second, ScriptCanvas::PropertyStatus::None, behaviorClass->IsMethodOverloaded(methodIter.first));
}
}
}
}
}
void PopulateBehaviorContextOverloadedMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::BehaviorContext& behaviorContext)
{
for (const AZ::ExplicitOverloadInfo& explicitOverload : behaviorContext.m_explicitOverloads)
{
RegisterMethod(nodePaletteModel, behaviorContext, explicitOverload.m_categoryPath, nullptr, explicitOverload.m_name, *explicitOverload.m_overloads.begin()->first, ScriptCanvas::PropertyStatus::None, true);
}
}
void PopulateBehaviorContextEBusHandler(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorEBus& behaviorEbus)
{
if (AZ::ScopedBehaviorEBusHandler handler{ behaviorEbus }; handler)
{
auto excludeEbusAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(
AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorEbus.m_attributes));
if (ShouldExcludeFromNodeList(excludeEbusAttributeData, handler->RTTI_GetType()))
{
return;
}
const AZ::BehaviorEBusHandler::EventArray& events(handler->GetEvents());
if (!events.empty())
{
AZStd::string translationContext = ScriptCanvasEditor::TranslationHelper::GetContextName(ScriptCanvasEditor::TranslationContextGroup::EbusHandler, behaviorEbus.m_name);
AZStd::string categoryPath;
{
AZStd::string translationKey = ScriptCanvasEditor::TranslationHelper::GetClassKey(ScriptCanvasEditor::TranslationContextGroup::EbusHandler, behaviorEbus.m_name, ScriptCanvasEditor::TranslationKeyId::Category);
AZStd::string translatedCategory = QCoreApplication::translate(translationContext.c_str(), translationKey.c_str()).toUtf8().data();
if (translatedCategory != translationKey)
{
categoryPath = translatedCategory;
}
else
{
AZStd::string behaviourContextCategory = GetCategoryPath(behaviorEbus.m_attributes, behaviorContext);
if (!behaviourContextCategory.empty())
{
categoryPath = behaviourContextCategory;
}
}
}
// Treat the EBusHandler name as a Category key in order to allow multiple busses to be merged into a single Category.
{
AZStd::string translationKey = ScriptCanvasEditor::TranslationHelper::GetClassKey(ScriptCanvasEditor::TranslationContextGroup::EbusHandler, behaviorEbus.m_name, ScriptCanvasEditor::TranslationKeyId::Name);
AZStd::string translatedName = QCoreApplication::translate(translationContext.c_str(), translationKey.c_str()).toUtf8().data();
if (!categoryPath.empty())
{
categoryPath.append("/");
}
else
{
categoryPath = "Other/";
}
if (translatedName != translationKey)
{
categoryPath.append(translatedName.c_str());
}
else
{
categoryPath.append(behaviorEbus.m_name.c_str());
}
}
for (const auto& event : events)
{
nodePaletteModel.RegisterEBusHandlerNodeModelInformation(categoryPath.c_str(), behaviorEbus.m_name, event.m_name, ScriptCanvas::EBusBusId(behaviorEbus.m_name), event);
}
}
}
}
void PopulateBehaviorContextEBusEventMethods(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorEBus& behaviorEbus)
{
if (!behaviorEbus.m_events.empty())
{
AZStd::string categoryPath;
AZStd::string translationContext = ScriptCanvasEditor::TranslationHelper::GetContextName(ScriptCanvasEditor::TranslationContextGroup::EbusSender, behaviorEbus.m_name);
AZStd::string translationKey = ScriptCanvasEditor::TranslationHelper::GetClassKey(ScriptCanvasEditor::TranslationContextGroup::EbusSender, behaviorEbus.m_name, ScriptCanvasEditor::TranslationKeyId::Category);
AZStd::string translatedCategory = QCoreApplication::translate(translationContext.c_str(), translationKey.c_str()).toUtf8().data();
if (translatedCategory != translationKey)
{
categoryPath = translatedCategory;
}
else
{
AZStd::string behaviourContextCategory = GetCategoryPath(behaviorEbus.m_attributes, behaviorContext);
if (!behaviourContextCategory.empty())
{
categoryPath = behaviourContextCategory;
}
}
// Parent
AZStd::string displayName = ScriptCanvasEditor::TranslationHelper::GetClassKeyTranslation(ScriptCanvasEditor::TranslationContextGroup::EbusSender, behaviorEbus.m_name, ScriptCanvasEditor::TranslationKeyId::Name);
// Treat the EBus name as a Category key in order to allow multiple busses to be merged into a single Category.
if (!categoryPath.empty())
{
categoryPath.append("/");
}
else
{
categoryPath = "Other/";
}
if (displayName.empty())
{
categoryPath.append(behaviorEbus.m_name.c_str());
}
else
{
categoryPath.append(displayName.c_str());
}
ScriptCanvasEditor::CategoryInformation ebusCategoryInformation;
ebusCategoryInformation.m_tooltip = ScriptCanvasEditor::TranslationHelper::GetClassKeyTranslation(ScriptCanvasEditor::TranslationContextGroup::EbusSender, behaviorEbus.m_name, ScriptCanvasEditor::TranslationKeyId::Tooltip);
nodePaletteModel.RegisterCategoryInformation(categoryPath, ebusCategoryInformation);
for (auto event : behaviorEbus.m_events)
{
if (IsDeprecated(event.second.m_attributes))
{
continue;
}
auto excludeEventAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, event.second.m_attributes));
if (ShouldExcludeFromNodeList(excludeEventAttributeData, AZ::Uuid::CreateNull()))
{
continue; // skip this event
}
const bool isOverload{ false }; // overloaded events are not trivially supported
nodePaletteModel.RegisterEBusSenderNodeModelInformation(categoryPath, behaviorEbus.m_name, event.first, ScriptCanvas::EBusBusId(behaviorEbus.m_name.c_str()), ScriptCanvas::EBusEventId(event.first.c_str()), event.second, ScriptCanvas::PropertyStatus::None, isOverload);
}
}
}
void PopulateBehaviorContextEBuses(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel,
const AZ::BehaviorContext& behaviorContext)
{
AZStd::unordered_set<AZ::Crc32> skipBuses = GetEBusExcludeSet(behaviorContext);
for (const auto& [ebusName, behaviorEbus] : behaviorContext.m_ebuses)
{
if (behaviorEbus == nullptr)
{
continue;
}
auto skipBusIterator = skipBuses.find(AZ::Crc32(ebusName));
if (skipBusIterator != skipBuses.end())
{
continue;
}
// Skip buses mapped by their deprecated name (usually duplicates)
if (ebusName == behaviorEbus->m_deprecatedName)
{
continue;
}
// Only bind Behavior Buses marked with the Scope type of Launcher
if (!AZ::Internal::IsInScope(behaviorEbus->m_attributes, AZ::Script::Attributes::ScopeFlags::Launcher))
{
continue; // skip this bus
}
if (IsDeprecated(behaviorEbus->m_attributes))
{
continue;
}
auto excludeEbusAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(
AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorEbus->m_attributes));
if (ShouldExcludeFromNodeList(excludeEbusAttributeData, AZ::Uuid::CreateNull()))
{
continue;
}
if (auto runtimeEbusAttributePtr = AZ::FindAttribute(AZ::RuntimeEBusAttribute, behaviorEbus->m_attributes))
{
bool isRuntimeEbus = false;
AZ::AttributeReader(nullptr, runtimeEbusAttributePtr).Read<bool>(isRuntimeEbus);
if (isRuntimeEbus)
{
continue;
}
}
// EBus Handler
PopulateBehaviorContextEBusHandler(nodePaletteModel, behaviorContext, *behaviorEbus);
// EBus Sender
PopulateBehaviorContextEBusEventMethods(nodePaletteModel, behaviorContext, *behaviorEbus);
}
}
// Helper function for populating the node palette model.
// Pulled out just to make the tabbing a bit nicer, since it's a huge method.
void PopulateNodePaletteModel(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
AZ_Assert(serializeContext, "Could not find SerializeContext. Aborting Palette Creation.");
AZ_Assert(behaviorContext, "Could not find BehaviorContext. Aborting Palette Creation.");
if (serializeContext == nullptr || behaviorContext == nullptr)
{
return;
}
// Populates the NodePalette with each ScriptCanvas LibraryDefinition derived class
// static InitNodeRegistry() function
PopulateScriptCanvasDerivedNodes(nodePaletteModel, *serializeContext);
// Populates the VariablePalette with type registered with the ScriptCanvas DataRegistry
PopulateVariablePalette();
// Populates the NodePalette with Behavior Class method nodes
PopulateBehaviorContextClassMethods(nodePaletteModel, *behaviorContext);
// Populates the NodePalette with BehaviorContext methods overloaded on the same name
PopulateBehaviorContextOverloadedMethods(nodePaletteModel, *behaviorContext);
// Populates the NodePalette with EBus Event method nodes and EBus Event handler nodes
PopulateBehaviorContextEBuses(nodePaletteModel, *behaviorContext);
// Populates the NodePalette with Methods reflected directly on the BehaviorContext
PopulateBehaviorContextGlobalMethods(nodePaletteModel, *behaviorContext);
// Populates the NodePalette with Properties reflected directly on the BehaviorContext
PopulateBehaviorContextGlobalProperties(nodePaletteModel, *behaviorContext);
}
}
namespace ScriptCanvasEditor
{
////////////////////////////////
// NodePaletteModelInformation
////////////////////////////////
void NodePaletteModelInformation::PopulateTreeItem(GraphCanvas::NodePaletteTreeItem& treeItem) const
{
if (!m_toolTip.empty())
{
treeItem.SetToolTip(m_toolTip.c_str());
}
if (!m_styleOverride.empty())
{
treeItem.SetStyleOverride(m_styleOverride.c_str());
}
if (!m_titlePaletteOverride.empty())
{
const bool forceSet = true;
treeItem.SetTitlePalette(m_titlePaletteOverride.c_str(), forceSet);
}
}
/////////////////////
// NodePaletteModel
/////////////////////
NodePaletteModel::NodePaletteModel()
: m_paletteId(AZ::Entity::MakeId())
{
UpgradeNotifications::Bus::Handler::BusConnect();
}
NodePaletteModel::~NodePaletteModel()
{
UpgradeNotifications::Bus::Handler::BusDisconnect();
DisconnectLambdas();
ClearRegistry();
}
NodePaletteId NodePaletteModel::GetNotificationId() const
{
return m_paletteId;
}
void NodePaletteModel::AssignAssetModel(AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel)
{
m_assetModel = assetModel;
if (m_assetModel)
{
TraverseTree();
ConnectLambdas();
}
}
void NodePaletteModel::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 NodePaletteModel::DisconnectLambdas()
{
for (auto connection : m_lambdaConnections)
{
QObject::disconnect(connection);
}
}
void NodePaletteModel::RepopulateModel()
{
ClearRegistry();
PopulateNodePaletteModel((*this));
if (m_assetModel)
{
TraverseTree();
}
NodePaletteModelNotificationBus::Event(m_paletteId, &NodePaletteModelNotifications::OnAssetModelRepopulated);
}
void NodePaletteModel::RegisterCustomNode(AZStd::string_view categoryPath, const AZ::Uuid& uuid, AZStd::string_view name, const AZ::SerializeContext::ClassData* classData)
{
ScriptCanvas::NodeTypeIdentifier nodeIdentifier = ScriptCanvas::NodeUtils::ConstructCustomNodeIdentifier(uuid);
auto mapIter = m_registeredNodes.find(nodeIdentifier);
if (mapIter == m_registeredNodes.end())
{
CustomNodeModelInformation* customNodeInformation = aznew CustomNodeModelInformation();
customNodeInformation->m_nodeIdentifier = nodeIdentifier;
customNodeInformation->m_typeId = uuid;
customNodeInformation->m_displayName = name;
bool isDeprecated(false);
if (classData && classData->m_editData && classData->m_editData->m_name)
{
auto nodeContextName = ScriptCanvasEditor::Nodes::GetContextName(*classData);
auto contextName = ScriptCanvasEditor::TranslationHelper::GetContextName(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, nodeContextName);
GraphCanvas::TranslationKeyedString nodeKeyedString({}, contextName);
nodeKeyedString.m_key = ScriptCanvasEditor::TranslationHelper::GetKey(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, nodeContextName, classData->m_editData->m_name, ScriptCanvasEditor::TranslationItemType::Node, ScriptCanvasEditor::TranslationKeyId::Name);
customNodeInformation->m_displayName = nodeKeyedString.GetDisplayString();
GraphCanvas::TranslationKeyedString tooltipKeyedString(AZStd::string(), nodeKeyedString.m_context);
tooltipKeyedString.m_key = ScriptCanvasEditor::TranslationHelper::GetKey(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, nodeContextName, classData->m_editData->m_name, ScriptCanvasEditor::TranslationItemType::Node, ScriptCanvasEditor::TranslationKeyId::Tooltip);
customNodeInformation->m_toolTip = tooltipKeyedString.GetDisplayString();
if (customNodeInformation->m_displayName.empty())
{
customNodeInformation->m_displayName = classData->m_editData->m_name;
}
GraphCanvas::TranslationKeyedString categoryKeyedString(ScriptCanvasEditor::Nodes::GetCategoryName(*classData), nodeKeyedString.m_context);
categoryKeyedString.m_key = ScriptCanvasEditor::TranslationHelper::GetKey(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, nodeContextName, classData->m_editData->m_name, ScriptCanvasEditor::TranslationItemType::Node, ScriptCanvasEditor::TranslationKeyId::Category);
customNodeInformation->m_categoryPath = categoryKeyedString.GetDisplayString();
if (customNodeInformation->m_categoryPath.empty())
{
if (contextName.empty())
{
customNodeInformation->m_categoryPath = categoryPath;
}
else
{
customNodeInformation->m_categoryPath = contextName;
}
}
auto editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
if (editorDataElement)
{
if (auto categoryStyleAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::CategoryStyle))
{
if (auto categoryAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(categoryStyleAttribute))
{
if (categoryAttributeData->Get(nullptr))
{
customNodeInformation->m_styleOverride = categoryAttributeData->Get(nullptr);
}
}
}
if (auto titlePaletteAttribute = editorDataElement->FindAttribute(ScriptCanvas::Attributes::Node::TitlePaletteOverride))
{
if (auto titlePaletteAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(titlePaletteAttribute))
{
if (titlePaletteAttributeData->Get(nullptr))
{
customNodeInformation->m_titlePaletteOverride = titlePaletteAttributeData->Get(nullptr);
}
}
}
if (auto deprecatedAttribute = editorDataElement->FindAttribute(AZ::Script::Attributes::Deprecated))
{
if (auto deprecatedAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<bool>*>(deprecatedAttribute))
{
isDeprecated = deprecatedAttributeData->Get(nullptr);
}
}
if (customNodeInformation->m_toolTip.empty() && classData->m_editData->m_description)
{
customNodeInformation->m_toolTip = classData->m_editData->m_description;
}
}
}
if (!isDeprecated)
{
m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, customNodeInformation));
}
else
{
delete customNodeInformation;
}
}
}
void NodePaletteModel::RegisterClassNode
( const AZStd::string& categoryPath
, const AZStd::string& methodClass
, const AZStd::string& methodName
, const AZ::BehaviorMethod* behaviorMethod
, const AZ::BehaviorContext* behaviorContext
, ScriptCanvas::PropertyStatus propertyStatus
, bool isOverload)
{
ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructMethodOverloadedNodeIdentifier(methodName) : ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(methodClass, methodName, propertyStatus);
auto registerIter = m_registeredNodes.find(nodeIdentifier);
if (registerIter == m_registeredNodes.end())
{
MethodNodeModelInformation* methodModelInformation = aznew MethodNodeModelInformation();
methodModelInformation->m_isOverload = isOverload;
methodModelInformation->m_nodeIdentifier = nodeIdentifier;
methodModelInformation->m_classMethod = methodClass;
methodModelInformation->m_methodName = methodName;
methodModelInformation->m_propertyStatus = propertyStatus;
methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette";
methodModelInformation->m_displayName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, methodClass.c_str(), methodName.c_str(), TranslationItemType::Node, TranslationKeyId::Name);
if (methodModelInformation->m_displayName.empty())
{
methodModelInformation->m_displayName = methodName;
}
methodModelInformation->m_toolTip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, methodClass.c_str(), methodName.c_str(), TranslationItemType::Node, TranslationKeyId::Tooltip);
GraphCanvas::TranslationKeyedString methodCategoryString;
methodCategoryString.m_context = ScriptCanvasEditor::TranslationHelper::GetContextName(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, methodClass.c_str());
methodCategoryString.m_key = ScriptCanvasEditor::TranslationHelper::GetKey(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, methodClass.c_str(), methodName.c_str(), ScriptCanvasEditor::TranslationItemType::Node, ScriptCanvasEditor::TranslationKeyId::Category);
methodModelInformation->m_categoryPath = methodCategoryString.GetDisplayString();
if (methodModelInformation->m_categoryPath.empty())
{
if (!MethodHasAttribute(behaviorMethod, AZ::ScriptCanvasAttributes::FloatingFunction))
{
methodModelInformation->m_categoryPath = categoryPath;
}
else if (MethodHasAttribute(behaviorMethod, AZ::Script::Attributes::Category))
{
methodModelInformation->m_categoryPath = GetCategoryPath(behaviorMethod->m_attributes, (*behaviorContext));
}
if (methodModelInformation->m_categoryPath.empty())
{
methodModelInformation->m_categoryPath = "Other";
}
}
m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, methodModelInformation));
}
}
void NodePaletteModel::RegisterGlobalConstant(const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorMethod& behaviorMethod)
{
// Construct Node Identifier using the BehaviorMethod name and the ScriptCanvas Method typeid
ScriptCanvas::NodeTypeIdentifier nodeIdentifier =
ScriptCanvas::NodeUtils::ConstructGlobalMethodNodeIdentifier(behaviorMethod.m_name);
// Register the methodModelInformation if not already registered
if (auto registerIter = m_registeredNodes.find(nodeIdentifier); registerIter == m_registeredNodes.end())
{
auto methodModelInformation = AZStd::make_unique<GlobalMethodNodeModelInformation>();
methodModelInformation->m_methodName = behaviorMethod.m_name;
methodModelInformation->m_nodeIdentifier = nodeIdentifier;
methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette";
methodModelInformation->m_displayName = TranslationHelper::GetGlobalMethodKeyTranslation(methodModelInformation->m_methodName,
TranslationItemType::Node, TranslationKeyId::Name);
methodModelInformation->m_toolTip = TranslationHelper::GetGlobalMethodKeyTranslation(methodModelInformation->m_methodName,
TranslationItemType::Node, TranslationKeyId::Tooltip);
methodModelInformation->m_categoryPath = TranslationHelper::GetGlobalMethodKeyTranslation(methodModelInformation->m_methodName,
TranslationItemType::Node, TranslationKeyId::Category);
if (methodModelInformation->m_displayName.empty())
{
methodModelInformation->m_displayName = methodModelInformation->m_methodName;
}
if (methodModelInformation->m_categoryPath.empty())
{
methodModelInformation->m_categoryPath = GetCategoryPath(behaviorMethod.m_attributes, behaviorContext);
// Default to making the Category for Global Methods to be informative that the method
// is registered with the Behavior Context
if (methodModelInformation->m_categoryPath.empty())
{
methodModelInformation->m_categoryPath = "Constants";
}
}
m_registeredNodes.emplace(nodeIdentifier, methodModelInformation.release());
}
}
void NodePaletteModel::RegisterMethodNode(const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorMethod& behaviorMethod)
{
// Construct Node Identifier using the BehaviorMethod name and the ScriptCanvas Method typeid
ScriptCanvas::NodeTypeIdentifier nodeIdentifier =
ScriptCanvas::NodeUtils::ConstructGlobalMethodNodeIdentifier(behaviorMethod.m_name);
// Register the methodModelInformation if not already registered
if (auto registerIter = m_registeredNodes.find(nodeIdentifier); registerIter == m_registeredNodes.end())
{
auto methodModelInformation = AZStd::make_unique<GlobalMethodNodeModelInformation>();
methodModelInformation->m_methodName = behaviorMethod.m_name;
methodModelInformation->m_nodeIdentifier = nodeIdentifier;
methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette";
methodModelInformation->m_displayName = TranslationHelper::GetGlobalMethodKeyTranslation(methodModelInformation->m_methodName,
TranslationItemType::Node, TranslationKeyId::Name);
methodModelInformation->m_toolTip = TranslationHelper::GetGlobalMethodKeyTranslation(methodModelInformation->m_methodName,
TranslationItemType::Node, TranslationKeyId::Tooltip);
methodModelInformation->m_categoryPath = TranslationHelper::GetGlobalMethodKeyTranslation(methodModelInformation->m_methodName,
TranslationItemType::Node, TranslationKeyId::Category);
if (methodModelInformation->m_displayName.empty())
{
methodModelInformation->m_displayName = methodModelInformation->m_methodName;
}
if (methodModelInformation->m_categoryPath.empty())
{
methodModelInformation->m_categoryPath = GetCategoryPath(behaviorMethod.m_attributes, behaviorContext);
// Default to making the Category for Global Methods to be informative that the method
// is registered with the Behavior Context
if (methodModelInformation->m_categoryPath.empty())
{
methodModelInformation->m_categoryPath = "Behavior Context: Global Methods";
}
}
m_registeredNodes.emplace(nodeIdentifier, methodModelInformation.release());
}
}
void NodePaletteModel::RegisterEBusHandlerNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const AZ::BehaviorEBusHandler::BusForwarderEvent& forwardEvent)
{
ScriptCanvas::NodeTypeIdentifier nodeIdentifier = ScriptCanvas::NodeUtils::ConstructEBusEventReceiverIdentifier(busId, forwardEvent.m_eventId);
auto nodeIter = m_registeredNodes.find(nodeIdentifier);
if (nodeIter == m_registeredNodes.end())
{
EBusHandlerNodeModelInformation* handlerInformation = aznew EBusHandlerNodeModelInformation();
handlerInformation->m_titlePaletteOverride = "HandlerNodeTitlePalette";
handlerInformation->m_categoryPath = categoryPath;
handlerInformation->m_nodeIdentifier = nodeIdentifier;
handlerInformation->m_busName = busName;
handlerInformation->m_eventName = eventName;
handlerInformation->m_busId = busId;
handlerInformation->m_eventId = forwardEvent.m_eventId;
AZStd::string displayEventName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusHandler, busName.data(), eventName.data(), TranslationItemType::Node, TranslationKeyId::Name);
if (displayEventName.empty())
{
handlerInformation->m_displayName = eventName;
}
else
{
handlerInformation->m_displayName = displayEventName;
}
handlerInformation->m_toolTip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusHandler, busName.data(), eventName.data(), TranslationItemType::Node, TranslationKeyId::Tooltip);
m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, handlerInformation));
}
}
void NodePaletteModel::RegisterEBusSenderNodeModelInformation
( AZStd::string_view categoryPath
, AZStd::string_view busName
, AZStd::string_view eventName
, const ScriptCanvas::EBusBusId& busId
, const ScriptCanvas::EBusEventId& eventId
, const AZ::BehaviorEBusEventSender&
, ScriptCanvas::PropertyStatus propertyStatus
, bool isOverload)
{
ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructEBusEventSenderOverloadedIdentifier(busId, eventId) : ScriptCanvas::NodeUtils::ConstructEBusEventSenderIdentifier(busId, eventId);
auto nodeIter = m_registeredNodes.find(nodeIdentifier);
if (nodeIter == m_registeredNodes.end())
{
EBusSenderNodeModelInformation* senderInformation = aznew EBusSenderNodeModelInformation();
senderInformation->m_isOverload = isOverload;
senderInformation->m_titlePaletteOverride = "MethodNodeTitlePalette";
senderInformation->m_categoryPath = categoryPath;
senderInformation->m_nodeIdentifier = nodeIdentifier;
senderInformation->m_propertyStatus = propertyStatus;
senderInformation->m_busName = busName;
senderInformation->m_eventName = eventName;
senderInformation->m_busId = busId;
senderInformation->m_eventId = eventId;
AZStd::string displayEventName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusSender, busName.data(), eventName.data(), TranslationItemType::Node, TranslationKeyId::Name);
if (displayEventName.empty())
{
senderInformation->m_displayName = eventName;
}
else
{
senderInformation->m_displayName = displayEventName;
}
senderInformation->m_toolTip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusSender, busName.data(), eventName.data(), TranslationItemType::Node, TranslationKeyId::Tooltip);
m_registeredNodes.emplace(AZStd::make_pair(nodeIdentifier, senderInformation));
}
}
AZStd::vector<ScriptCanvas::NodeTypeIdentifier> NodePaletteModel::RegisterScriptEvent(ScriptEvents::ScriptEventsAsset* scriptEventAsset)
{
const ScriptEvents::ScriptEvent& scriptEvent = scriptEventAsset->m_definition;
ScriptCanvas::EBusBusId busId = scriptEventAsset->GetBusId();
AZStd::string category = scriptEvent.GetCategory();
auto methods = scriptEvent.GetMethods();
AZStd::vector<ScriptCanvas::NodeTypeIdentifier> identifiers;
// Each event has a handler and a reciever
identifiers.reserve(methods.size() * 2);
for (const auto& method : methods)
{
ScriptCanvas::EBusEventId eventId = method.GetEventId();
ScriptCanvas::NodeTypeIdentifier senderIdentifier = ScriptCanvas::NodeUtils::ConstructSendScriptEventIdentifier(busId, eventId);
ScriptCanvas::NodeTypeIdentifier receiverIdentifier = ScriptCanvas::NodeUtils::ConstructScriptEventReceiverIdentifier(busId, eventId);
ScriptEventHandlerNodeModelInformation* handlerInformation = aznew ScriptEventHandlerNodeModelInformation();
handlerInformation->m_titlePaletteOverride = "HandlerNodeTitlePalette";
handlerInformation->m_busName = scriptEvent.GetName();
handlerInformation->m_eventName = method.GetName();
handlerInformation->m_displayName = method.GetName();
handlerInformation->m_categoryPath = scriptEvent.GetCategory();
handlerInformation->m_busId = busId;
handlerInformation->m_eventId = eventId;
handlerInformation->m_nodeIdentifier = receiverIdentifier;
m_registeredNodes.emplace(AZStd::make_pair(receiverIdentifier, handlerInformation));
ScriptEventSenderNodeModelInformation* senderInformation = aznew ScriptEventSenderNodeModelInformation();
senderInformation->m_titlePaletteOverride = "MethodNodeTitlePalette";
senderInformation->m_busName = scriptEvent.GetName();
senderInformation->m_eventName = method.GetName();
senderInformation->m_displayName = method.GetName();
senderInformation->m_categoryPath = scriptEvent.GetCategory();
senderInformation->m_busId = busId;
senderInformation->m_eventId = eventId;
senderInformation->m_nodeIdentifier = senderIdentifier;
m_registeredNodes.emplace(AZStd::make_pair(senderIdentifier, senderInformation));
m_assetMapping.insert(AZStd::make_pair(scriptEventAsset->GetId(), senderIdentifier));
m_assetMapping.insert(AZStd::make_pair(scriptEventAsset->GetId(), receiverIdentifier));
identifiers.emplace_back(senderIdentifier);
identifiers.emplace_back(receiverIdentifier);
}
return identifiers;
}
void NodePaletteModel::RegisterCategoryInformation(const AZStd::string& category, const CategoryInformation& categoryInformation)
{
auto categoryIter = m_categoryInformation.find(category);
if (categoryIter == m_categoryInformation.end())
{
m_categoryInformation[category] = categoryInformation;
}
}
const CategoryInformation* NodePaletteModel::FindCategoryInformation(const AZStd::string& categoryStyle) const
{
auto categoryIter = m_categoryInformation.find(categoryStyle);
if (categoryIter != m_categoryInformation.end())
{
return &(categoryIter->second);
}
return nullptr;
}
const CategoryInformation* NodePaletteModel::FindBestCategoryInformation(AZStd::string_view categoryView) const
{
const CategoryInformation* bestCategoryFit = nullptr;
auto categoryIter = m_categoryInformation.find(categoryView);
size_t offset = AZStd::string_view::npos;
AZStd::string_view categoryTrail = categoryView;
while (categoryIter == m_categoryInformation.end() && !categoryTrail.empty())
{
size_t seperator = categoryTrail.find_last_of('/', offset);
if (seperator == AZStd::string_view::npos)
{
categoryTrail = nullptr;
}
else
{
categoryTrail = categoryTrail.substr(0, seperator - 1);
categoryIter = m_categoryInformation.find(categoryTrail);
}
}
if (categoryIter != m_categoryInformation.end())
{
bestCategoryFit = &(categoryIter->second);
}
return bestCategoryFit;
}
const NodePaletteModelInformation* NodePaletteModel::FindNodePaletteInformation(const ScriptCanvas::NodeTypeIdentifier& nodeType) const
{
auto registryIter = m_registeredNodes.find(nodeType);
if (registryIter != m_registeredNodes.end())
{
return registryIter->second;
}
return nullptr;
}
const NodePaletteModel::NodePaletteRegistry& NodePaletteModel::GetNodeRegistry() const
{
return m_registeredNodes;
}
GraphCanvas::GraphCanvasTreeItem* NodePaletteModel::CreateCategoryNode(AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvas::GraphCanvasTreeItem* parentItem) const
{
GraphCanvas::NodePaletteTreeItem* treeItem = parentItem->CreateChildNode<GraphCanvas::NodePaletteTreeItem>(categoryName, ScriptCanvasEditor::AssetEditorId);
const CategoryInformation* categoryInformation = FindCategoryInformation(categoryPath);
if (categoryInformation)
{
if (!categoryInformation->m_tooltip.empty())
{
treeItem->SetToolTip(categoryInformation->m_tooltip.c_str());
}
if (!categoryInformation->m_paletteOverride.empty())
{
treeItem->SetTitlePalette(categoryInformation->m_paletteOverride.c_str());
}
if (!categoryInformation->m_styleOverride.empty())
{
treeItem->SetStyleOverride(categoryInformation->m_styleOverride.c_str());
}
}
return treeItem;
}
void NodePaletteModel::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());
auto nodeTypeIdentifiers = ProcessAsset(entry);
for (auto nodeTypeIdentifier : nodeTypeIdentifiers)
{
auto nodeIter = m_registeredNodes.find(nodeTypeIdentifier);
if (nodeIter != m_registeredNodes.end())
{
NodePaletteModelNotificationBus::Event(m_paletteId, &NodePaletteModelNotifications::OnAssetNodeAdded, nodeIter->second);
}
}
}
}
void NodePaletteModel::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);
if (productEntry)
{
RemoveAsset(productEntry->GetAssetId());
}
}
}
}
void NodePaletteModel::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);
}
}
AZStd::vector<ScriptCanvas::NodeTypeIdentifier> NodePaletteModel::ProcessAsset(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
AZStd::lock_guard<AZStd::recursive_mutex> myLocker(m_mutex);
if (entry)
{
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptEvents::ScriptEventsAsset>())
{
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
auto busAsset = AZ::Data::AssetManager::Instance().GetAsset(assetId, azrtti_typeid<ScriptEvents::ScriptEventsAsset>(), AZ::Data::AssetLoadBehavior::PreLoad);
busAsset.BlockUntilLoadComplete();
if (busAsset.IsReady())
{
ScriptEvents::ScriptEventsAsset* data = busAsset.GetAs<ScriptEvents::ScriptEventsAsset>();
return RegisterScriptEvent(data);
}
else
{
AZ_TracePrintf("NodePaletteModel", "Could not refresh node palette properly, the asset failed to load correctly.");
}
}
}
}
return AZStd::vector<ScriptCanvas::NodeTypeIdentifier>();
}
void NodePaletteModel::RemoveAsset(const AZ::Data::AssetId& assetId)
{
auto mapRange = m_assetMapping.equal_range(assetId);
for (auto rangeIter = mapRange.first; rangeIter != mapRange.second; ++rangeIter)
{
auto nodeIter = m_registeredNodes.find(rangeIter->second);
if (nodeIter != m_registeredNodes.end())
{
NodePaletteModelNotificationBus::Event(m_paletteId, &NodePaletteModelNotifications::OnAssetNodeRemoved, nodeIter->second);
delete nodeIter->second;
m_registeredNodes.erase(nodeIter);
}
}
}
void NodePaletteModel::ClearRegistry()
{
for (auto& mapPair : m_registeredNodes)
{
delete mapPair.second;
}
m_registeredNodes.clear();
m_categoryInformation.clear();
}
}