DynamicSlotFullCreation.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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 <QMenu>
  9. #include <GraphCanvas/Components/GridBus.h>
  10. #include <GraphCanvas/Components/Nodes/Comment/CommentBus.h>
  11. #include <GraphCanvas/Components/SceneBus.h>
  12. #include <GraphCanvas/Components/Slots/Data/DataSlotBus.h>
  13. #include <GraphCanvas/Components/VisualBus.h>
  14. #include <GraphCanvas/Editor/EditorTypes.h>
  15. #include <GraphCanvas/GraphCanvasBus.h>
  16. #include <GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h>
  17. #include <GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h>
  18. #include <Editor/View/Widgets/NodePalette/CreateNodeMimeEvent.h>
  19. #include <ScriptCanvas/Bus/RequestBus.h>
  20. #include <ScriptCanvas/Core/Graph.h>
  21. #include <ScriptCanvas/Core/GraphBus.h>
  22. #include <ScriptCanvas/Core/Node.h>
  23. #include <ScriptCanvas/Variable/VariableBus.h>
  24. #include <Editor/Include/ScriptCanvas/Bus/NodeIdPair.h>
  25. #include <Editor/Include/ScriptCanvas/GraphCanvas/MappingBus.h>
  26. #include <ScriptCanvasDeveloperEditor/AutomationActions/DynamicSlotFullCreation.h>
  27. #include <ScriptCanvasDeveloperEditor/DeveloperUtils.h>
  28. namespace ScriptCanvasDeveloperEditor
  29. {
  30. namespace DynamicSlotFullCreation
  31. {
  32. class DynamicSlotFullCreationInterface
  33. : public ProcessNodePaletteInterface
  34. {
  35. public:
  36. DynamicSlotFullCreationInterface(DeveloperUtils::ConnectionStyle connectionStyle)
  37. {
  38. m_chainConfig.m_connectionStyle = connectionStyle;
  39. m_chainConfig.m_skipHandlers = true;
  40. }
  41. virtual ~DynamicSlotFullCreationInterface() = default;
  42. void SetupInterface(const AZ::EntityId& activeGraphCanvasGraphId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId) override
  43. {
  44. m_graphCanvasGraphId = activeGraphCanvasGraphId;
  45. m_scriptCanvasId = scriptCanvasId;
  46. GraphCanvas::SceneRequestBus::EventResult(m_viewId, activeGraphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
  47. GraphCanvas::SceneRequestBus::EventResult(m_gridId, activeGraphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
  48. GraphCanvas::GridRequestBus::EventResult(m_minorPitch, m_gridId, &GraphCanvas::GridRequests::GetMinorPitch);
  49. QGraphicsScene* graphicsScene = nullptr;
  50. GraphCanvas::SceneRequestBus::EventResult(graphicsScene, activeGraphCanvasGraphId, &GraphCanvas::SceneRequests::AsQGraphicsScene);
  51. if (graphicsScene)
  52. {
  53. QRectF sceneArea = graphicsScene->sceneRect();
  54. sceneArea.adjust(m_minorPitch.GetX(), m_minorPitch.GetY(), -m_minorPitch.GetX(), -m_minorPitch.GetY());
  55. GraphCanvas::ViewRequestBus::Event(m_viewId, &GraphCanvas::ViewRequests::CenterOnArea, sceneArea);
  56. QApplication::processEvents();
  57. }
  58. GraphCanvas::ViewRequestBus::EventResult(m_nodeCreationPos, m_viewId, &GraphCanvas::ViewRequests::GetViewSceneCenter);
  59. GraphCanvas::GraphCanvasGraphicsView* graphicsView = nullptr;
  60. GraphCanvas::ViewRequestBus::EventResult(graphicsView, m_viewId, &GraphCanvas::ViewRequests::AsGraphicsView);
  61. m_viewportRectangle = graphicsView->mapToScene(graphicsView->viewport()->geometry()).boundingRect();
  62. ScriptCanvas::GraphRequestBus::EventResult(m_graph, m_scriptCanvasId, &ScriptCanvas::GraphRequests::GetGraph);
  63. ScriptCanvas::GraphVariableManagerRequests* variableRequests = ScriptCanvas::GraphVariableManagerRequestBus::FindFirstHandler(m_scriptCanvasId);
  64. if (variableRequests)
  65. {
  66. const ScriptCanvas::GraphVariableMapping* mapping = variableRequests->GetVariables();
  67. AZStd::unordered_set< ScriptCanvas::Data::Type > dataTypes;
  68. for (const auto& variablePair : (*mapping))
  69. {
  70. auto insertResult = dataTypes.insert(variablePair.second.GetDataType());
  71. if (insertResult.second)
  72. {
  73. m_availableVariableIds.emplace_back(variablePair.first);
  74. m_variableTypeMapping[variablePair.first] = variablePair.second.GetDataType();
  75. }
  76. }
  77. }
  78. // Temporary work around until the extra automation tools can be merged over that have better ways of doing this.
  79. const GraphCanvas::GraphCanvasTreeItem* treeItem = nullptr;
  80. ScriptCanvasEditor::AutomationRequestBus::BroadcastResult(treeItem, &ScriptCanvasEditor::AutomationRequests::GetNodePaletteRoot);
  81. const GraphCanvas::NodePaletteTreeItem* onGraphStartItem = nullptr;
  82. if (treeItem)
  83. {
  84. AZStd::unordered_set< const GraphCanvas::GraphCanvasTreeItem* > unexploredSet = { treeItem };
  85. while (!unexploredSet.empty())
  86. {
  87. const GraphCanvas::GraphCanvasTreeItem* treeItem2 = (*unexploredSet.begin());
  88. unexploredSet.erase(unexploredSet.begin());
  89. const GraphCanvas::NodePaletteTreeItem* nodePaletteTreeItem = azrtti_cast<const GraphCanvas::NodePaletteTreeItem*>(treeItem2);
  90. if (nodePaletteTreeItem && nodePaletteTreeItem->GetName().compare("On Graph Start") == 0)
  91. {
  92. onGraphStartItem = nodePaletteTreeItem;
  93. break;
  94. }
  95. for (int i = 0; i < treeItem2->GetChildCount(); ++i)
  96. {
  97. const GraphCanvas::GraphCanvasTreeItem* childItem = treeItem2->FindChildByRow(i);
  98. if (childItem)
  99. {
  100. unexploredSet.insert(childItem);
  101. }
  102. }
  103. }
  104. }
  105. if (onGraphStartItem)
  106. {
  107. GraphCanvas::GraphCanvasMimeEvent* mimeEvent = onGraphStartItem->CreateMimeEvent();
  108. if (mimeEvent)
  109. {
  110. ScriptCanvasEditor::NodeIdPair createdPair = DeveloperUtils::HandleMimeEvent(mimeEvent, m_graphCanvasGraphId, m_viewportRectangle, m_widthOffset, m_heightOffset, m_maxRowHeight, m_minorPitch);
  111. DeveloperUtils::CreateConnectedChain(createdPair, m_chainConfig);
  112. }
  113. }
  114. }
  115. bool ShouldProcessItem([[maybe_unused]] const GraphCanvas::NodePaletteTreeItem* nodePaletteTreeItem) const override
  116. {
  117. return !m_availableVariableIds.empty();
  118. }
  119. void ProcessItem(const GraphCanvas::NodePaletteTreeItem* nodePaletteTreeItem) override
  120. {
  121. AZStd::unordered_set<ScriptCanvasEditor::NodeIdPair> createdPairs;
  122. GraphCanvas::GraphCanvasMimeEvent* mimeEvent = nodePaletteTreeItem->CreateMimeEvent();
  123. AZStd::unordered_set< AZ::EntityId > nodesToDelete;
  124. if (ScriptCanvasEditor::MultiCreateNodeMimeEvent* multiCreateMimeEvent = azrtti_cast<ScriptCanvasEditor::MultiCreateNodeMimeEvent*>(mimeEvent))
  125. {
  126. AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > mimeEvents = multiCreateMimeEvent->CreateMimeEvents();
  127. for (GraphCanvas::GraphCanvasMimeEvent* currentEvent : mimeEvents)
  128. {
  129. ScriptCanvasEditor::NodeIdPair processPair = ProcessMimeEvent(currentEvent);
  130. nodesToDelete.insert(GraphCanvas::GraphUtils::FindOutermostNode(processPair.m_graphCanvasId));
  131. delete currentEvent;
  132. }
  133. }
  134. else if (mimeEvent)
  135. {
  136. ScriptCanvasEditor::NodeIdPair processPair = ProcessMimeEvent(mimeEvent);
  137. nodesToDelete.insert(GraphCanvas::GraphUtils::FindOutermostNode(processPair.m_graphCanvasId));
  138. }
  139. delete mimeEvent;
  140. GraphCanvas::SceneRequestBus::Event(m_graphCanvasGraphId, &GraphCanvas::SceneRequests::Delete, nodesToDelete);
  141. }
  142. private:
  143. bool HasDynamicSlots(ScriptCanvasEditor::NodeIdPair creationPair) const
  144. {
  145. ScriptCanvas::Node* node = m_graph->FindNode(creationPair.m_scriptCanvasId);
  146. if (node)
  147. {
  148. for (const ScriptCanvas::Slot* slot : node->GetAllSlots())
  149. {
  150. if (slot->IsDynamicSlot())
  151. {
  152. return true;
  153. }
  154. }
  155. }
  156. return false;
  157. }
  158. void PopulateSlots(GraphCanvas::GraphCanvasMimeEvent* currentEvent, const ScriptCanvasEditor::NodeIdPair& prototypeNode)
  159. {
  160. int origWidth = m_widthOffset;
  161. int origHeight = m_heightOffset;
  162. int origRowHeight = m_maxRowHeight;
  163. // Creating enough data to do a brute-force lexographical ordering of all availble
  164. // variable types to all slots. In every combination.
  165. //
  166. // Create a mapping of available types to each slot.
  167. // Along with an order or the slots.
  168. // Will consume data from the outermost slot, and as it empties, will continue to consume downward.
  169. // With each empty consumption triggering a refilling of all the after slots.
  170. AZStd::vector<ScriptCanvas::SlotId> slotOrdering;
  171. AZStd::unordered_map<ScriptCanvas::SlotId, AZStd::vector<ScriptCanvas::VariableId> > groupDataTypes;
  172. AZStd::unordered_set<AZ::Crc32> usedSlotGroups;
  173. AZStd::unordered_map<ScriptCanvas::SlotId, ScriptCanvas::TransientSlotIdentifier> prototypeIdentifiers;
  174. int tempWidth = m_widthOffset;
  175. int tempHeight = m_heightOffset;
  176. int tempRowHeight = m_maxRowHeight;
  177. // Generate a prototype node which will we scrape for data.
  178. ScriptCanvas::Node* protoTypeNode = m_graph->FindNode(prototypeNode.m_scriptCanvasId);
  179. for (const ScriptCanvas::Slot* slot : protoTypeNode->GetAllSlots())
  180. {
  181. prototypeIdentifiers[slot->GetId()] = slot->GetTransientIdentifier();
  182. if (slot->IsDynamicSlot())
  183. {
  184. AZ::Crc32 dynamicGroup = slot->GetDynamicGroup();
  185. if (usedSlotGroups.find(dynamicGroup) != usedSlotGroups.end())
  186. {
  187. continue;
  188. }
  189. slotOrdering.emplace_back(slot->GetId());
  190. groupDataTypes.insert_key(slot->GetId());
  191. if (dynamicGroup != AZ::Crc32())
  192. {
  193. usedSlotGroups.insert(dynamicGroup);
  194. }
  195. }
  196. }
  197. // Populate all of the groups with all the data types they need to have.
  198. for (auto& keyPair : groupDataTypes)
  199. {
  200. keyPair.second = m_availableVariableIds;
  201. }
  202. AZStd::vector<AZ::EntityId> usedGraphCanvasNodeIds;
  203. // Construct a single node to use, until we need to swap it out for a new one.
  204. // Then we can just delete this node at the end.
  205. auto nodeIdPair = DeveloperUtils::HandleMimeEvent(currentEvent, m_graphCanvasGraphId, m_viewportRectangle, tempWidth, tempHeight, tempRowHeight, m_minorPitch);
  206. ScriptCanvas::Node* node = m_graph->FindNode(nodeIdPair.m_scriptCanvasId);
  207. // Begin brute force lexical permutations!
  208. while (node)
  209. {
  210. int i = 0;
  211. // Attempt to assign the current set of variable types.
  212. for (; i < slotOrdering.size(); ++i)
  213. {
  214. auto groupDataIter = groupDataTypes.find(slotOrdering[i]);
  215. auto transientSlotIter = prototypeIdentifiers.find(slotOrdering[i]);
  216. if (transientSlotIter != prototypeIdentifiers.end())
  217. {
  218. ScriptCanvas::TransientSlotIdentifier slotIdentifier = transientSlotIter->second;
  219. ScriptCanvas::Slot* slot = node->GetSlotByTransientId(slotIdentifier);
  220. const ScriptCanvas::VariableId nextVariableId = groupDataIter->second.front();
  221. auto variableTypeIter = m_variableTypeMapping.find(nextVariableId);
  222. if (variableTypeIter == m_variableTypeMapping.end() || !slot->IsTypeMatchFor(variableTypeIter->second))
  223. {
  224. break;
  225. }
  226. if (!slot->IsVariableReference())
  227. {
  228. GraphCanvas::Endpoint endpoint = ConvertToGraphCanvasEndpoint(slot->GetEndpoint());
  229. bool canConvertToReference = false;
  230. GraphCanvas::DataSlotRequestBus::EventResult(canConvertToReference, endpoint.GetSlotId(), &GraphCanvas::DataSlotRequests::CanConvertToReference, false);
  231. if (canConvertToReference)
  232. {
  233. GraphCanvas::DataSlotRequestBus::Event(endpoint.GetSlotId(), &GraphCanvas::DataSlotRequests::ConvertToReference, false);
  234. }
  235. }
  236. if (!slot->IsVariableReference())
  237. {
  238. break;
  239. }
  240. node->SetSlotVariableId(slot->GetId(), nextVariableId);
  241. }
  242. }
  243. // If we succeeded, we need to update our offsets, and create a new node.
  244. if (i >= slotOrdering.size())
  245. {
  246. DeveloperUtils::CreateConnectedChain(nodeIdPair, m_chainConfig);
  247. usedGraphCanvasNodeIds.emplace_back(nodeIdPair.m_graphCanvasId);
  248. DeveloperUtils::UpdateViewportPositionOffsetForNode(nodeIdPair.m_graphCanvasId, m_viewportRectangle, m_widthOffset, m_heightOffset, m_maxRowHeight, m_minorPitch);
  249. tempWidth = m_widthOffset;
  250. tempHeight = m_heightOffset;
  251. tempRowHeight = m_maxRowHeight;
  252. nodeIdPair = DeveloperUtils::HandleMimeEvent(currentEvent, m_graphCanvasGraphId, m_viewportRectangle, tempWidth, tempHeight, tempRowHeight, m_minorPitch);
  253. node = m_graph->FindNode(nodeIdPair.m_scriptCanvasId);
  254. i = aznumeric_cast<int>(slotOrdering.size()) - 1;
  255. }
  256. else
  257. {
  258. // Reset all of the previous variable ids if we failed.
  259. for (int r = 0; r < i; ++r)
  260. {
  261. auto transientSlotIter = prototypeIdentifiers.find(slotOrdering[r]);
  262. if (transientSlotIter != prototypeIdentifiers.end())
  263. {
  264. ScriptCanvas::TransientSlotIdentifier slotIdentifier = transientSlotIter->second;
  265. ScriptCanvas::Slot* slot = node->GetSlotByTransientId(slotIdentifier);
  266. node->ClearSlotVariableId(slot->GetId());
  267. }
  268. }
  269. tempWidth = m_widthOffset;
  270. tempHeight = m_heightOffset;
  271. tempRowHeight = m_maxRowHeight;
  272. }
  273. // Determine where we failed, and remove that value
  274. // Updating everything after it to the new state.
  275. //
  276. // i.e. if we failed on the first element. We can drop values off of it.
  277. // Then reset everything after it to the full 'state' allowing us to skip
  278. // large swathes of invalid configurations.
  279. while (i < slotOrdering.size())
  280. {
  281. const ScriptCanvas::SlotId& indexPoint = slotOrdering[i];
  282. auto groupDataIter = groupDataTypes.find(indexPoint);
  283. if (groupDataIter != groupDataTypes.end())
  284. {
  285. groupDataIter->second.erase(groupDataIter->second.begin());
  286. if (groupDataIter->second.empty())
  287. {
  288. --i;
  289. }
  290. else
  291. {
  292. break;
  293. }
  294. }
  295. else
  296. {
  297. break;
  298. }
  299. }
  300. // If we rolled off the front node. We are done.
  301. // Otherwise we need to reset elements.
  302. if (i >= 0)
  303. {
  304. i++;
  305. while (i < slotOrdering.size())
  306. {
  307. const ScriptCanvas::SlotId& indexPoint = slotOrdering[i];
  308. auto groupDataIter = groupDataTypes.find(indexPoint);
  309. if (groupDataIter != groupDataTypes.end())
  310. {
  311. groupDataIter->second = m_availableVariableIds;
  312. }
  313. i++;
  314. }
  315. }
  316. else
  317. {
  318. break;
  319. }
  320. }
  321. if (!usedGraphCanvasNodeIds.empty())
  322. {
  323. // Create a group to create some visual chunking that'll look nice.
  324. //
  325. // Need to force one group per element for visual chunkiness.
  326. // Each group will also represent the end of a chunk for the row(otherwise weird overlap issues could visually occur)
  327. AZ::EntityId groupId = GraphCanvas::GraphUtils::CreateGroupForElements(m_graphCanvasGraphId, usedGraphCanvasNodeIds);
  328. GraphCanvas::CommentRequestBus::Event(groupId, &GraphCanvas::CommentRequests::SetComment, "New Group");
  329. if (groupId.IsValid())
  330. {
  331. tempWidth = origWidth;
  332. tempHeight = origHeight;
  333. tempRowHeight = origRowHeight;
  334. DeveloperUtils::UpdateViewportPositionOffsetForNode(groupId, m_viewportRectangle, tempWidth, tempHeight, tempRowHeight, m_minorPitch);
  335. // If we got kicked down to a new row. Nothing left to do.
  336. if (tempRowHeight <= 0)
  337. {
  338. m_widthOffset = tempWidth;
  339. m_heightOffset = tempHeight;
  340. m_maxRowHeight = tempRowHeight;
  341. }
  342. else
  343. {
  344. m_widthOffset = 0;
  345. m_heightOffset = origHeight + tempRowHeight + aznumeric_cast<int>(m_minorPitch.GetY());
  346. m_maxRowHeight = 0;
  347. }
  348. GraphCanvas::CommentRequestBus::Event(groupId, &GraphCanvas::CommentRequests::SetComment, protoTypeNode->GetDebugName().c_str());
  349. }
  350. }
  351. AZStd::unordered_set< AZ::EntityId > deleteSet = {
  352. GraphCanvas::GraphUtils::FindOutermostNode(nodeIdPair.m_graphCanvasId)
  353. };
  354. GraphCanvas::SceneRequestBus::Event(m_graphCanvasGraphId, &GraphCanvas::SceneRequests::Delete, deleteSet);
  355. }
  356. ScriptCanvasEditor::NodeIdPair ProcessMimeEvent(GraphCanvas::GraphCanvasMimeEvent* currentEvent)
  357. {
  358. int tempWidth = m_widthOffset;
  359. int tempHeight = m_heightOffset;
  360. int tempRowHeight = m_maxRowHeight;
  361. auto nodeIdPair = DeveloperUtils::HandleMimeEvent(currentEvent, m_graphCanvasGraphId, m_viewportRectangle, tempWidth, tempHeight, tempRowHeight, m_minorPitch);
  362. if (HasDynamicSlots(nodeIdPair))
  363. {
  364. PopulateSlots(currentEvent, nodeIdPair);
  365. }
  366. return nodeIdPair;
  367. }
  368. GraphCanvas::Endpoint ConvertToGraphCanvasEndpoint(const ScriptCanvas::Endpoint& endpoint)
  369. {
  370. GraphCanvas::Endpoint graphCanvasEndpoint;
  371. ScriptCanvasEditor::SlotMappingRequestBus::EventResult(graphCanvasEndpoint.m_slotId, endpoint.GetNodeId(), &ScriptCanvasEditor::SlotMappingRequests::MapToGraphCanvasId, endpoint.GetSlotId());
  372. GraphCanvas::SlotRequestBus::EventResult(graphCanvasEndpoint.m_nodeId, graphCanvasEndpoint.GetSlotId(), &GraphCanvas::SlotRequests::GetNode);
  373. return graphCanvasEndpoint;
  374. }
  375. AZ::EntityId m_graphCanvasGraphId;
  376. ScriptCanvas::ScriptCanvasId m_scriptCanvasId;
  377. AZ::Vector2 m_nodeCreationPos = AZ::Vector2::CreateZero();
  378. AZ::EntityId m_viewId;
  379. AZ::EntityId m_gridId;
  380. AZ::Vector2 m_minorPitch = AZ::Vector2::CreateZero();
  381. QRectF m_viewportRectangle;
  382. int m_widthOffset = 0;
  383. int m_heightOffset = 0;
  384. int m_maxRowHeight = 0;
  385. ScriptCanvas::Graph* m_graph = nullptr;
  386. DeveloperUtils::CreateConnectedChainConfig m_chainConfig;
  387. AZStd::vector<ScriptCanvas::VariableId> m_availableVariableIds;
  388. AZStd::unordered_map<ScriptCanvas::VariableId, ScriptCanvas::Data::Type> m_variableTypeMapping;
  389. };
  390. void VariablePaletteFullCreationAction()
  391. {
  392. ScriptCanvasEditor::AutomationRequestBus::Broadcast(&ScriptCanvasEditor::AutomationRequests::SignalAutomationBegin);
  393. DynamicSlotFullCreationInterface fullCreationInterface(DeveloperUtils::NoConnections);
  394. DeveloperUtils::ProcessNodePalette(fullCreationInterface);
  395. ScriptCanvasEditor::AutomationRequestBus::Broadcast(&ScriptCanvasEditor::AutomationRequests::SignalAutomationEnd);
  396. }
  397. void VariablePaletteFullyConnectionCreationAction()
  398. {
  399. ScriptCanvasEditor::AutomationRequestBus::Broadcast(&ScriptCanvasEditor::AutomationRequests::SignalAutomationBegin);
  400. DynamicSlotFullCreationInterface fullCreationInterface(DeveloperUtils::SingleExecutionConnection);
  401. DeveloperUtils::ProcessNodePalette(fullCreationInterface);
  402. ScriptCanvasEditor::AutomationRequestBus::Broadcast(&ScriptCanvasEditor::AutomationRequests::SignalAutomationEnd);
  403. }
  404. QAction* CreateDynamicSlotFullCreationAction(QMenu* mainMenu)
  405. {
  406. QAction* dynamicSlotFullCreationAction = nullptr;
  407. if (mainMenu)
  408. {
  409. mainMenu->addSeparator();
  410. {
  411. dynamicSlotFullCreationAction = mainMenu->addAction(QAction::tr("Mass Populate Dynamic Nodes"));
  412. dynamicSlotFullCreationAction->setAutoRepeat(false);
  413. dynamicSlotFullCreationAction->setToolTip("Tries to create every node in the node palette with dynamic slots.\nAnd will generate variations of the node with variables assigned to each slot in each combination depending on what variables are available in the Variable Manager.");
  414. dynamicSlotFullCreationAction->setShortcut(QKeySequence(QAction::tr("Ctrl+Shift+k", "Debug|Create Variable Palette")));
  415. QObject::connect(dynamicSlotFullCreationAction, &QAction::triggered, &VariablePaletteFullCreationAction);
  416. }
  417. {
  418. dynamicSlotFullCreationAction = mainMenu->addAction(QAction::tr("Mass Populate and Connect Dynamic Nodes"));
  419. dynamicSlotFullCreationAction->setAutoRepeat(false);
  420. dynamicSlotFullCreationAction->setToolTip("Tries to create and connect every node in the node palette with dynamic slots.\nAnd will generate variations of the node with variables assigned to each slot in each combination depending on what variables are available in the Variable Manager.");
  421. QObject::connect(dynamicSlotFullCreationAction, &QAction::triggered, &VariablePaletteFullyConnectionCreationAction);
  422. }
  423. }
  424. return dynamicSlotFullCreationAction;
  425. }
  426. }
  427. }