3
0

GraphValidationDockWidget.cpp 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479
  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 <QButtonGroup>
  9. #include <AzQtComponents/Components/ToastNotification.h>
  10. #include <GraphCanvas/Components/Connections/ConnectionBus.h>
  11. #include <GraphCanvas/Components/GridBus.h>
  12. #include <GraphCanvas/Components/Nodes/NodeBus.h>
  13. #include <GraphCanvas/Components/Nodes/Group/NodeGroupBus.h>
  14. #include <GraphCanvas/Components/SceneBus.h>
  15. #include <GraphCanvas/Components/StyleBus.h>
  16. #include <GraphCanvas/Components/VisualBus.h>
  17. #include <GraphCanvas/Utils/GraphUtils.h>
  18. #include <GraphCanvas/Utils/NodeNudgingController.h>
  19. #include <Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.h>
  20. #include <Editor/View/Widgets/ValidationPanel/ui_GraphValidationPanel.h>
  21. #include <Editor/View/Widgets/ValidationPanel/GraphValidationDockWidgetBus.h>
  22. #include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
  23. #include <Editor/Nodes/NodeCreateUtils.h>
  24. #include <Editor/View/Widgets/VariablePanel/VariableDockWidget.h>
  25. #include <ScriptCanvas/Bus/EditorScriptCanvasBus.h>
  26. #include <ScriptCanvas/Bus/RequestBus.h>
  27. #include <ScriptCanvas/Core/ConnectionBus.h>
  28. #include <ScriptCanvas/Core/NodeBus.h>
  29. #include <ScriptCanvas/Debugger/ValidationEvents/DataValidation/DataValidationEvents.h>
  30. #include <ScriptCanvas/Debugger/ValidationEvents/ExecutionValidation/ExecutionValidationEvents.h>
  31. #include <ScriptCanvas/Debugger/ValidationEvents/ValidationEffects/HighlightEffect.h>
  32. #include <ScriptCanvas/Debugger/ValidationEvents/ValidationEffects/GreyOutEffect.h>
  33. #include <ScriptCanvas/GraphCanvas/MappingBus.h>
  34. #include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
  35. #include <ScriptCanvas/Variable/VariableBus.h>
  36. namespace ScriptCanvasEditor
  37. {
  38. /////////////////////////////////////
  39. // HighlightElementValidationEffect
  40. /////////////////////////////////////
  41. HighlightElementValidationEffect::HighlightElementValidationEffect()
  42. {
  43. m_templateConfiguration.m_blurRadius = 0; // #17174 using blur degrades performance
  44. m_templateConfiguration.m_pen = QPen();
  45. m_templateConfiguration.m_pen.setBrush(Qt::red);
  46. m_templateConfiguration.m_pen.setWidth(5);
  47. m_templateConfiguration.m_zValue = 0;
  48. m_templateConfiguration.m_pulseRate = AZStd::chrono::milliseconds(2500);
  49. }
  50. HighlightElementValidationEffect::HighlightElementValidationEffect(const QColor& color)
  51. : HighlightElementValidationEffect()
  52. {
  53. m_templateConfiguration.m_pen.setBrush(color);
  54. }
  55. HighlightElementValidationEffect::HighlightElementValidationEffect(const GraphCanvas::SceneMemberGlowOutlineConfiguration& glowConfiguration)
  56. : m_templateConfiguration(glowConfiguration)
  57. {
  58. }
  59. void HighlightElementValidationEffect::AddTarget(const AZ::EntityId& scriptCanvasTargetId)
  60. {
  61. AZ::EntityId graphCanvasMemberId;
  62. SceneMemberMappingRequestBus::EventResult(graphCanvasMemberId, scriptCanvasTargetId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
  63. graphCanvasMemberId = GraphCanvas::GraphUtils::FindVisibleElement(graphCanvasMemberId);
  64. m_targets.emplace_back(graphCanvasMemberId);
  65. }
  66. void HighlightElementValidationEffect::DisplayEffect(const GraphCanvas::GraphId& graphId)
  67. {
  68. for (const auto& targetId : m_targets)
  69. {
  70. GraphCanvas::SceneMemberGlowOutlineConfiguration glowConfiguration = m_templateConfiguration;
  71. glowConfiguration.m_sceneMember = targetId;
  72. GraphCanvas::SceneMemberUIRequestBus::EventResult(glowConfiguration.m_zValue, targetId, &GraphCanvas::SceneMemberUIRequests::GetZValue);
  73. GraphCanvas::GraphicsEffectId effectId;
  74. GraphCanvas::SceneRequestBus::EventResult(effectId, graphId, &GraphCanvas::SceneRequests::CreateGlowOnSceneMember, glowConfiguration);
  75. if (effectId.IsValid())
  76. {
  77. m_graphicEffectIds.emplace_back(effectId);
  78. }
  79. }
  80. m_graphId = graphId;
  81. }
  82. void HighlightElementValidationEffect::CancelEffect()
  83. {
  84. for (const auto& graphicsEffectId : m_graphicEffectIds)
  85. {
  86. GraphCanvas::SceneRequestBus::Event(m_graphId, &GraphCanvas::SceneRequests::CancelGraphicsEffect, graphicsEffectId);
  87. }
  88. m_graphicEffectIds.clear();
  89. }
  90. ///////////////////////////////
  91. // UnusedNodeValidationEffect
  92. ///////////////////////////////
  93. constexpr const char* UnusedSelector = ":unused";
  94. constexpr const char* UnknownUseState = ":partially_unused";
  95. void UnusedNodeValidationEffect::AddUnusedNode(const AZ::EntityId& scriptCanvasNodeId)
  96. {
  97. AZ::EntityId graphCanvasMemberId;
  98. SceneMemberMappingRequestBus::EventResult(graphCanvasMemberId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
  99. auto insertResult = m_rootUnusedNodes.insert(graphCanvasMemberId);
  100. if (!insertResult.second)
  101. {
  102. return;
  103. }
  104. m_isDirty = true;
  105. m_unprocessedIds.insert(graphCanvasMemberId);
  106. }
  107. void UnusedNodeValidationEffect::RemoveUnusedNode(const AZ::EntityId& scriptCanvasNodeId)
  108. {
  109. AZ::EntityId graphCanvasMemberId;
  110. SceneMemberMappingRequestBus::EventResult(graphCanvasMemberId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
  111. size_t removeCount = m_rootUnusedNodes.erase(graphCanvasMemberId);
  112. if (removeCount == 0)
  113. {
  114. return;
  115. }
  116. m_isDirty = true;
  117. ClearStyleSelectors();
  118. m_unprocessedIds = m_rootUnusedNodes;
  119. m_inactiveNodes.clear();
  120. }
  121. void UnusedNodeValidationEffect::DisplayEffect(const GraphCanvas::GraphId&)
  122. {
  123. if (!m_isDirty)
  124. {
  125. return;
  126. }
  127. AZStd::unordered_set< GraphCanvas::NodeId > processedIds;
  128. m_isDirty = false;
  129. while (!m_unprocessedIds.empty())
  130. {
  131. AZ::EntityId currentMemberId = (*m_unprocessedIds.begin());
  132. m_unprocessedIds.erase(m_unprocessedIds.begin());
  133. processedIds.insert(currentMemberId);
  134. AZStd::vector< GraphCanvas::SlotId > slotIds;
  135. GraphCanvas::NodeRequestBus::EventResult(slotIds, currentMemberId, &GraphCanvas::NodeRequests::GetSlotIds);
  136. bool isFullyDisabled = true;
  137. AZStd::unordered_set< GraphCanvas::ConnectionId > connectionsToStylize;
  138. for (const auto& slotId : slotIds)
  139. {
  140. AZStd::vector< GraphCanvas::ConnectionId > connectionIds;
  141. GraphCanvas::SlotRequestBus::EventResult(connectionIds, slotId, &GraphCanvas::SlotRequests::GetConnections);
  142. GraphCanvas::ConnectionType connectionType = GraphCanvas::ConnectionType::CT_Invalid;
  143. GraphCanvas::SlotRequestBus::EventResult(connectionType, slotId, &GraphCanvas::SlotRequests::GetConnectionType);
  144. GraphCanvas::SlotType slotType = GraphCanvas::SlotTypes::DataSlot;
  145. GraphCanvas::SlotRequestBus::EventResult(slotType, slotId, &GraphCanvas::SlotRequests::GetSlotType);
  146. for (const auto& connectionId : connectionIds)
  147. {
  148. if (slotType == GraphCanvas::SlotTypes::DataSlot
  149. || connectionType == GraphCanvas::ConnectionType::CT_Output)
  150. {
  151. connectionsToStylize.insert(connectionId);
  152. }
  153. if (slotType == GraphCanvas::SlotTypes::ExecutionSlot)
  154. {
  155. if (connectionType == GraphCanvas::ConnectionType::CT_Output)
  156. {
  157. GraphCanvas::Endpoint targetEndpoint;
  158. GraphCanvas::ConnectionRequestBus::EventResult(targetEndpoint, connectionId, &GraphCanvas::ConnectionRequests::GetTargetEndpoint);
  159. if (processedIds.find(targetEndpoint.GetNodeId()) == processedIds.end())
  160. {
  161. m_unprocessedIds.insert(targetEndpoint.GetNodeId());
  162. }
  163. }
  164. else if (connectionType == GraphCanvas::ConnectionType::CT_Input)
  165. {
  166. GraphCanvas::Endpoint sourceEndpoint;
  167. GraphCanvas::ConnectionRequestBus::EventResult(sourceEndpoint, connectionId, &GraphCanvas::ConnectionRequests::GetSourceEndpoint);
  168. // If we find a node that we are unsure about its activation state.
  169. // Don't mark ourselves as fully disabled.
  170. if (m_inactiveNodes.find(sourceEndpoint.GetNodeId()) == m_inactiveNodes.end())
  171. {
  172. isFullyDisabled = false;
  173. }
  174. }
  175. }
  176. }
  177. }
  178. const char* selectorState;
  179. if (isFullyDisabled)
  180. {
  181. selectorState = UnusedSelector;
  182. m_inactiveNodes.insert(currentMemberId);
  183. }
  184. else
  185. {
  186. selectorState = UnknownUseState;
  187. }
  188. ApplySelector(currentMemberId, selectorState);
  189. for (const auto& connectionId : connectionsToStylize)
  190. {
  191. ApplySelector(connectionId, selectorState);
  192. }
  193. }
  194. }
  195. void UnusedNodeValidationEffect::CancelEffect()
  196. {
  197. // The remove node logic handles these updates
  198. return;
  199. }
  200. void UnusedNodeValidationEffect::ClearStyleSelectors()
  201. {
  202. while (!m_styleSelectors.empty())
  203. {
  204. auto mapIter = m_styleSelectors.begin();
  205. RemoveSelector(mapIter->first);
  206. }
  207. }
  208. void UnusedNodeValidationEffect::ApplySelector(const AZ::EntityId& memberId, AZStd::string_view styleSelector)
  209. {
  210. RemoveSelector(memberId);
  211. GraphCanvas::StyledEntityRequestBus::Event(memberId, &GraphCanvas::StyledEntityRequests::AddSelectorState, styleSelector.data());
  212. m_styleSelectors[memberId] = styleSelector;
  213. }
  214. void UnusedNodeValidationEffect::RemoveSelector(const AZ::EntityId& memberId)
  215. {
  216. auto mapIter = m_styleSelectors.find(memberId);
  217. if (mapIter != m_styleSelectors.end())
  218. {
  219. GraphCanvas::StyledEntityRequestBus::Event(memberId, &GraphCanvas::StyledEntityRequests::RemoveSelectorState, mapIter->second.data());
  220. m_styleSelectors.erase(mapIter);
  221. }
  222. }
  223. /////////////////////////
  224. // GraphValidationModel
  225. /////////////////////////
  226. GraphValidationModel::GraphValidationModel()
  227. : m_errorIcon(":/ScriptCanvasEditorResources/Resources/error_icon.png")
  228. , m_warningIcon(":/ScriptCanvasEditorResources/Resources/warning_symbol.png")
  229. , m_messageIcon(":/ScriptCanvasEditorResources/Resources/message_icon.png")
  230. , m_autoFixIcon(":/ScriptCanvasEditorResources/Resources/wrench_icon.png")
  231. {
  232. }
  233. GraphValidationModel::~GraphValidationModel()
  234. {
  235. m_validationResults.ClearResults();
  236. }
  237. void GraphValidationModel::RunValidation(const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
  238. {
  239. layoutAboutToBeChanged();
  240. if (scriptCanvasId.IsValid())
  241. {
  242. AZ::EBusAggregateResults<AZStd::pair<ScriptCanvas::ScriptCanvasId, ScriptCanvas::ValidationResults>> results;
  243. ScriptCanvas::ValidationRequestBus::EventResult(results, scriptCanvasId, &ScriptCanvas::ValidationRequests::GetValidationResults);
  244. for (auto r : results.values)
  245. {
  246. if (r.first == scriptCanvasId)
  247. {
  248. for (auto e : r.second.GetEvents())
  249. {
  250. m_validationResults.AddValidationEvent(e.get());
  251. }
  252. }
  253. }
  254. }
  255. layoutChanged();
  256. GraphValidatorDockWidgetNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphValidatorDockWidgetNotifications::OnResultsChanged, m_validationResults.ErrorCount(), m_validationResults.WarningCount());
  257. }
  258. void GraphValidationModel::AddEvents(ScriptCanvas::ValidationResults& validationEvents)
  259. {
  260. if (validationEvents.HasErrors() || validationEvents.HasWarnings())
  261. {
  262. layoutAboutToBeChanged();
  263. for (auto event : validationEvents.GetEvents())
  264. {
  265. m_validationResults.AddValidationEvent(event.get());
  266. }
  267. layoutChanged();
  268. }
  269. GraphValidatorDockWidgetNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphValidatorDockWidgetNotifications::OnResultsChanged, m_validationResults.ErrorCount(), m_validationResults.WarningCount());
  270. }
  271. void GraphValidationModel::Clear()
  272. {
  273. m_validationResults.ClearResults();
  274. }
  275. QModelIndex GraphValidationModel::index(int row, int column, const QModelIndex&) const
  276. {
  277. if (row < 0 || row >= m_validationResults.GetEvents().size())
  278. {
  279. return QModelIndex();
  280. }
  281. return createIndex(row, column, const_cast<ScriptCanvas::ValidationEvent*>(FindItemForRow(row)));
  282. }
  283. QModelIndex GraphValidationModel::parent(const QModelIndex&) const
  284. {
  285. return QModelIndex();
  286. }
  287. int GraphValidationModel::columnCount(const QModelIndex& ) const
  288. {
  289. return ColumnIndex::Count;
  290. }
  291. int GraphValidationModel::rowCount(const QModelIndex&) const
  292. {
  293. return static_cast<int>(m_validationResults.GetEvents().size());
  294. }
  295. QVariant GraphValidationModel::headerData(int section, Qt::Orientation orientation, int role) const
  296. {
  297. if (orientation == Qt::Orientation::Vertical)
  298. {
  299. return QVariant();
  300. }
  301. if (role == Qt::DisplayRole)
  302. {
  303. switch (section)
  304. {
  305. case ColumnIndex::Description:
  306. {
  307. return QString("Description");
  308. }
  309. break;
  310. default:
  311. break;
  312. }
  313. }
  314. return QVariant();
  315. }
  316. QVariant GraphValidationModel::data(const QModelIndex& index, int role) const
  317. {
  318. const ScriptCanvas::ValidationEvent* validationEvent = FindItemForIndex(index);
  319. if (role == Qt::DisplayRole)
  320. {
  321. switch (index.column())
  322. {
  323. case ColumnIndex::Description:
  324. {
  325. return QString(validationEvent->GetDescription().data());
  326. }
  327. break;
  328. default:
  329. break;
  330. }
  331. }
  332. else if (role == Qt::DecorationRole)
  333. {
  334. switch (index.column())
  335. {
  336. // We always want the icon on the leftmost column. So doing away with my usual
  337. // Labelling to keep the spirit of what I'm after(simple table re-ordering).
  338. case 0:
  339. {
  340. switch (validationEvent->GetSeverity())
  341. {
  342. case ScriptCanvas::ValidationSeverity::Error:
  343. {
  344. return m_errorIcon;
  345. }
  346. break;
  347. case ScriptCanvas::ValidationSeverity::Warning:
  348. {
  349. return m_warningIcon;
  350. }
  351. break;
  352. case ScriptCanvas::ValidationSeverity::Informative:
  353. {
  354. return m_messageIcon;
  355. }
  356. break;
  357. default:
  358. break;
  359. }
  360. }
  361. break;
  362. case ColumnIndex::AutoFix:
  363. {
  364. if (validationEvent->CanAutoFix())
  365. {
  366. return m_autoFixIcon;
  367. }
  368. }
  369. break;
  370. default:
  371. break;
  372. }
  373. }
  374. else if (role == Qt::ToolTipRole)
  375. {
  376. switch (index.column())
  377. {
  378. case ColumnIndex::Description:
  379. {
  380. return QString("%1 - %2").arg(validationEvent->GetIdentifier().c_str()).arg(validationEvent->GetTooltip().data());
  381. }
  382. case ColumnIndex::AutoFix:
  383. {
  384. if (validationEvent->CanAutoFix())
  385. {
  386. return "A potential automatic fix can be applied for this issue. Press this button to fix the error.";
  387. }
  388. }
  389. }
  390. }
  391. return QVariant();
  392. }
  393. const ScriptCanvas::ValidationEvent* GraphValidationModel::FindItemForIndex(const QModelIndex& index) const
  394. {
  395. if (index.isValid())
  396. {
  397. return FindItemForRow(index.row());
  398. }
  399. return nullptr;
  400. }
  401. const ScriptCanvas::ValidationEvent* GraphValidationModel::FindItemForRow(int row) const
  402. {
  403. const auto& validationEvents = m_validationResults.GetEvents();
  404. if (row < 0 || row >= validationEvents.size())
  405. {
  406. return nullptr;
  407. }
  408. return validationEvents[row].get();
  409. }
  410. const ScriptCanvas::ValidationResults& GraphValidationModel::GetValidationResults() const
  411. {
  412. return m_validationResults;
  413. }
  414. ////////////////////////////////////////
  415. // GraphValidationSortFilterProxyModel
  416. ////////////////////////////////////////
  417. GraphValidationSortFilterProxyModel::GraphValidationSortFilterProxyModel()
  418. : m_severityFilter(ScriptCanvas::ValidationSeverity::Unknown)
  419. {
  420. // TODO: Populate the errors from the user settings
  421. }
  422. bool GraphValidationSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
  423. {
  424. QAbstractItemModel* model = sourceModel();
  425. QModelIndex index = model->index(sourceRow, 0, sourceParent);
  426. const ScriptCanvas::ValidationEvent* currentItem = static_cast<const ScriptCanvas::ValidationEvent*>(index.internalPointer());
  427. // If our filter is set to all, we can just show the message
  428. bool showRow = ((m_severityFilter == ScriptCanvas::ValidationSeverity::Unknown) || (currentItem->GetSeverity() == m_severityFilter));
  429. if (showRow && !m_filter.isEmpty())
  430. {
  431. AZStd::string_view descriptionView = currentItem->GetDescription();
  432. QString description = QString::fromUtf8(descriptionView.data(), static_cast<int>(descriptionView.size()));
  433. if (description.lastIndexOf(m_regex) < 0)
  434. {
  435. QString errorId = QString(currentItem->GetIdentifier().c_str());
  436. if (errorId.lastIndexOf(m_regex) < 0)
  437. {
  438. showRow = false;
  439. }
  440. }
  441. }
  442. return showRow;
  443. }
  444. void GraphValidationSortFilterProxyModel::SetFilter(const QString& filterString)
  445. {
  446. QString escapedString = QRegExp::escape(filterString);
  447. if (m_filter != escapedString)
  448. {
  449. m_filter = escapedString;
  450. m_regex = QRegExp(m_filter, Qt::CaseInsensitive);
  451. invalidateFilter();
  452. }
  453. }
  454. void GraphValidationSortFilterProxyModel::SetSeverityFilter(ScriptCanvas::ValidationSeverity severityFilter)
  455. {
  456. if (m_severityFilter != severityFilter)
  457. {
  458. m_severityFilter = severityFilter;
  459. invalidateFilter();
  460. }
  461. }
  462. ScriptCanvas::ValidationSeverity GraphValidationSortFilterProxyModel::GetSeverityFilter() const
  463. {
  464. return m_severityFilter;
  465. }
  466. bool GraphValidationSortFilterProxyModel::IsShowingErrors()
  467. {
  468. return m_severityFilter == ScriptCanvas::ValidationSeverity::Unknown || m_severityFilter == ScriptCanvas::ValidationSeverity::Error;
  469. }
  470. bool GraphValidationSortFilterProxyModel::IsShowingWarnings()
  471. {
  472. return m_severityFilter == ScriptCanvas::ValidationSeverity::Unknown || m_severityFilter == ScriptCanvas::ValidationSeverity::Warning;
  473. }
  474. //////////////////////////////
  475. // GraphValidationDockWidget
  476. //////////////////////////////
  477. GraphValidationDockWidget::GraphValidationDockWidget(QWidget* parent /*= nullptr*/)
  478. : AzQtComponents::StyledDockWidget(parent)
  479. , ui(new Ui::GraphValidationPanel())
  480. , m_proxyModel(aznew GraphValidationSortFilterProxyModel())
  481. {
  482. ui->setupUi(this);
  483. m_proxyModel->setSourceModel(aznew GraphValidationModel());
  484. ui->statusTableView->setModel(m_proxyModel);
  485. ui->statusTableView->horizontalHeader()->setStretchLastSection(false);
  486. ui->statusTableView->horizontalHeader()->setSectionResizeMode(GraphValidationModel::Description, QHeaderView::ResizeMode::Stretch);
  487. ui->statusTableView->horizontalHeader()->setSectionResizeMode(GraphValidationModel::AutoFix, QHeaderView::ResizeMode::Fixed);
  488. ui->statusTableView->horizontalHeader()->resizeSection(GraphValidationModel::AutoFix, 32);
  489. ui->searchWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
  490. QButtonGroup* buttonGroup = new QButtonGroup(this);
  491. buttonGroup->setExclusive(true);
  492. buttonGroup->addButton(ui->allFilter);
  493. buttonGroup->addButton(ui->errorOnlyFilter);
  494. buttonGroup->addButton(ui->warningOnlyFilter);
  495. ui->allFilter->setChecked(true);
  496. QObject::connect(ui->allFilter, &QPushButton::clicked, this, &GraphValidationDockWidget::OnSeverityFilterChanged);
  497. QObject::connect(ui->errorOnlyFilter, &QPushButton::clicked, this, &GraphValidationDockWidget::OnSeverityFilterChanged);
  498. QObject::connect(ui->warningOnlyFilter, &QPushButton::clicked, this, &GraphValidationDockWidget::OnSeverityFilterChanged);
  499. QObject::connect(ui->runValidation, &QToolButton::clicked, this, &GraphValidationDockWidget::OnRunValidator);
  500. QObject::connect(ui->fixSelected, &QPushButton::clicked, this, &GraphValidationDockWidget::FixSelected);
  501. QObject::connect(ui->statusTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &GraphValidationDockWidget::OnTableSelectionChanged);
  502. QObject::connect(ui->statusTableView, &QTableView::doubleClicked, this, &GraphValidationDockWidget::FocusOnEvent);
  503. QObject::connect(ui->statusTableView, &QTableView::clicked, this, &GraphValidationDockWidget::TryAutoFixEvent);
  504. QObject::connect(ui->searchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this, &GraphValidationDockWidget::OnFilterChanged);
  505. GraphCanvas::AssetEditorNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
  506. ui->runValidation->setEnabled(false);
  507. ui->fixSelected->setEnabled(false);
  508. ui->fixSelected->setVisible(false);
  509. UpdateText();
  510. UpdateSelectedText();
  511. }
  512. GraphValidationDockWidget::~GraphValidationDockWidget()
  513. {
  514. GraphCanvas::AssetEditorNotificationBus::Handler::BusDisconnect();
  515. }
  516. void GraphValidationDockWidget::OnActiveGraphChanged(const GraphCanvas::GraphId& graphCanvasGraphId)
  517. {
  518. if (graphCanvasGraphId == m_activeGraphIds.graphCanvasId)
  519. {
  520. // No change
  521. return;
  522. }
  523. OnToastDismissed();
  524. if (graphCanvasGraphId.IsValid())
  525. {
  526. ScriptCanvas::ScriptCanvasId scriptCanvasId;
  527. GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
  528. if (m_models.find(graphCanvasGraphId) == m_models.end())
  529. {
  530. // We have not created a model for this graph yet.
  531. m_models[graphCanvasGraphId] = AZStd::make_pair(scriptCanvasId, AZStd::unique_ptr<ValidationData>(aznew ValidationData(graphCanvasGraphId, scriptCanvasId)));
  532. }
  533. m_activeGraphIds.graphCanvasId = graphCanvasGraphId;
  534. m_activeGraphIds.scriptCanvasId = scriptCanvasId;
  535. }
  536. else
  537. {
  538. return;
  539. }
  540. if (ValidationData* valdata = m_models[m_activeGraphIds.graphCanvasId].second.get())
  541. {
  542. m_proxyModel->setSourceModel(valdata->GetModel());
  543. Refresh();
  544. GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
  545. GraphCanvas::SceneNotificationBus::Handler::BusConnect(m_activeGraphIds.graphCanvasId);
  546. ui->statusTableView->clearSelection();
  547. }
  548. }
  549. void GraphValidationDockWidget::Refresh()
  550. {
  551. ui->statusTableView->clearSelection();
  552. UpdateText();
  553. ui->runValidation->setEnabled(GetActiveData().first.IsValid());
  554. }
  555. const ScriptCanvasEditor::GraphValidationModel* GraphValidationDockWidget::GetActiveModel() const
  556. {
  557. if (m_models.contains(m_activeGraphIds.graphCanvasId))
  558. {
  559. const auto it = m_models.find(m_activeGraphIds.graphCanvasId);
  560. const GraphModelPair& pair = const_cast<const GraphModelPair&>(it->second);
  561. return pair.second->GetModel();
  562. }
  563. return nullptr;
  564. }
  565. ScriptCanvasEditor::GraphValidationDockWidget::GraphModelPair& GraphValidationDockWidget::GetActiveData()
  566. {
  567. auto iter = m_models.find(m_activeGraphIds.graphCanvasId);
  568. if (iter != m_models.end())
  569. {
  570. return iter->second;
  571. }
  572. static GraphModelPair invalidPair = AZStd::make_pair(AZ::EntityId(), nullptr);
  573. return invalidPair;
  574. }
  575. void GraphValidationDockWidget::OnSelectionChanged()
  576. {
  577. ui->statusTableView->clearSelection();
  578. }
  579. void GraphValidationDockWidget::OnConnectionDragBegin()
  580. {
  581. ui->statusTableView->clearSelection();
  582. }
  583. bool GraphValidationDockWidget::HasValidationIssues() const
  584. {
  585. const auto model = GetActiveModel();
  586. return model ? model->rowCount() > 0 : false;
  587. }
  588. void GraphValidationDockWidget::OnRunValidator(bool displayAsNotification)
  589. {
  590. ui->statusTableView->clearSelection();
  591. if (auto model = GetActiveData().second ? GetActiveData().second->GetModel() : nullptr)
  592. {
  593. model->Clear();
  594. model->RunValidation(m_activeGraphIds.scriptCanvasId);
  595. }
  596. ui->allFilter->click();
  597. UpdateText();
  598. if (!displayAsNotification)
  599. {
  600. ui->statusTableView->selectAll();
  601. }
  602. else if (HasValidationIssues())
  603. {
  604. GetActiveData().second->DisplayToast();
  605. }
  606. }
  607. void GraphValidationDockWidget::OnShowErrors()
  608. {
  609. ui->errorOnlyFilter->setChecked(true);
  610. OnSeverityFilterChanged();
  611. }
  612. void GraphValidationDockWidget::OnShowWarnings()
  613. {
  614. ui->warningOnlyFilter->setChecked(true);
  615. OnSeverityFilterChanged();
  616. }
  617. void GraphValidationDockWidget::OnTableSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
  618. {
  619. // Handle the deselection cases correctly
  620. for (auto modelIndex : deselected.indexes())
  621. {
  622. OnRowDeselected(m_proxyModel->mapToSource(modelIndex).row());
  623. }
  624. ui->fixSelected->setEnabled(false);
  625. // We want everything to be in sync visually.
  626. // So fake reselect everything to restart everything
  627. if (!selected.empty())
  628. {
  629. for (auto modelIndex : ui->statusTableView->selectionModel()->selectedIndexes())
  630. {
  631. if (modelIndex.column() == 0)
  632. {
  633. QModelIndex sourceIndex = m_proxyModel->mapToSource(modelIndex);
  634. if (auto model = GetActiveModel())
  635. {
  636. const ScriptCanvas::ValidationEvent* validationEvent = model->FindItemForIndex(sourceIndex);
  637. if (validationEvent && validationEvent->CanAutoFix())
  638. {
  639. ui->fixSelected->setEnabled(true);
  640. }
  641. OnRowSelected(sourceIndex.row());
  642. }
  643. }
  644. }
  645. m_unusedNodeValidationEffect.DisplayEffect(m_activeGraphIds.graphCanvasId);
  646. }
  647. }
  648. void GraphValidationDockWidget::FocusOnEvent(const QModelIndex& modelIndex)
  649. {
  650. if (auto model = GetActiveModel())
  651. {
  652. const ScriptCanvas::ValidationEvent* validationEvent = model->FindItemForIndex(m_proxyModel->mapToSource(modelIndex));
  653. AZ::EntityId graphCanvasMemberId;
  654. if (const ScriptCanvas::FocusOnEntityEffect* focusOnEntityEffect = azrtti_cast<const ScriptCanvas::FocusOnEntityEffect*>(validationEvent))
  655. {
  656. const AZ::EntityId& scriptCanvasId = focusOnEntityEffect->GetFocusTarget();
  657. SceneMemberMappingRequestBus::EventResult(graphCanvasMemberId, scriptCanvasId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
  658. }
  659. if (graphCanvasMemberId.IsValid())
  660. {
  661. GraphCanvas::FocusConfig focusConfig;
  662. if (GraphCanvas::GraphUtils::IsNodeGroup(graphCanvasMemberId))
  663. {
  664. focusConfig.m_spacingType = GraphCanvas::FocusConfig::SpacingType::GridStep;
  665. focusConfig.m_spacingAmount = 1;
  666. }
  667. else
  668. {
  669. focusConfig.m_spacingType = GraphCanvas::FocusConfig::SpacingType::Scalar;
  670. focusConfig.m_spacingAmount = 2;
  671. }
  672. AZStd::vector<AZ::EntityId> memberIds = { graphCanvasMemberId };
  673. GraphCanvas::GraphUtils::FocusOnElements(memberIds, focusConfig);
  674. }
  675. }
  676. }
  677. void GraphValidationDockWidget::TryAutoFixEvent(const QModelIndex& modelIndex)
  678. {
  679. if (modelIndex.column() != GraphValidationModel::ColumnIndex::AutoFix)
  680. {
  681. return;
  682. }
  683. if (auto model = GetActiveModel())
  684. {
  685. const ScriptCanvas::ValidationEvent* validationEvent = model->FindItemForIndex(m_proxyModel->mapToSource(modelIndex));
  686. if (!validationEvent->CanAutoFix())
  687. {
  688. return;
  689. }
  690. AutoFixEvent(validationEvent);
  691. }
  692. OnRunValidator();
  693. }
  694. void GraphValidationDockWidget::FixSelected()
  695. {
  696. {
  697. GraphCanvas::ScopedGraphUndoBlocker undoBlocker(m_activeGraphIds.graphCanvasId);
  698. for (auto modelIndex : ui->statusTableView->selectionModel()->selectedIndexes())
  699. {
  700. if (modelIndex.column() == 0)
  701. {
  702. QModelIndex sourceIndex = m_proxyModel->mapToSource(modelIndex);
  703. if (auto model = GetActiveModel())
  704. {
  705. const ScriptCanvas::ValidationEvent* validationEvent = model->FindItemForIndex(sourceIndex);
  706. if (validationEvent->CanAutoFix())
  707. {
  708. AutoFixEvent(validationEvent);
  709. }
  710. }
  711. }
  712. }
  713. }
  714. GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_activeGraphIds.scriptCanvasId);
  715. OnRunValidator();
  716. }
  717. void GraphValidationDockWidget::OnSeverityFilterChanged()
  718. {
  719. if (ui->allFilter->isChecked())
  720. {
  721. // Using unknown as a proxy for all
  722. m_proxyModel->SetSeverityFilter(ScriptCanvas::ValidationSeverity::Unknown);
  723. }
  724. else if (ui->errorOnlyFilter->isChecked())
  725. {
  726. m_proxyModel->SetSeverityFilter(ScriptCanvas::ValidationSeverity::Error);
  727. }
  728. else if (ui->warningOnlyFilter->isChecked())
  729. {
  730. m_proxyModel->SetSeverityFilter(ScriptCanvas::ValidationSeverity::Warning);
  731. }
  732. UpdateText();
  733. }
  734. void GraphValidationDockWidget::OnFilterChanged(const QString& filterString)
  735. {
  736. m_proxyModel->SetFilter(filterString);
  737. }
  738. void GraphValidationDockWidget::AutoFixEvent(const ScriptCanvas::ValidationEvent* validationEvent)
  739. {
  740. if (validationEvent->GetIdCrc() == ScriptCanvas::DataValidationIds::ScopedDataConnectionCrc)
  741. {
  742. AutoFixScopedDataConnection(static_cast<const ScriptCanvas::ScopedDataConnectionEvent*>(validationEvent));
  743. }
  744. else if (validationEvent->GetIdCrc() == ScriptCanvas::DataValidationIds::InvalidVariableTypeCrc)
  745. {
  746. AutoFixDeleteInvalidVariables(static_cast<const ScriptCanvas::InvalidVariableTypeEvent*>(validationEvent));
  747. }
  748. else if (validationEvent->GetIdCrc() == ScriptCanvas::DataValidationIds::ScriptEventVersionMismatchCrc)
  749. {
  750. AutoFixScriptEventVersionMismatch(static_cast<const ScriptCanvas::ScriptEventVersionMismatch*>(validationEvent));
  751. }
  752. else
  753. {
  754. AZ_Error("ScriptCanvas", false, "Cannot auto fix event type %s despite it being marked at auto fixable", validationEvent->GetIdentifier().c_str());
  755. }
  756. }
  757. void GraphValidationDockWidget::AutoFixScriptEventVersionMismatch(const ScriptCanvas::ScriptEventVersionMismatch* scriptEventMismatchEvent)
  758. {
  759. {
  760. GraphCanvas::ScopedGraphUndoBlocker undoBlocker(m_activeGraphIds.graphCanvasId);
  761. AZ::EntityId graphCanvasId;
  762. SceneMemberMappingRequestBus::EventResult(graphCanvasId, scriptEventMismatchEvent->GetNodeId(), &SceneMemberMappingRequests::GetGraphCanvasEntityId);
  763. // Detach all connections
  764. GraphCanvas::GraphUtils::DetachNodeAndStitchConnections(graphCanvasId);
  765. // TODO #lsempe:
  766. // Notify the node to update to its latest version
  767. //EditorGraphRequestBus::Event(m_scriptCanvasGraphId, &EditorGraphRequests::UpdateScriptEventVersion, scriptEventMismatchEvent->GetNodeId());
  768. }
  769. GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_activeGraphIds.scriptCanvasId);
  770. }
  771. void GraphValidationDockWidget::AutoFixDeleteInvalidVariables(const ScriptCanvas::InvalidVariableTypeEvent* invalidVariableEvent)
  772. {
  773. {
  774. GraphCanvas::ScopedGraphUndoBlocker undoBlocker(m_activeGraphIds.graphCanvasId);
  775. AZStd::vector<NodeIdPair> variableNodes;
  776. EditorGraphRequestBus::EventResult(variableNodes, m_activeGraphIds.scriptCanvasId, &EditorGraphRequests::GetVariableNodes, invalidVariableEvent->GetVariableId());
  777. for (auto& variableNode : variableNodes)
  778. {
  779. GraphCanvas::GraphUtils::DetachNodeAndStitchConnections(variableNode.m_graphCanvasId);
  780. }
  781. ScriptCanvas::GraphVariableManagerRequestBus::Event(m_activeGraphIds.scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::RemoveVariable, invalidVariableEvent->GetVariableId());
  782. }
  783. GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_activeGraphIds.scriptCanvasId);
  784. }
  785. void GraphValidationDockWidget::AutoFixScopedDataConnection(const ScriptCanvas::ScopedDataConnectionEvent* connectionEvent)
  786. {
  787. AZStd::unordered_set< AZ::EntityId > createdNodes;
  788. {
  789. GraphCanvas::ScopedGraphUndoBlocker undoBlocker(m_activeGraphIds.graphCanvasId);
  790. const AZ::EntityId& scriptCanvasConnectionId = connectionEvent->GetConnectionId();
  791. // Information gathering step
  792. ScriptCanvas::Endpoint scriptCanvasSourceEndpoint;
  793. ScriptCanvas::ConnectionRequestBus::EventResult(scriptCanvasSourceEndpoint, scriptCanvasConnectionId, &ScriptCanvas::ConnectionRequests::GetSourceEndpoint);
  794. // Going to match the visual expectation here, and always have it create a new variable and store the value
  795. // at this point in time.
  796. ScriptCanvas::VariableId targetVariableId;
  797. ScriptCanvas::Data::Type variableType = ScriptCanvas::Data::Type::Invalid();
  798. ScriptCanvas::NodeRequestBus::EventResult(variableType, scriptCanvasSourceEndpoint.GetNodeId(), &ScriptCanvas::NodeRequests::GetSlotDataType, scriptCanvasSourceEndpoint.GetSlotId());
  799. if (!variableType.IsValid())
  800. {
  801. AZ_Error("ScriptCanvas", false, "Could not auto fix latent connection(%s) because connection did not return a valid data type.", scriptCanvasConnectionId.ToString().c_str());
  802. return;
  803. }
  804. const AZStd::string& varName = VariableDockWidget::FindDefaultVariableName(m_activeGraphIds.scriptCanvasId);
  805. ScriptCanvas::Datum datum(variableType, ScriptCanvas::Datum::eOriginality::Original);
  806. AZ::Outcome<ScriptCanvas::VariableId, AZStd::string> outcome = AZ::Failure(AZStd::string());
  807. ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_activeGraphIds.scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, varName, datum, false);
  808. if (outcome.IsSuccess())
  809. {
  810. targetVariableId = outcome.GetValue();
  811. }
  812. else
  813. {
  814. AZ_Error("ScriptCanvas", false, "Could not auto fix latent connection(%s) because variable creation failed with the message: %s", scriptCanvasConnectionId.ToString().c_str(), outcome.GetError().c_str());
  815. return;
  816. }
  817. // Convert elements over to GraphCanvas to begin interactions with the visual front end.
  818. GraphCanvas::ConnectionId graphCanvasConnectionId;
  819. SceneMemberMappingRequestBus::EventResult(graphCanvasConnectionId, scriptCanvasConnectionId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
  820. GraphCanvas::Endpoint sourceEndpoint;
  821. GraphCanvas::ConnectionRequestBus::EventResult(sourceEndpoint, graphCanvasConnectionId, &GraphCanvas::ConnectionRequests::GetSourceEndpoint);
  822. GraphCanvas::Endpoint targetEndpoint;
  823. GraphCanvas::ConnectionRequestBus::EventResult(targetEndpoint, graphCanvasConnectionId, &GraphCanvas::ConnectionRequests::GetTargetEndpoint);
  824. AZ::EntityId gridId;
  825. GraphCanvas::SceneRequestBus::EventResult(gridId, m_activeGraphIds.graphCanvasId, &GraphCanvas::SceneRequests::GetGrid);
  826. AZ::Vector2 gridStep(0,0);
  827. GraphCanvas::GridRequestBus::EventResult(gridStep, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
  828. AZStd::unordered_set< AZ::EntityId > deletedMemberIds;
  829. deletedMemberIds.insert(graphCanvasConnectionId);
  830. // Inserting the Set into the execution flow
  831. {
  832. // Map of all of the execution outs on the latent node to Endpoints.
  833. AZStd::unordered_multimap< GraphCanvas::Endpoint, GraphCanvas::ConnectionId > sourceExecutionMapping;
  834. AZStd::vector< GraphCanvas::SlotId > slotIds;
  835. GraphCanvas::NodeRequestBus::EventResult(slotIds, sourceEndpoint.GetNodeId(), &GraphCanvas::NodeRequests::FindVisibleSlotIdsByType, GraphCanvas::ConnectionType::CT_Output, GraphCanvas::SlotTypes::ExecutionSlot);
  836. for (const GraphCanvas::SlotId& slotId : slotIds)
  837. {
  838. AZStd::vector< GraphCanvas::ConnectionId > connectionIds;
  839. GraphCanvas::SlotRequestBus::EventResult(connectionIds, slotId, &GraphCanvas::SlotRequests::GetConnections);
  840. GraphCanvas::Endpoint executionSource(sourceEndpoint.GetNodeId(), slotId);
  841. for (const GraphCanvas::ConnectionId& connectionId : connectionIds)
  842. {
  843. sourceExecutionMapping.insert(AZStd::make_pair(executionSource, connectionId));
  844. }
  845. }
  846. if (!sourceExecutionMapping.empty())
  847. {
  848. AZ::Vector2 position;
  849. GraphCanvas::Endpoint lastEndpoint;
  850. AZ::EntityId setVariableGraphCanvasId;
  851. AZStd::vector< GraphCanvas::Endpoint> dataEndpoints;
  852. dataEndpoints.push_back(sourceEndpoint);
  853. GraphCanvas::CreateConnectionsBetweenConfig connectionConfig;
  854. connectionConfig.m_connectionType = GraphCanvas::CreateConnectionsBetweenConfig::CreationType::SingleConnection;
  855. for (const auto& sourcePair : sourceExecutionMapping)
  856. {
  857. const GraphCanvas::Endpoint& executionSourceEndpoint = sourcePair.first;
  858. const GraphCanvas::ConnectionId& executionTargetConnectionId = sourcePair.second;
  859. if (lastEndpoint != executionSourceEndpoint)
  860. {
  861. if (!lastEndpoint.IsValid())
  862. {
  863. QGraphicsItem* sourceItem;
  864. GraphCanvas::SceneMemberUIRequestBus::EventResult(sourceItem, sourceEndpoint.GetNodeId(), &GraphCanvas::SceneMemberUIRequests::GetRootGraphicsItem);
  865. if (sourceItem)
  866. {
  867. QRectF sourceBoundingRect = sourceItem->sceneBoundingRect();
  868. position.SetX(aznumeric_cast<float>(sourceBoundingRect.right() + gridStep.GetX()));
  869. position.SetY(aznumeric_cast<float>(sourceBoundingRect.top()));
  870. }
  871. }
  872. NodeIdPair createdNodePair = Nodes::CreateSetVariableNode(targetVariableId, m_activeGraphIds.scriptCanvasId);
  873. setVariableGraphCanvasId = createdNodePair.m_graphCanvasId;
  874. GraphCanvas::SceneRequestBus::Event(m_activeGraphIds.graphCanvasId, &GraphCanvas::SceneRequests::AddNode, setVariableGraphCanvasId, position, false);
  875. createdNodes.insert(setVariableGraphCanvasId);
  876. position += gridStep;
  877. connectionConfig.m_createdConnections.clear();
  878. GraphCanvas::GraphUtils::CreateConnectionsBetween(dataEndpoints, setVariableGraphCanvasId, connectionConfig);
  879. lastEndpoint = executionSourceEndpoint;
  880. }
  881. GraphCanvas::ConnectionSpliceConfig spliceConfig;
  882. spliceConfig.m_allowOpportunisticConnections = false;
  883. GraphCanvas::GraphUtils::SpliceNodeOntoConnection(setVariableGraphCanvasId, executionTargetConnectionId, spliceConfig);
  884. }
  885. }
  886. else
  887. {
  888. NodeIdPair setVariableNodeIdPair = Nodes::CreateSetVariableNode(targetVariableId, m_activeGraphIds.scriptCanvasId);
  889. createdNodes.insert(setVariableNodeIdPair.m_graphCanvasId);
  890. QRectF sourceBoundingRect;
  891. QGraphicsItem* graphicsItem = nullptr;
  892. GraphCanvas::SceneMemberUIRequestBus::EventResult(graphicsItem, sourceEndpoint.GetNodeId(), &GraphCanvas::SceneMemberUIRequests::GetRootGraphicsItem);
  893. if (graphicsItem)
  894. {
  895. sourceBoundingRect = graphicsItem->sceneBoundingRect();
  896. }
  897. AZ::Vector2 position = AZ::Vector2(aznumeric_cast<float>(sourceBoundingRect.right() + gridStep.GetX()), aznumeric_cast<float>(sourceBoundingRect.top()));
  898. GraphCanvas::SceneRequestBus::Event(m_activeGraphIds.graphCanvasId, &GraphCanvas::SceneRequests::AddNode, setVariableNodeIdPair.m_graphCanvasId, position, false);
  899. AZStd::vector< GraphCanvas::Endpoint> endpoints;
  900. endpoints.reserve(slotIds.size() + 1);
  901. endpoints.emplace_back(sourceEndpoint);
  902. for (const GraphCanvas::SlotId& slotId : slotIds)
  903. {
  904. endpoints.emplace_back(sourceEndpoint.GetNodeId(), slotId);
  905. }
  906. GraphCanvas::CreateConnectionsBetweenConfig connectionConfig;
  907. connectionConfig.m_connectionType = GraphCanvas::CreateConnectionsBetweenConfig::CreationType::FullyConnected;
  908. GraphCanvas::GraphUtils::CreateConnectionsBetween(endpoints, setVariableNodeIdPair.m_graphCanvasId, connectionConfig);
  909. }
  910. }
  911. // Inserting the get into the execution flow
  912. {
  913. NodeIdPair getVariableNodeIdPair = Nodes::CreateGetVariableNode(targetVariableId, m_activeGraphIds.scriptCanvasId);
  914. createdNodes.insert(getVariableNodeIdPair.m_graphCanvasId);
  915. QRectF targetBoundingRect;
  916. QGraphicsItem* graphicsItem = nullptr;
  917. GraphCanvas::SceneMemberUIRequestBus::EventResult(graphicsItem, targetEndpoint.GetNodeId(), &GraphCanvas::SceneMemberUIRequests::GetRootGraphicsItem);
  918. if (graphicsItem)
  919. {
  920. targetBoundingRect = graphicsItem->sceneBoundingRect();
  921. }
  922. AZ::Vector2 position = AZ::Vector2(aznumeric_cast<float>(targetBoundingRect.left() - gridStep.GetX()), aznumeric_cast<float>(targetBoundingRect.top()));
  923. QGraphicsItem* newGraphicsItem = nullptr;
  924. GraphCanvas::SceneMemberUIRequestBus::EventResult(newGraphicsItem, getVariableNodeIdPair.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::GetRootGraphicsItem);
  925. if (newGraphicsItem)
  926. {
  927. position.SetX(aznumeric_cast<float>(position.GetX() - newGraphicsItem->sceneBoundingRect().width()));
  928. }
  929. GraphCanvas::SceneRequestBus::Event(m_activeGraphIds.graphCanvasId, &GraphCanvas::SceneRequests::AddNode, getVariableNodeIdPair.m_graphCanvasId, position, false);
  930. AZStd::vector< GraphCanvas::SlotId > slotIds;
  931. GraphCanvas::NodeRequestBus::EventResult(slotIds, targetEndpoint.GetNodeId(), &GraphCanvas::NodeRequests::FindVisibleSlotIdsByType, GraphCanvas::ConnectionType::CT_Input, GraphCanvas::SlotTypes::ExecutionSlot);
  932. AZStd::vector< GraphCanvas::Endpoint > executionSourceEndpoints;
  933. AZStd::vector< GraphCanvas::Endpoint > validTargetEndpoints;
  934. validTargetEndpoints.push_back(targetEndpoint);
  935. for (const GraphCanvas::SlotId& slotId : slotIds)
  936. {
  937. AZStd::vector< GraphCanvas::ConnectionId > connectionIds;
  938. GraphCanvas::SlotRequestBus::EventResult(connectionIds, slotId, &GraphCanvas::SlotRequests::GetConnections);
  939. validTargetEndpoints.emplace_back(targetEndpoint.GetNodeId(), slotId);
  940. for (const GraphCanvas::ConnectionId& connectionId : connectionIds)
  941. {
  942. GraphCanvas::Endpoint targetExecutionSourceEndpoint;
  943. GraphCanvas::ConnectionRequestBus::EventResult(targetExecutionSourceEndpoint, connectionId, &GraphCanvas::ConnectionRequests::GetSourceEndpoint);
  944. executionSourceEndpoints.push_back(targetExecutionSourceEndpoint);
  945. deletedMemberIds.insert(connectionId);
  946. }
  947. }
  948. // Hook up all of the connection inputs
  949. if (!executionSourceEndpoints.empty())
  950. {
  951. GraphCanvas::CreateConnectionsBetweenConfig config;
  952. config.m_connectionType = GraphCanvas::CreateConnectionsBetweenConfig::CreationType::FullyConnected;
  953. GraphCanvas::GraphUtils::CreateConnectionsBetween(executionSourceEndpoints, getVariableNodeIdPair.m_graphCanvasId, config);
  954. }
  955. // Hook up to the actual target endpoints
  956. GraphCanvas::CreateConnectionsBetweenConfig config;
  957. config.m_connectionType = GraphCanvas::CreateConnectionsBetweenConfig::CreationType::SinglePass;
  958. GraphCanvas::GraphUtils::CreateConnectionsBetween(validTargetEndpoints, getVariableNodeIdPair.m_graphCanvasId, config);
  959. }
  960. GraphCanvas::SceneRequestBus::Event(m_activeGraphIds.graphCanvasId, &GraphCanvas::SceneRequests::Delete, deletedMemberIds);
  961. }
  962. GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_activeGraphIds.scriptCanvasId);
  963. GraphCanvas::NodeNudgingController nudgingController;
  964. nudgingController.SetGraphId(m_activeGraphIds.graphCanvasId);
  965. nudgingController.StartNudging(createdNodes);
  966. nudgingController.FinalizeNudging();
  967. }
  968. void GraphValidationDockWidget::UpdateText()
  969. {
  970. int errorCount = 0;
  971. int warningCount = 0;
  972. auto& tabdata = GetActiveData();
  973. if (tabdata.first.IsValid())
  974. {
  975. if (auto model = GetActiveModel())
  976. {
  977. // Clear our the text Filter
  978. ui->searchWidget->SetTextFilter("");
  979. m_proxyModel->SetFilter("");
  980. errorCount = model->GetValidationResults().ErrorCount();
  981. warningCount = model->GetValidationResults().WarningCount();
  982. }
  983. }
  984. ui->errorOnlyFilter->setText(QString("%1 Errors").arg(errorCount));
  985. ui->warningOnlyFilter->setText(QString("%1 Warnings").arg(warningCount));
  986. }
  987. void GraphValidationDockWidget::OnRowSelected(int row)
  988. {
  989. // If we already have an effect on this row, restart it to maintain visual consistency of the glows.
  990. if (auto effect = GetActiveData().second->GetEffect(row))
  991. {
  992. effect->CancelEffect();
  993. effect->DisplayEffect(m_activeGraphIds.graphCanvasId);
  994. return;
  995. }
  996. if (auto model = GetActiveModel())
  997. {
  998. const ScriptCanvas::ValidationEvent* validationEvent = model->FindItemForRow(row);
  999. if (const ScriptCanvas::HighlightEntityEffect* highlightEntity = azrtti_cast<const ScriptCanvas::HighlightEntityEffect*>(validationEvent))
  1000. {
  1001. HighlightElementValidationEffect* highlightEffect = aznew HighlightElementValidationEffect();
  1002. highlightEffect->AddTarget(highlightEntity->GetHighlightTarget());
  1003. highlightEffect->DisplayEffect(m_activeGraphIds.graphCanvasId);
  1004. GetActiveData().second->SetEffect(row, highlightEffect);
  1005. }
  1006. if (const ScriptCanvas::HighlightVariableEffect* highlightvariable = azrtti_cast<const ScriptCanvas::HighlightVariableEffect*>(validationEvent))
  1007. {
  1008. HighlightElementValidationEffect* highlightEffect = aznew HighlightElementValidationEffect();
  1009. AZStd::vector<NodeIdPair> variableNodes;
  1010. EditorGraphRequestBus::EventResult(variableNodes, m_activeGraphIds.scriptCanvasId, &EditorGraphRequests::GetVariableNodes, highlightvariable->GetHighlightVariableId());
  1011. for (auto& variable : variableNodes)
  1012. {
  1013. highlightEffect->AddTarget(variable.m_scriptCanvasId);
  1014. }
  1015. highlightEffect->DisplayEffect(m_activeGraphIds.graphCanvasId);
  1016. GetActiveData().second->SetEffect(row, highlightEffect);
  1017. }
  1018. if (const ScriptCanvas::GreyOutNodeEffect* greyOutEffect = azrtti_cast<const ScriptCanvas::GreyOutNodeEffect*>(validationEvent))
  1019. {
  1020. m_unusedNodeValidationEffect.AddUnusedNode(greyOutEffect->GetGreyOutNodeId());
  1021. }
  1022. UpdateSelectedText();
  1023. }
  1024. }
  1025. void GraphValidationDockWidget::OnRowDeselected(int row)
  1026. {
  1027. if (auto model = GetActiveModel())
  1028. {
  1029. const ScriptCanvas::ValidationEvent* validationEvent = model->FindItemForRow(row);
  1030. if (const ScriptCanvas::GreyOutNodeEffect* greyOutEffect = azrtti_cast<const ScriptCanvas::GreyOutNodeEffect*>(validationEvent))
  1031. {
  1032. m_unusedNodeValidationEffect.RemoveUnusedNode(greyOutEffect->GetGreyOutNodeId());
  1033. }
  1034. GetActiveData().second->ClearEffect(row);
  1035. UpdateSelectedText();
  1036. }
  1037. }
  1038. void GraphValidationDockWidget::UpdateSelectedText()
  1039. {
  1040. int selectedRowsSize = 0;
  1041. for (const QModelIndex& selectedRow : ui->statusTableView->selectionModel()->selectedRows())
  1042. {
  1043. QModelIndex sourceIndex = m_proxyModel->mapToSource(selectedRow);
  1044. if (auto model = GetActiveModel())
  1045. {
  1046. const ScriptCanvas::ValidationEvent* validationEvent = model->FindItemForRow(sourceIndex.row());
  1047. if (validationEvent->CanAutoFix())
  1048. {
  1049. selectedRowsSize++;
  1050. }
  1051. }
  1052. }
  1053. if (selectedRowsSize == 0)
  1054. {
  1055. ui->fixSelectedText->setVisible(false);
  1056. }
  1057. else
  1058. {
  1059. ui->fixSelectedText->setVisible(true);
  1060. ui->fixSelectedText->setText(QString("%1 Selected").arg(selectedRowsSize));
  1061. }
  1062. }
  1063. ///////////////////
  1064. // ValidationData
  1065. ///////////////////
  1066. ValidationData::ValidationData()
  1067. : m_model(nullptr)
  1068. {
  1069. }
  1070. ValidationData::ValidationData(GraphCanvas::GraphId graphCanvasId, ScriptCanvas::ScriptCanvasId scriptCanvasId)
  1071. : m_graphCanvasId(graphCanvasId)
  1072. {
  1073. m_model = AZStd::make_unique<GraphValidationModel>();
  1074. if (scriptCanvasId.IsValid())
  1075. {
  1076. ScriptCanvas::StatusRequestBus::Handler::BusConnect(scriptCanvasId);
  1077. }
  1078. }
  1079. ValidationData::~ValidationData()
  1080. {
  1081. ScriptCanvas::StatusRequestBus::Handler::BusDisconnect();
  1082. ClearEffects();
  1083. }
  1084. void ValidationData::ValidateGraph(ScriptCanvas::ValidationResults&)
  1085. {
  1086. // Do nothing, this is asking us to provide the validationEvents, we're only interested
  1087. // in receiving them
  1088. }
  1089. void ValidationData::ReportValidationResults(ScriptCanvas::ValidationResults& validationEvents)
  1090. {
  1091. m_model->Clear();
  1092. m_model->AddEvents(validationEvents);
  1093. }
  1094. ScriptCanvasEditor::ValidationEffect* ValidationData::GetEffect(int row)
  1095. {
  1096. if (m_validationEffects.find(row) != m_validationEffects.end())
  1097. {
  1098. return m_validationEffects[row];
  1099. }
  1100. return nullptr;
  1101. }
  1102. void ValidationData::SetEffect(int row, ValidationEffect* effect)
  1103. {
  1104. if (m_validationEffects.find(row) == m_validationEffects.end())
  1105. {
  1106. m_validationEffects[row] = effect;
  1107. }
  1108. }
  1109. void ValidationData::ClearEffect(int row)
  1110. {
  1111. auto it = m_validationEffects.find(row);
  1112. if (it != m_validationEffects.end())
  1113. {
  1114. m_validationEffects[row]->CancelEffect();
  1115. delete m_validationEffects[row];
  1116. m_validationEffects.erase(it);
  1117. }
  1118. }
  1119. void ValidationData::ClearEffects()
  1120. {
  1121. for (auto it : m_validationEffects)
  1122. {
  1123. delete it.second;
  1124. }
  1125. m_validationEffects.clear();
  1126. }
  1127. void ValidationData::DisplayToast()
  1128. {
  1129. if (m_model->GetValidationResults().GetEvents().empty())
  1130. {
  1131. return;
  1132. }
  1133. GraphCanvas::ViewId viewId;
  1134. GraphCanvas::SceneRequestBus::EventResult(viewId, m_graphCanvasId, &GraphCanvas::SceneRequests::GetViewId);
  1135. AzQtComponents::ToastType toastType;
  1136. AZStd::string titleLabel = "Validation Issue";
  1137. AZStd::string description = "";
  1138. if (m_model->GetValidationResults().HasErrors())
  1139. {
  1140. toastType = AzQtComponents::ToastType::Error;
  1141. description = AZStd::string::format("%i validation error(s) were found.", m_model->GetValidationResults().ErrorCount());
  1142. }
  1143. else
  1144. {
  1145. toastType = AzQtComponents::ToastType::Warning;
  1146. description = AZStd::string::format("%i validation warning(s) were found.", m_model->GetValidationResults().WarningCount());
  1147. }
  1148. AzQtComponents::ToastConfiguration toastConfiguration(toastType, titleLabel.c_str(), description.c_str());
  1149. AzToolsFramework::ToastId validationToastId;
  1150. GraphCanvas::ViewRequestBus::EventResult(validationToastId, viewId, &GraphCanvas::ViewRequests::ShowToastNotification, toastConfiguration);
  1151. AzToolsFramework::ToastNotificationBus::MultiHandler::BusConnect(validationToastId);
  1152. }
  1153. void ValidationData::OnToastInteraction()
  1154. {
  1155. UIRequestBus::Broadcast(&UIRequests::OpenValidationPanel);
  1156. }
  1157. void ValidationData::OnToastDismissed()
  1158. {
  1159. const AzToolsFramework::ToastId* toastId = AzToolsFramework::ToastNotificationBus::GetCurrentBusId();
  1160. if (toastId)
  1161. {
  1162. AzToolsFramework::ToastNotificationBus::MultiHandler::BusDisconnect((*toastId));
  1163. }
  1164. }
  1165. #include <Editor/View/Widgets/ValidationPanel/moc_GraphValidationDockWidget.cpp>
  1166. }