DynamicNodeSlotConfig.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AtomToolsFramework/Graph/DynamicNode/DynamicNodeManagerRequestBus.h>
  9. #include <AtomToolsFramework/Graph/DynamicNode/DynamicNodeSlotConfig.h>
  10. #include <AtomToolsFramework/Graph/DynamicNode/DynamicNodeUtil.h>
  11. #include <AtomToolsFramework/Util/Util.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/Serialization/EditContext.h>
  14. #include <AzCore/Serialization/Json/JsonUtils.h>
  15. #include <AzCore/Serialization/Json/RegistrationContext.h>
  16. #include <AzCore/Serialization/SerializeContext.h>
  17. #include <AzCore/std/string/regex.h>
  18. namespace AtomToolsFramework
  19. {
  20. void DynamicNodeSlotConfig::Reflect(AZ::ReflectContext* context)
  21. {
  22. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  23. {
  24. serializeContext->Class<DynamicNodeSlotConfig>()
  25. ->Version(0)
  26. ->Field("name", &DynamicNodeSlotConfig::m_name)
  27. ->Field("displayName", &DynamicNodeSlotConfig::m_displayName)
  28. ->Field("description", &DynamicNodeSlotConfig::m_description)
  29. ->Field("supportedDataTypeRegex", &DynamicNodeSlotConfig::m_supportedDataTypeRegex)
  30. ->Field("defaultDataType", &DynamicNodeSlotConfig::m_defaultDataType)
  31. ->Field("defaultValue", &DynamicNodeSlotConfig::m_defaultValue)
  32. ->Field("enumValues", &DynamicNodeSlotConfig::m_enumValues)
  33. ->Field("visibleOnNode", &DynamicNodeSlotConfig::m_visibleOnNode)
  34. ->Field("editableOnNode", &DynamicNodeSlotConfig::m_editableOnNode)
  35. ->Field("allowNameSubstitution", &DynamicNodeSlotConfig::m_allowNameSubstitution)
  36. ->Field("settings", &DynamicNodeSlotConfig::m_settings)
  37. ;
  38. if (auto editContext = serializeContext->GetEditContext())
  39. {
  40. editContext->Class<DynamicNodeSlotConfig>("DynamicNodeSlotConfig", "Configuration settings for individual slots on a dynamic node.")
  41. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  42. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  43. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &DynamicNodeSlotConfig::GetDisplayNameForEditor)
  44. ->SetDynamicEditDataProvider(&DynamicNodeSlotConfig::GetDynamicEditData)
  45. ->UIElement(AZ::Edit::UIHandlers::Button, "", "Add new settings groups from settings registered with this tool.")
  46. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &DynamicNodeSlotConfig::AddRegisteredSettingGroups)
  47. ->Attribute(AZ::Edit::Attributes::ButtonText, "Add Setting Groups")
  48. ->DataElement(AZ_CRC_CE("MultilineStringDialog"), &DynamicNodeSlotConfig::m_name, "Name", "Unique name used to identify individual slots on a node.")
  49. ->DataElement(AZ_CRC_CE("MultilineStringDialog"), &DynamicNodeSlotConfig::m_displayName, "Display Name", "User friendly title of the slot that will appear on the node UI.")
  50. ->DataElement(AZ_CRC_CE("MultilineStringDialog"), &DynamicNodeSlotConfig::m_description, "Description", "Detailed description of the node, its purpose, and behavior that will appear in tooltips and other UI.")
  51. ->DataElement(AZ_CRC_CE("MultilineStringDialog"), &DynamicNodeSlotConfig::m_supportedDataTypeRegex, "Supported Data Type Regex", "Regular expression to search for data types compatible with this slot.")
  52. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &DynamicNodeSlotConfig::ValidateDataTypes)
  53. ->Attribute(AZ::Edit::Attributes::ClearNotify, &DynamicNodeSlotConfig::ValidateDataTypes)
  54. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &DynamicNodeSlotConfig::m_defaultDataType, "Default Data Type", "Name of the default data type for this slot. If this is not specified the default data type will fall back to the first supported data type.")
  55. ->Attribute(AZ::Edit::Attributes::StringList, &DynamicNodeSlotConfig::GetSupportedDataTypeNames)
  56. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &DynamicNodeSlotConfig::ValidateDataTypes)
  57. ->Attribute(AZ::Edit::Attributes::ClearNotify, &DynamicNodeSlotConfig::ValidateDataTypes)
  58. ->DataElement(AZ::Edit::UIHandlers::Default, &DynamicNodeSlotConfig::m_defaultValue, "Default Value", "The initial value of an input or property slot that has no incoming connection.")
  59. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  60. ->ElementAttribute(AZ::Edit::Attributes::NameLabelOverride, "Default Value")
  61. ->DataElement(AZ::Edit::UIHandlers::Default, &DynamicNodeSlotConfig::m_enumValues, "Enum Values", "List of potential values if the data type is a string.")
  62. ->DataElement(AZ::Edit::UIHandlers::Default, &DynamicNodeSlotConfig::m_visibleOnNode, "Visible On Node", "Enable this for the slot to appear on the node UI in the graph view.")
  63. ->DataElement(AZ::Edit::UIHandlers::Default, &DynamicNodeSlotConfig::m_editableOnNode, "Editable On Node", "Enable this for the slot value to be editable on the node UI in the graph view.")
  64. ->DataElement(AZ::Edit::UIHandlers::Default, &DynamicNodeSlotConfig::m_allowNameSubstitution, "Allow Name Substitution", "Hint on whether or not the slot name can be substituted or mangled in applicable systems.")
  65. ->DataElement(AZ::Edit::UIHandlers::Default, &DynamicNodeSlotConfig::m_settings, "Settings", "Table of strings that can be used for any context specific or user defined data for each slot.")
  66. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
  67. ->Attribute(AZ::Edit::Attributes::ClearNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  68. ->Attribute(AZ::Edit::Attributes::AddNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  69. ->Attribute(AZ::Edit::Attributes::RemoveNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  70. ->ElementAttribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
  71. ->ElementAttribute(AZ::Edit::Attributes::ClearNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  72. ->ElementAttribute(AZ::Edit::Attributes::AddNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  73. ->ElementAttribute(AZ::Edit::Attributes::RemoveNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  74. ;
  75. }
  76. }
  77. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  78. {
  79. behaviorContext->Class<DynamicNodeSlotConfig>("DynamicNodeSlotConfig")
  80. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  81. ->Attribute(AZ::Script::Attributes::Category, "Editor")
  82. ->Attribute(AZ::Script::Attributes::Module, "atomtools")
  83. ->Constructor()
  84. ->Constructor<const DynamicNodeSlotConfig&>()
  85. ->Property("name", BehaviorValueProperty(&DynamicNodeSlotConfig::m_name))
  86. ->Property("displayName", BehaviorValueProperty(&DynamicNodeSlotConfig::m_displayName))
  87. ->Property("defaultValue", BehaviorValueProperty(&DynamicNodeSlotConfig::m_defaultValue))
  88. ->Property("defaultValue", BehaviorValueProperty(&DynamicNodeSlotConfig::m_defaultValue))
  89. ->Property("enumValues", BehaviorValueProperty(&DynamicNodeSlotConfig::m_enumValues))
  90. ->Property("supportedDataTypeRegex", BehaviorValueProperty(&DynamicNodeSlotConfig::m_supportedDataTypeRegex))
  91. ->Property("defaultDataType", BehaviorValueProperty(&DynamicNodeSlotConfig::m_defaultDataType))
  92. ->Property("visibleOnNode", BehaviorValueProperty(&DynamicNodeSlotConfig::m_visibleOnNode))
  93. ->Property("editableOnNode", BehaviorValueProperty(&DynamicNodeSlotConfig::m_editableOnNode))
  94. ->Property("allowNameSubstitution", BehaviorValueProperty(&DynamicNodeSlotConfig::m_allowNameSubstitution))
  95. ->Property("settings", BehaviorValueProperty(&DynamicNodeSlotConfig::m_settings))
  96. ;
  97. }
  98. }
  99. DynamicNodeSlotConfig::DynamicNodeSlotConfig(
  100. const AZStd::string& name,
  101. const AZStd::string& displayName,
  102. const AZStd::string& description,
  103. const AZStd::any& defaultValue,
  104. const AZStd::string& supportedDataTypeRegex,
  105. const DynamicNodeSettingsMap& settings)
  106. : m_name(name)
  107. , m_displayName(displayName)
  108. , m_description(description)
  109. , m_defaultValue(defaultValue)
  110. , m_supportedDataTypeRegex(supportedDataTypeRegex)
  111. , m_settings(settings)
  112. {
  113. }
  114. AZ::Crc32 DynamicNodeSlotConfig::ValidateDataTypes()
  115. {
  116. const auto& supportedDataTypes = GetSupportedDataTypes();
  117. if (supportedDataTypes.empty())
  118. {
  119. const bool hadDefaultValue = !m_defaultValue.empty();
  120. m_defaultDataType.clear();
  121. m_defaultValue.clear();
  122. return hadDefaultValue ? AZ::Edit::PropertyRefreshLevels::EntireTree : AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  123. }
  124. // Find the registered data types corresponding to the selected default value and default data type.
  125. auto defaultValueItr = AZStd::find_if(supportedDataTypes.begin(), supportedDataTypes.end(), [&](const auto& dataType) {
  126. return dataType->IsSupportedValue(m_defaultValue);
  127. });
  128. auto defaultTypeItr = AZStd::find_if(supportedDataTypes.begin(), supportedDataTypes.end(), [&](const auto& dataType) {
  129. return dataType->GetDisplayName() == m_defaultDataType;
  130. });
  131. // If the default data type is not set or does not match any of the supported types then assign it to the same type as the default
  132. // value or the first registered data type.
  133. if (defaultTypeItr == supportedDataTypes.end())
  134. {
  135. if (defaultValueItr != supportedDataTypes.end())
  136. {
  137. m_defaultDataType = (*defaultValueItr)->GetDisplayName();
  138. defaultTypeItr = defaultValueItr;
  139. }
  140. else
  141. {
  142. m_defaultDataType = supportedDataTypes.front()->GetDisplayName();
  143. defaultTypeItr = supportedDataTypes.begin();
  144. }
  145. }
  146. // Finally, if the default value does not match the default data type then reset it.
  147. if (defaultTypeItr != defaultValueItr)
  148. {
  149. m_defaultValue = (*defaultTypeItr)->GetDefaultValue();
  150. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  151. }
  152. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  153. }
  154. AZStd::any DynamicNodeSlotConfig::GetDefaultValue() const
  155. {
  156. const auto& supportedDataTypes = GetSupportedDataTypes();
  157. for (const auto& dataType : supportedDataTypes)
  158. {
  159. if (dataType->IsSupportedValue(m_defaultValue))
  160. {
  161. return m_defaultValue;
  162. }
  163. }
  164. return !supportedDataTypes.empty() ? supportedDataTypes.front()->GetDefaultValue() : AZStd::any();
  165. }
  166. AZStd::string DynamicNodeSlotConfig::GetDefaultDataTypeName() const
  167. {
  168. const auto& dataType = GetDefaultDataType();
  169. return dataType ? dataType->GetDisplayName() : AZStd::string();
  170. }
  171. GraphModel::DataTypePtr DynamicNodeSlotConfig::GetDefaultDataType() const
  172. {
  173. const auto& supportedDataTypes = GetSupportedDataTypes();
  174. for (const auto& dataType : supportedDataTypes)
  175. {
  176. if (dataType->GetDisplayName() == m_defaultDataType)
  177. {
  178. return dataType;
  179. }
  180. }
  181. return !supportedDataTypes.empty() ? supportedDataTypes.front() : GraphModel::DataTypePtr();
  182. }
  183. AZStd::vector<AZStd::string> DynamicNodeSlotConfig::GetSupportedDataTypeNames() const
  184. {
  185. AZStd::vector<AZStd::string> dataTypeNames;
  186. for (const auto& dataType : GetSupportedDataTypes())
  187. {
  188. dataTypeNames.push_back(dataType->GetDisplayName());
  189. }
  190. return dataTypeNames;
  191. }
  192. GraphModel::DataTypeList DynamicNodeSlotConfig::GetSupportedDataTypes() const
  193. {
  194. GraphModel::DataTypeList supportedDataTypes;
  195. if (!m_supportedDataTypeRegex.empty())
  196. {
  197. DynamicNodeManagerRequestBus::BroadcastResult(supportedDataTypes, &DynamicNodeManagerRequestBus::Events::GetRegisteredDataTypes);
  198. AZStd::regex supportedDataTypeRegex(m_supportedDataTypeRegex, AZStd::regex::flag_type::icase);
  199. AZStd::erase_if(supportedDataTypes, [&](const auto& dataType) { return !AZStd::regex_match(dataType->GetDisplayName(), supportedDataTypeRegex); });
  200. }
  201. return supportedDataTypes;
  202. }
  203. AZStd::string DynamicNodeSlotConfig::GetDisplayNameForEditor() const
  204. {
  205. return AZStd::string::format("Slot (%s)", !m_name.empty() ? m_name.c_str() : "unnamed");
  206. }
  207. void DynamicNodeSlotConfig::AutoFillMissingData()
  208. {
  209. if (m_displayName.empty())
  210. {
  211. m_displayName = GetDisplayNameFromText(m_name);
  212. }
  213. if (m_description.empty())
  214. {
  215. m_description = m_displayName;
  216. }
  217. }
  218. AZ::Crc32 DynamicNodeSlotConfig::AddRegisteredSettingGroups()
  219. {
  220. if (AddRegisteredSettingGroupsToMap(m_settings))
  221. {
  222. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  223. }
  224. return AZ::Edit::PropertyRefreshLevels::None;
  225. }
  226. const AZ::Edit::ElementData* DynamicNodeSlotConfig::GetDynamicEditData(
  227. const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType)
  228. {
  229. const DynamicNodeSlotConfig* owner = reinterpret_cast<const DynamicNodeSlotConfig*>(handlerPtr);
  230. if (elementType == azrtti_typeid<AZStd::string>())
  231. {
  232. return FindDynamicEditDataForSetting(owner->m_settings, elementPtr);
  233. }
  234. return nullptr;
  235. }
  236. } // namespace AtomToolsFramework