TrackViewNodes.cpp 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603
  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. // Description : TrackView's tree control.
  9. #include "EditorDefs.h"
  10. #include "TrackViewNodes.h"
  11. // Qt
  12. #include <QAction>
  13. #include <QCompleter>
  14. #include <QDropEvent>
  15. #include <QFileDialog>
  16. #include <QInputDialog>
  17. #include <QMenu>
  18. #include <QMessageBox>
  19. #include <QMimeData>
  20. #include <QScrollBar>
  21. #include <QStyledItemDelegate>
  22. // AzToolsFramework
  23. #include <AzToolsFramework/Entity/EditorEntitySearchBus.h>
  24. #include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
  25. // AzQtComponents
  26. #include <AzQtComponents/Components/InputDialog.h>
  27. #include <AzQtComponents/Components/Widgets/ColorPicker.h>
  28. #include <AzQtComponents/Components/Widgets/FileDialog.h>
  29. // CryCommon
  30. #include <CryCommon/Maestro/Bus/EditorSequenceComponentBus.h>
  31. #include <CryCommon/Maestro/Types/AnimValueType.h>
  32. #include <CryCommon/Maestro/Types/AnimNodeType.h>
  33. #include <CryCommon/Maestro/Types/AnimParamType.h>
  34. #include <CryCommon/Maestro/Types/SequenceType.h>
  35. // Editor
  36. #include "TrackView/TVEventsDialog.h"
  37. #include "TrackView/TrackViewDialog.h"
  38. #include "Util/AutoDirectoryRestoreFileDialog.h"
  39. #include "TrackViewFBXImportPreviewDialog.h"
  40. #include "AnimationContext.h"
  41. CTrackViewNodesCtrl::CRecord::CRecord(CTrackViewNode* pNode /*= nullptr*/)
  42. : m_pNode(pNode)
  43. , m_bVisible(false)
  44. {
  45. if (pNode)
  46. {
  47. QVariant v;
  48. v.setValue<CTrackViewNodePtr>(pNode);
  49. setData(0, Qt::UserRole, v);
  50. }
  51. }
  52. class CTrackViewNodesCtrlDelegate
  53. : public QStyledItemDelegate
  54. {
  55. public:
  56. CTrackViewNodesCtrlDelegate(QObject* parent = nullptr)
  57. : QStyledItemDelegate(parent)
  58. {}
  59. void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
  60. {
  61. bool enabled = index.data(CTrackViewNodesCtrl::CRecord::EnableRole).toBool();
  62. QStyleOptionViewItem opt = option;
  63. if (!enabled)
  64. {
  65. opt.state &= ~QStyle::State_Enabled;
  66. }
  67. QStyledItemDelegate::paint(painter, opt, index);
  68. }
  69. };
  70. class CTrackViewNodesTreeWidget
  71. : public QTreeWidget
  72. {
  73. public:
  74. CTrackViewNodesTreeWidget(QWidget* parent)
  75. : QTreeWidget(parent)
  76. , m_controller(nullptr)
  77. {
  78. setItemDelegate(new CTrackViewNodesCtrlDelegate(this));
  79. }
  80. void setController(CTrackViewNodesCtrl* p)
  81. {
  82. m_controller = p;
  83. }
  84. protected:
  85. // Allow both CopyActions and MoveActions to be valid drag and drop operations.
  86. Qt::DropActions supportedDropActions() const override
  87. {
  88. return Qt::CopyAction | Qt::MoveAction;
  89. }
  90. void dragMoveEvent(QDragMoveEvent* event) override
  91. {
  92. CTrackViewNodesCtrl::CRecord* record = (CTrackViewNodesCtrl::CRecord*) itemAt(event->pos());
  93. if (!record)
  94. {
  95. return;
  96. }
  97. CTrackViewNode* pTargetNode = record->GetNode();
  98. QTreeWidget::dragMoveEvent(event);
  99. if (!event->isAccepted())
  100. {
  101. return;
  102. }
  103. if (pTargetNode && pTargetNode->IsGroupNode() /*&& !m_draggedNodes.DoesContain(pTargetNode)*/)
  104. {
  105. CTrackViewAnimNode* pDragTarget = static_cast<CTrackViewAnimNode*>(pTargetNode);
  106. bool bAllValidReparenting = true;
  107. QList<CTrackViewAnimNode*> nodes = draggedNodes(event);
  108. Q_FOREACH(CTrackViewAnimNode * pDraggedNode, nodes)
  109. {
  110. if (!pDraggedNode->IsValidReparentingTo(pDragTarget))
  111. {
  112. bAllValidReparenting = false;
  113. break;
  114. }
  115. }
  116. if (!(bAllValidReparenting && !nodes.isEmpty()))
  117. {
  118. event->ignore();
  119. }
  120. return;
  121. }
  122. }
  123. void dropEvent(QDropEvent* event) override
  124. {
  125. CTrackViewNodesCtrl::CRecord* record = (CTrackViewNodesCtrl::CRecord*) itemAt(event->pos());
  126. if (!record)
  127. {
  128. return;
  129. }
  130. CTrackViewNode* targetNode = record->GetNode();
  131. if (targetNode && targetNode->IsGroupNode())
  132. {
  133. CTrackViewAnimNode* dragTarget = static_cast<CTrackViewAnimNode*>(targetNode);
  134. bool allValidReparenting = true;
  135. QList<CTrackViewAnimNode*> nodes = draggedNodes(event);
  136. Q_FOREACH(CTrackViewAnimNode * draggedNode, nodes)
  137. {
  138. if (!draggedNode->IsValidReparentingTo(dragTarget))
  139. {
  140. allValidReparenting = false;
  141. break;
  142. }
  143. }
  144. if (allValidReparenting && !nodes.isEmpty())
  145. {
  146. // By default here the drop action is a CopyAction. That is what we want in case
  147. // some other random control accepts this drop (and then does nothing with the data).
  148. // If that happens we will not receive any notifications. If the Action default was MoveAction,
  149. // the dragged items in the tree would be deleted out from under us causing a crash.
  150. // Since we are here, we know this drop is on the same control so we can
  151. // switch it to a MoveAction right now. The node parents will be fixed up below.
  152. event->setDropAction(Qt::MoveAction);
  153. QTreeWidget::dropEvent(event);
  154. if (!event->isAccepted())
  155. {
  156. return;
  157. }
  158. if (nodes.size() > 0)
  159. {
  160. // All nodes are from the same sequence
  161. CTrackViewSequence* sequence = nodes[0]->GetSequence();
  162. AZ_Assert(nullptr != sequence, "GetSequence() should never be null");
  163. AzToolsFramework::ScopedUndoBatch undoBatch("Drag and Drop Track View Nodes");
  164. Q_FOREACH(CTrackViewAnimNode * draggedNode, nodes)
  165. {
  166. draggedNode->SetNewParent(dragTarget);
  167. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  168. }
  169. }
  170. }
  171. }
  172. }
  173. void keyPressEvent(QKeyEvent* event) override
  174. {
  175. // HAVE TO INCLUDE CASES FOR THESE IN THE ShortcutOverride handler in ::event() below
  176. switch (event->key())
  177. {
  178. case Qt::Key_Tab:
  179. if (m_controller)
  180. {
  181. m_controller->ShowNextResult();
  182. event->accept();
  183. }
  184. return;
  185. default:
  186. break;
  187. }
  188. QTreeWidget::keyPressEvent(event);
  189. }
  190. bool event(QEvent* e) override
  191. {
  192. if (e->type() == QEvent::ShortcutOverride)
  193. {
  194. // since we respond to the following things, let Qt know so that shortcuts don't override us
  195. bool respondsToEvent = false;
  196. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
  197. if (keyEvent->key() == Qt::Key_Tab)
  198. {
  199. respondsToEvent = true;
  200. }
  201. if (respondsToEvent)
  202. {
  203. e->accept();
  204. return true;
  205. }
  206. }
  207. return QTreeWidget::event(e);
  208. }
  209. bool focusNextPrevChild([[maybe_unused]] bool next) override
  210. {
  211. return false; // so we get the tab key
  212. }
  213. private:
  214. QList<CTrackViewAnimNode*> draggedNodes(QDropEvent* event)
  215. {
  216. QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
  217. QDataStream stream(&encoded, QIODevice::ReadOnly);
  218. QList<CTrackViewAnimNode*> nodes;
  219. while (!stream.atEnd())
  220. {
  221. int row, col;
  222. QMap<int, QVariant> roleDataMap;
  223. stream >> row >> col >> roleDataMap;
  224. QVariant v = roleDataMap[Qt::UserRole];
  225. if (v.isValid())
  226. {
  227. CTrackViewNode* pNode = v.value<CTrackViewNodePtr>();
  228. if (pNode && pNode->GetNodeType() == eTVNT_AnimNode)
  229. {
  230. nodes << (CTrackViewAnimNode*)pNode;
  231. }
  232. }
  233. }
  234. return nodes;
  235. }
  236. CTrackViewNodesCtrl* m_controller;
  237. };
  238. QDataStream& operator<<(QDataStream& out, const CTrackViewNodePtr& obj)
  239. {
  240. out.writeRawData((const char*) &obj, sizeof(obj));
  241. return out;
  242. }
  243. QDataStream& operator>>(QDataStream& in, CTrackViewNodePtr& obj)
  244. {
  245. in.readRawData((char*) &obj, sizeof(obj));
  246. return in;
  247. }
  248. enum EMenuItem
  249. {
  250. eMI_SelectInViewport = 603,
  251. eMI_CopyNodes = 605,
  252. eMI_CopySelectedNodes = 602,
  253. eMI_PasteNodes = 604,
  254. eMI_RemoveSelected = 10,
  255. eMI_CopyKeys = 599,
  256. eMI_CopySelectedKeys = 600,
  257. eMI_PasteKeys = 601,
  258. eMI_AddTrackBase = 1000,
  259. eMI_RemoveTrack = 299,
  260. eMI_ExpandAll = 650,
  261. eMI_CollapseAll = 659,
  262. eMI_ExpandFolders = 660,
  263. eMI_CollapseFolders = 661,
  264. eMI_ExpandEntities = 651,
  265. eMI_CollapseEntities = 652,
  266. eMI_ExpandCameras = 653,
  267. eMI_CollapseCameras = 654,
  268. eMI_ExpandEvents = 657,
  269. eMI_CollapseEvents = 658,
  270. eMI_Rename = 11,
  271. eMI_CreateFolder = 610,
  272. eMI_AddSelectedEntities = 500,
  273. eMI_AddDirectorNode = 501,
  274. eMI_AddConsoleVariable = 502,
  275. eMI_AddScriptVariable = 503,
  276. eMI_AddEvent = 505,
  277. eMI_AddCurrentLayer = 506,
  278. eMI_AddCommentNode = 507,
  279. eMI_AddRadialBlur = 508,
  280. eMI_AddColorCorrection = 509,
  281. eMI_AddDOF = 510,
  282. eMI_AddScreenfader = 511,
  283. eMI_AddShadowSetup = 513,
  284. eMI_EditEvents = 550,
  285. eMI_SaveToFBX = 12,
  286. eMI_ImportFromFBX = 14,
  287. eMI_SetAsViewCamera = 13,
  288. eMI_SetAsActiveDirector = 15,
  289. eMI_Disable = 17,
  290. eMI_Mute = 18,
  291. eMI_CustomizeTrackColor = 19,
  292. eMI_ClearCustomTrackColor = 20,
  293. eMI_ShowHideBase = 100,
  294. eMI_SelectSubmaterialBase = 2000,
  295. eMI_SetAnimationLayerBase = 3000,
  296. };
  297. // The 'MI' represents a Menu Item.
  298. AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
  299. #include <TrackView/ui_TrackViewNodes.h>
  300. AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
  301. //////////////////////////////////////////////////////////////////////////
  302. CTrackViewNodesCtrl::CTrackViewNodesCtrl(QWidget* hParentWnd, CTrackViewDialog* parent /* = 0 */)
  303. : QWidget(hParentWnd)
  304. , m_bIgnoreNotifications(false)
  305. , m_bNeedReload(false)
  306. , m_bSelectionChanging(false)
  307. , ui(new Ui::CTrackViewNodesCtrl)
  308. , m_pTrackViewDialog(parent)
  309. {
  310. ui->setupUi(this);
  311. m_pDopeSheet = nullptr;
  312. m_currentMatchIndex = 0;
  313. m_matchCount = 0;
  314. qRegisterMetaType<CTrackViewNodePtr>("CTrackViewNodePtr");
  315. qRegisterMetaTypeStreamOperators<CTrackViewNodePtr>("CTrackViewNodePtr");
  316. ui->treeWidget->hide();
  317. ui->searchField->hide();
  318. ui->searchCount->hide();
  319. ui->searchField->installEventFilter(this);
  320. ui->treeWidget->setController(this);
  321. ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
  322. connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this, &CTrackViewNodesCtrl::OnNMRclick);
  323. connect(ui->treeWidget, &QTreeWidget::itemExpanded, this, &CTrackViewNodesCtrl::OnItemExpanded);
  324. connect(ui->treeWidget, &QTreeWidget::itemCollapsed, this, &CTrackViewNodesCtrl::OnItemExpanded);
  325. connect(ui->treeWidget, &QTreeWidget::itemSelectionChanged, this, &CTrackViewNodesCtrl::OnSelectionChanged);
  326. connect(ui->treeWidget, &QTreeWidget::itemDoubleClicked, this, &CTrackViewNodesCtrl::OnItemDblClick);
  327. connect(ui->searchField, &QLineEdit::textChanged, this, &CTrackViewNodesCtrl::OnFilterChange);
  328. m_bEditLock = false;
  329. m_arrowCursor = Qt::ArrowCursor;
  330. m_noIcon = Qt::ForbiddenCursor;
  331. ///////////////////////////////////////////////////////////////
  332. // Populate m_componentTypeToIconMap with all component icons
  333. AZ::SerializeContext* serializeContext = nullptr;
  334. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  335. AZ_Assert(serializeContext, "Failed to acquire serialize context.");
  336. serializeContext->EnumerateDerived<AZ::Component>([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
  337. {
  338. AZStd::string iconPath;
  339. AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
  340. if (!iconPath.empty())
  341. {
  342. m_componentTypeToIconMap[classData->m_typeId] = QIcon(iconPath.c_str());
  343. }
  344. return true; // continue enumerating
  345. });
  346. ///////////////////////////////////////////////////////////////
  347. GetIEditor()->GetSequenceManager()->AddListener(this);
  348. GetIEditor()->GetAnimation()->AddListener(this);
  349. GetIEditor()->GetUndoManager()->AddListener(this);
  350. };
  351. //////////////////////////////////////////////////////////////////////////
  352. CTrackViewNodesCtrl::~CTrackViewNodesCtrl()
  353. {
  354. GetIEditor()->GetUndoManager()->RemoveListener(this);
  355. GetIEditor()->GetAnimation()->RemoveListener(this);
  356. GetIEditor()->GetSequenceManager()->RemoveListener(this);
  357. }
  358. bool CTrackViewNodesCtrl::eventFilter(QObject* o, QEvent* e)
  359. {
  360. if (o == ui->searchField && e->type() == QEvent::KeyPress)
  361. {
  362. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
  363. if (keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier)
  364. {
  365. ShowNextResult();
  366. return true;
  367. }
  368. }
  369. return QWidget::eventFilter(o, e);
  370. }
  371. //////////////////////////////////////////////////////////////////////////
  372. void CTrackViewNodesCtrl::OnSequenceChanged()
  373. {
  374. assert(m_pTrackViewDialog);
  375. m_nodeToRecordMap.clear();
  376. ui->treeWidget->clear();
  377. FillAutoCompletionListForFilter();
  378. Reload();
  379. }
  380. // IAnimationContextListener
  381. void CTrackViewNodesCtrl::OnSequenceChanged([[maybe_unused]]CTrackViewSequence* pNewSequence)
  382. {
  383. OnSequenceChanged();
  384. }
  385. // ITrackViewSequenceManagerListener
  386. void CTrackViewNodesCtrl::OnSequenceRemoved([[maybe_unused]] CTrackViewSequence* pSequence)
  387. {
  388. OnSequenceChanged();
  389. }
  390. //////////////////////////////////////////////////////////////////////////
  391. void CTrackViewNodesCtrl::SetDopeSheet(CTrackViewDopeSheetBase* pDopeSheet)
  392. {
  393. m_pDopeSheet = pDopeSheet;
  394. }
  395. //////////////////////////////////////////////////////////////////////////
  396. CTrackViewNodesCtrl::CRecord* CTrackViewNodesCtrl::AddAnimNodeRecord(CRecord* pParentRecord, CTrackViewAnimNode* animNode)
  397. {
  398. CRecord* pNewRecord = new CRecord(animNode);
  399. pNewRecord->setText(0, QString::fromUtf8(animNode->GetName().c_str()));
  400. UpdateAnimNodeRecord(pNewRecord, animNode);
  401. pParentRecord->insertChild(GetInsertPosition(pParentRecord, animNode), pNewRecord);
  402. FillNodesRec(pNewRecord, animNode);
  403. return pNewRecord;
  404. }
  405. //////////////////////////////////////////////////////////////////////////
  406. CTrackViewNodesCtrl::CRecord* CTrackViewNodesCtrl::AddTrackRecord(CRecord* pParentRecord, CTrackViewTrack* pTrack)
  407. {
  408. CRecord* pNewTrackRecord = new CRecord(pTrack);
  409. pNewTrackRecord->setText(0, QString::fromUtf8(pTrack->GetName().c_str()));
  410. UpdateTrackRecord(pNewTrackRecord, pTrack);
  411. pParentRecord->insertChild(GetInsertPosition(pParentRecord, pTrack), pNewTrackRecord);
  412. FillNodesRec(pNewTrackRecord, pTrack);
  413. return pNewTrackRecord;
  414. }
  415. //////////////////////////////////////////////////////////////////////////
  416. int CTrackViewNodesCtrl::GetInsertPosition(CRecord* pParentRecord, CTrackViewNode* pNode)
  417. {
  418. // Search for insert position
  419. const int siblingCount = pParentRecord->childCount();
  420. for (int i = 0; i < siblingCount; ++i)
  421. {
  422. CRecord* record = static_cast<CRecord*>(pParentRecord->child(i));
  423. CTrackViewNode* pSiblingNode = record->GetNode();
  424. if (*pNode < *pSiblingNode)
  425. {
  426. return i;
  427. }
  428. }
  429. return siblingCount;
  430. }
  431. //////////////////////////////////////////////////////////////////////////
  432. void CTrackViewNodesCtrl::AddNodeRecord(CRecord* record, CTrackViewNode* pNode)
  433. {
  434. assert(m_nodeToRecordMap.find(pNode) == m_nodeToRecordMap.end());
  435. if (m_nodeToRecordMap.find(pNode) != m_nodeToRecordMap.end())
  436. {
  437. // For safety. Shouldn't happen
  438. return;
  439. }
  440. CRecord* pNewRecord = nullptr;
  441. if (pNode->IsHidden())
  442. {
  443. return;
  444. }
  445. switch (pNode->GetNodeType())
  446. {
  447. case eTVNT_AnimNode:
  448. pNewRecord = AddAnimNodeRecord(record, static_cast<CTrackViewAnimNode*>(pNode));
  449. break;
  450. case eTVNT_Track:
  451. pNewRecord = AddTrackRecord(record, static_cast<CTrackViewTrack*>(pNode));
  452. break;
  453. }
  454. if (pNewRecord)
  455. {
  456. if (!pNode->IsGroupNode() && pNode->GetChildCount() == 0) // groups and compound tracks are draggable
  457. {
  458. pNewRecord->setFlags(pNewRecord->flags() & ~Qt::ItemIsDragEnabled);
  459. }
  460. if (!pNode->IsGroupNode()) // only groups can be dropped into
  461. {
  462. pNewRecord->setFlags(pNewRecord->flags() & ~Qt::ItemIsDropEnabled);
  463. }
  464. if (pNode->GetExpanded())
  465. {
  466. pNewRecord->setExpanded(true);
  467. }
  468. if (pNode->IsSelected())
  469. {
  470. m_bIgnoreNotifications = true;
  471. SelectRow(pNode, false, false);
  472. m_bIgnoreNotifications = false;
  473. }
  474. m_nodeToRecordMap[pNode] = pNewRecord;
  475. }
  476. }
  477. //////////////////////////////////////////////////////////////////////////
  478. void CTrackViewNodesCtrl::FillNodesRec(CRecord* record, CTrackViewNode* pCurrentNode)
  479. {
  480. const unsigned int childCount = pCurrentNode->GetChildCount();
  481. for (unsigned int childIndex = 0; childIndex < childCount; ++childIndex)
  482. {
  483. CTrackViewNode* pNode = pCurrentNode->GetChild(childIndex);
  484. if (!pNode->IsHidden())
  485. {
  486. AddNodeRecord(record, pNode);
  487. }
  488. }
  489. }
  490. //////////////////////////////////////////////////////////////////////////
  491. void CTrackViewNodesCtrl::UpdateNodeRecord(CRecord* record)
  492. {
  493. CTrackViewNode* pNode = record->GetNode();
  494. if (pNode)
  495. {
  496. switch (pNode->GetNodeType())
  497. {
  498. case eTVNT_AnimNode:
  499. UpdateAnimNodeRecord(record, static_cast<CTrackViewAnimNode*>(pNode));
  500. break;
  501. case eTVNT_Track:
  502. UpdateTrackRecord(record, static_cast<CTrackViewTrack*>(pNode));
  503. break;
  504. }
  505. }
  506. }
  507. //////////////////////////////////////////////////////////////////////////
  508. void CTrackViewNodesCtrl::UpdateTrackRecord(CRecord* record, CTrackViewTrack* pTrack)
  509. {
  510. record->setIcon(0, GetIconForTrack(pTrack));
  511. // Check if parameter is valid for non sub tracks
  512. CTrackViewAnimNode* animNode = pTrack->GetAnimNode();
  513. const bool isParamValid = pTrack->IsSubTrack() || animNode->IsParamValid(pTrack->GetParameterType());
  514. // Check if disabled or muted
  515. const bool bDisabledOrMuted = pTrack->IsDisabled() || pTrack->IsMuted();
  516. // If track is not valid and disabled/muted color node in grey
  517. record->setData(0, CRecord::EnableRole, !bDisabledOrMuted && isParamValid);
  518. }
  519. //////////////////////////////////////////////////////////////////////////
  520. void CTrackViewNodesCtrl::UpdateAnimNodeRecord(CRecord* record, CTrackViewAnimNode* animNode)
  521. {
  522. const QColor TextColorForMissingEntity(226, 52, 43); // LY palette for 'Error/Failure'
  523. const QColor BackColorForActiveDirector(243, 81, 29); // LY palette for 'Primary'
  524. const QColor BackColorForInactiveDirector(22, 23, 27); // LY palette for 'Background (In Focus)'
  525. const QColor BackColorForGroupNodes(42, 84, 244); // LY palette for 'Secondary'
  526. QFont f = font();
  527. f.setBold(true);
  528. record->setFont(0, f);
  529. AnimNodeType nodeType = animNode->GetType();
  530. if (nodeType == AnimNodeType::Component)
  531. {
  532. // get the component icon from cached component icons
  533. AZ::Entity* azEntity = nullptr;
  534. AZ::ComponentApplicationBus::BroadcastResult(azEntity, &AZ::ComponentApplicationBus::Events::FindEntity,
  535. static_cast<CTrackViewAnimNode*>(animNode->GetParentNode())->GetAzEntityId());
  536. if (azEntity)
  537. {
  538. const AZ::Component* component = azEntity->FindComponent(animNode->GetComponentId());
  539. if (component)
  540. {
  541. auto findIter = m_componentTypeToIconMap.find(AzToolsFramework::GetUnderlyingComponentType(*component));
  542. if (findIter != m_componentTypeToIconMap.end())
  543. {
  544. record->setIcon(0, findIter->second);
  545. }
  546. }
  547. }
  548. }
  549. else
  550. {
  551. record->setIcon(0, TrackViewNodeIcon(nodeType));
  552. }
  553. const bool disabled = animNode->IsDisabled();
  554. record->setData(0, CRecord::EnableRole, !disabled);
  555. if (nodeType == AnimNodeType::Group)
  556. {
  557. record->setBackground(0, BackColorForGroupNodes);
  558. record->setSizeHint(0, QSize(30, 20));
  559. }
  560. else if (nodeType == AnimNodeType::AzEntity)
  561. {
  562. AZ::Entity* entity = nullptr;
  563. AZ::ComponentApplicationBus::BroadcastResult(
  564. entity, &AZ::ComponentApplicationBus::Events::FindEntity, animNode->GetAzEntityId());
  565. if (!entity)
  566. {
  567. // In case of a missing entity, color it red.
  568. record->setForeground(0, TextColorForMissingEntity);
  569. }
  570. else
  571. {
  572. record->setData(0, Qt::ForegroundRole, QPalette::ColorRole::NoRole);
  573. }
  574. }
  575. // Mark the active director and other directors properly.
  576. if (animNode->IsActiveDirector())
  577. {
  578. record->setBackground(0, BackColorForActiveDirector);
  579. }
  580. else if (nodeType == AnimNodeType::Director)
  581. {
  582. record->setBackground(0, BackColorForInactiveDirector);
  583. }
  584. }
  585. //////////////////////////////////////////////////////////////////////////
  586. void CTrackViewNodesCtrl::Reload()
  587. {
  588. ui->treeWidget->clear();
  589. OnFillItems();
  590. }
  591. void CTrackViewNodesCtrl::OnFillItems()
  592. {
  593. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  594. if (sequence)
  595. {
  596. CTrackViewSequenceNotificationContext context(sequence);
  597. m_nodeToRecordMap.clear();
  598. CRecord* pRootGroupRec = new CRecord(sequence);
  599. pRootGroupRec->setText(0, QString::fromUtf8(sequence->GetName().c_str()));
  600. QFont f = font();
  601. f.setBold(true);
  602. pRootGroupRec->setData(0, Qt::FontRole, f);
  603. pRootGroupRec->setSizeHint(0, QSize(width(), 24));
  604. m_nodeToRecordMap[sequence] = pRootGroupRec;
  605. ui->treeWidget->addTopLevelItem(pRootGroupRec);
  606. FillNodesRec(pRootGroupRec, sequence);
  607. pRootGroupRec->setExpanded(sequence->GetExpanded());
  608. // Additional empty record like space for scrollbar in key control
  609. CRecord* pGroupRec = new CRecord();
  610. pGroupRec->setSizeHint(0, QSize(width(), 18));
  611. ui->treeWidget->addTopLevelItem(pRootGroupRec);
  612. }
  613. }
  614. //////////////////////////////////////////////////////////////////////////
  615. void CTrackViewNodesCtrl::OnItemExpanded(QTreeWidgetItem* item)
  616. {
  617. CRecord* record = (CRecord*) item;
  618. if (record && record->GetNode())
  619. {
  620. bool currentlyExpanded = record->GetNode()->GetExpanded();
  621. bool expanded = item->isExpanded();
  622. if (expanded != currentlyExpanded)
  623. {
  624. bool isDuringUndo = false;
  625. AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
  626. // Don't record another undo event if this OnItemExpanded callback is fired because we are Undoing or Redoing.
  627. if (isDuringUndo)
  628. {
  629. record->GetNode()->SetExpanded(expanded);
  630. }
  631. else
  632. {
  633. CTrackViewSequence* sequence = record->GetNode()->GetSequence();
  634. AZ_Assert(nullptr != sequence, "Expected valid sequence");
  635. AzToolsFramework::ScopedUndoBatch undoBatch("Set Node Expanded");
  636. record->GetNode()->SetExpanded(expanded);
  637. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  638. }
  639. }
  640. }
  641. UpdateDopeSheet();
  642. }
  643. //////////////////////////////////////////////////////////////////////////
  644. void CTrackViewNodesCtrl::OnSelectionChanged()
  645. {
  646. // Need to avoid the second call to this, because GetSelectedRows is broken
  647. // with multi selection
  648. if (m_bSelectionChanging)
  649. {
  650. return;
  651. }
  652. m_bSelectionChanging = true;
  653. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  654. if (sequence)
  655. {
  656. CTrackViewSequenceNotificationContext context(sequence);
  657. sequence->ClearSelection();
  658. QList<QTreeWidgetItem*> items = ui->treeWidget->selectedItems();
  659. int nCount = items.count();
  660. for (int i = 0; i < nCount; i++)
  661. {
  662. CRecord* record = (CRecord*)items.at(i);
  663. if (record && record->GetNode())
  664. {
  665. if (!record->GetNode()->IsSelected())
  666. {
  667. record->GetNode()->SetSelected(true);
  668. ui->treeWidget->setCurrentItem(record);
  669. }
  670. }
  671. }
  672. }
  673. m_bSelectionChanging = false;
  674. UpdateDopeSheet();
  675. }
  676. //////////////////////////////////////////////////////////////////////////
  677. void CTrackViewNodesCtrl::OnNMRclick(QPoint point)
  678. {
  679. CRecord* record = nullptr;
  680. bool isOnAzEntity = false;
  681. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  682. if (!sequence)
  683. {
  684. return;
  685. }
  686. CTrackViewSequenceNotificationContext context(sequence);
  687. // Find node under mouse.
  688. // Select the item that is at the point myPoint.
  689. record = static_cast<CRecord*>(ui->treeWidget->itemAt(point));
  690. CTrackViewAnimNode* groupNode = nullptr;
  691. CTrackViewNode* pNode = nullptr;
  692. CTrackViewAnimNode* animNode = nullptr;
  693. CTrackViewTrack* pTrack = nullptr;
  694. if (record && record->GetNode())
  695. {
  696. pNode = record->GetNode();
  697. if (pNode)
  698. {
  699. ETrackViewNodeType nodeType = pNode->GetNodeType();
  700. if (nodeType == eTVNT_AnimNode)
  701. {
  702. animNode = static_cast<CTrackViewAnimNode*>(pNode);
  703. isOnAzEntity = (animNode && animNode->GetType() == AnimNodeType::AzEntity);
  704. if (animNode->GetType() == AnimNodeType::Director || animNode->GetType() == AnimNodeType::Group || isOnAzEntity)
  705. {
  706. groupNode = animNode;
  707. }
  708. }
  709. else if (nodeType == eTVNT_Sequence)
  710. {
  711. groupNode = sequence;
  712. }
  713. else if (nodeType == eTVNT_Track)
  714. {
  715. pTrack = static_cast<CTrackViewTrack*>(pNode);
  716. animNode = pTrack->GetAnimNode();
  717. }
  718. }
  719. }
  720. else
  721. {
  722. pNode = sequence;
  723. groupNode = sequence;
  724. record = m_nodeToRecordMap[sequence];
  725. }
  726. int cmd = ShowPopupMenu(point, record);
  727. float scrollPos = SaveVerticalScrollPos();
  728. if (cmd == eMI_RemoveSelected)
  729. {
  730. // If we are about to delete the sequence, cancel the notification
  731. // context, otherwise it will notify on a stale sequence pointer.
  732. if (sequence->IsSelected())
  733. {
  734. context.Cancel();
  735. }
  736. // Let the AZ Undo system manage the nodes on the sequence entity
  737. AzToolsFramework::ScopedUndoBatch undoBatch("Delete Selected Nodes/Tracks");
  738. auto id = sequence->GetSequenceComponentEntityId();
  739. sequence->DeleteSelectedNodes();
  740. undoBatch.MarkEntityDirty(id);
  741. }
  742. if (groupNode)
  743. {
  744. // Group operations applicable to AZEntities and Group Nodes
  745. if (cmd == eMI_ExpandAll)
  746. {
  747. BeginUndoTransaction();
  748. groupNode->GetAllAnimNodes().ExpandAll();
  749. EndUndoTransaction();
  750. }
  751. else if (cmd == eMI_CollapseAll)
  752. {
  753. BeginUndoTransaction();
  754. groupNode->GetAllAnimNodes().CollapseAll();
  755. EndUndoTransaction();
  756. }
  757. if (!isOnAzEntity)
  758. {
  759. // Group operations not applicable to AZEntities
  760. if (cmd == eMI_AddSelectedEntities)
  761. {
  762. AzToolsFramework::ScopedUndoBatch undoBatch("Add Entities to Track View");
  763. CTrackViewAnimNodeBundle addedNodes = groupNode->AddSelectedEntities(m_pTrackViewDialog->GetDefaultTracksForEntityNode());
  764. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  765. int selectedEntitiesCount = 0;
  766. AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
  767. selectedEntitiesCount, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntitiesCount);
  768. // check to make sure all nodes were added and notify user if they weren't
  769. if (addedNodes.GetCount() != static_cast<unsigned int>(selectedEntitiesCount))
  770. {
  771. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  772. AZStd::string messages = movieSystem ? movieSystem->GetUserNotificationMsgs() : "";
  773. // Create a list of all lines
  774. AZStd::vector<AZStd::string> lines;
  775. AzFramework::StringFunc::Tokenize(messages.c_str(), lines, "\n");
  776. // Truncate very long messages. No information is lost because
  777. // all of these errors will have been logged to the console already.
  778. const int maxLines = 30;
  779. AZStd::string shortMessages;
  780. if (lines.size() > maxLines)
  781. {
  782. int numLines = 0;
  783. for (AZStd::string line : lines)
  784. {
  785. shortMessages += line + "\n";
  786. if (++numLines >= maxLines)
  787. {
  788. break;
  789. }
  790. }
  791. shortMessages += "Message truncated, please see console for a full list of warnings.\n";
  792. }
  793. else
  794. {
  795. shortMessages = messages;
  796. }
  797. QMessageBox::information(this, tr("Track View Warning"), tr(shortMessages.c_str()));
  798. // clear the notification log now that we've consumed and presented them.
  799. movieSystem->ClearUserNotificationMsgs();
  800. }
  801. groupNode->BindToEditorObjects();
  802. }
  803. else if (cmd == eMI_AddCurrentLayer)
  804. {
  805. AzToolsFramework::ScopedUndoBatch undoBatch("Add Current Layer to Track View");
  806. groupNode->AddCurrentLayer();
  807. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  808. }
  809. else if (cmd == eMI_AddScreenfader)
  810. {
  811. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Screen Fader Node");
  812. groupNode->CreateSubNode("ScreenFader", AnimNodeType::ScreenFader);
  813. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  814. }
  815. else if (cmd == eMI_AddCommentNode)
  816. {
  817. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Comment Node");
  818. QString commentNodeName = groupNode->GetAvailableNodeNameStartingWith("Comment");
  819. groupNode->CreateSubNode(commentNodeName, AnimNodeType::Comment);
  820. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  821. }
  822. else if (cmd == eMI_AddRadialBlur)
  823. {
  824. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Radial Blur Node");
  825. groupNode->CreateSubNode("RadialBlur", AnimNodeType::RadialBlur);
  826. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  827. }
  828. else if (cmd == eMI_AddColorCorrection)
  829. {
  830. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Color Correction Node");
  831. groupNode->CreateSubNode("ColorCorrection", AnimNodeType::ColorCorrection);
  832. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  833. }
  834. else if (cmd == eMI_AddDOF)
  835. {
  836. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Depth of Field Node");
  837. groupNode->CreateSubNode("DepthOfField", AnimNodeType::DepthOfField);
  838. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  839. }
  840. else if (cmd == eMI_AddShadowSetup)
  841. {
  842. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Shadow Setup Node");
  843. groupNode->CreateSubNode("ShadowsSetup", AnimNodeType::ShadowSetup);
  844. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  845. }
  846. else if (cmd == eMI_AddDirectorNode)
  847. {
  848. QString name = groupNode->GetAvailableNodeNameStartingWith("Director");
  849. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Director Node");
  850. groupNode->CreateSubNode(name, AnimNodeType::Director);
  851. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  852. }
  853. else if (cmd == eMI_AddConsoleVariable)
  854. {
  855. QString stringValue = QInputDialog::getText(this, tr("Console Variable Name"), QString());
  856. if (!stringValue.isEmpty())
  857. {
  858. QString name = groupNode->GetAvailableNodeNameStartingWith(stringValue);
  859. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Console (CVar) Node");
  860. groupNode->CreateSubNode(name, AnimNodeType::CVar);
  861. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  862. }
  863. }
  864. else if (cmd == eMI_AddScriptVariable)
  865. {
  866. QString stringValue = QInputDialog::getText(this, tr("Script Variable Name"), QString());
  867. if (!stringValue.isEmpty())
  868. {
  869. QString name = groupNode->GetAvailableNodeNameStartingWith(stringValue);
  870. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Script Variable Node");
  871. groupNode->CreateSubNode(name, AnimNodeType::ScriptVar);
  872. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  873. }
  874. }
  875. else if (cmd == eMI_AddEvent)
  876. {
  877. QString stringValue = QInputDialog::getText(this, tr("Track Event Name"), QString());
  878. if (!stringValue.isEmpty())
  879. {
  880. AzToolsFramework::ScopedUndoBatch undoBatch("Add Track View Event Node");
  881. groupNode->CreateSubNode(stringValue, AnimNodeType::Event);
  882. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  883. }
  884. }
  885. else if (cmd == eMI_PasteNodes)
  886. {
  887. AzToolsFramework::ScopedUndoBatch undoBatch("Paste Track View Nodes");
  888. groupNode->PasteNodesFromClipboard(this);
  889. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  890. }
  891. else if (cmd == eMI_CreateFolder)
  892. {
  893. CreateFolder(groupNode);
  894. }
  895. else if (cmd == eMI_ExpandFolders)
  896. {
  897. AzToolsFramework::ScopedUndoBatch undoBatch("Expand Track View folders");
  898. groupNode->GetAnimNodesByType(AnimNodeType::Group).ExpandAll();
  899. groupNode->GetAnimNodesByType(AnimNodeType::Director).ExpandAll();
  900. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  901. }
  902. else if (cmd == eMI_CollapseFolders)
  903. {
  904. AzToolsFramework::ScopedUndoBatch undoBatch("Collapse Track View folders");
  905. groupNode->GetAnimNodesByType(AnimNodeType::Group).CollapseAll();
  906. groupNode->GetAnimNodesByType(AnimNodeType::Director).CollapseAll();
  907. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  908. }
  909. else if (cmd == eMI_ExpandEntities)
  910. {
  911. AzToolsFramework::ScopedUndoBatch undoBatch("Expand Track View entities");
  912. groupNode->GetAnimNodesByType(AnimNodeType::AzEntity).ExpandAll();
  913. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  914. }
  915. else if (cmd == eMI_CollapseEntities)
  916. {
  917. AzToolsFramework::ScopedUndoBatch undoBatch("Collapse Track View entities");
  918. groupNode->GetAnimNodesByType(AnimNodeType::AzEntity).CollapseAll();
  919. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  920. }
  921. else if (cmd == eMI_ExpandEvents)
  922. {
  923. AzToolsFramework::ScopedUndoBatch undoBatch("Expand Track View events");
  924. groupNode->GetAnimNodesByType(AnimNodeType::Event).ExpandAll();
  925. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  926. }
  927. else if (cmd == eMI_CollapseEvents)
  928. {
  929. AzToolsFramework::ScopedUndoBatch undoBatch("Collapse Track View events");
  930. groupNode->GetAnimNodesByType(AnimNodeType::Event).CollapseAll();
  931. undoBatch.MarkEntityDirty(groupNode->GetSequence()->GetSequenceComponentEntityId());
  932. }
  933. }
  934. }
  935. if (cmd == eMI_EditEvents)
  936. {
  937. EditEvents();
  938. }
  939. else if (cmd == eMI_Rename)
  940. {
  941. if (animNode || groupNode)
  942. {
  943. CTrackViewAnimNode* animNode2 = static_cast<CTrackViewAnimNode*>(pNode);
  944. QString oldName = QString::fromUtf8(animNode2->GetName().c_str());
  945. AzQtComponents::InputDialog inputDialog(this);
  946. inputDialog.setWindowTitle(tr("Rename Node"));
  947. inputDialog.setTextValue(oldName);
  948. inputDialog.setLabelText("");
  949. inputDialog.SetRegularExpressionValidator("[a-zA-Z0-9_\\-\\_]*");
  950. // Max name length is 512 for a sequence
  951. constexpr int MaxNameLength = 512;
  952. inputDialog.SetMaxLength(MaxNameLength);
  953. // add check for duplicate entity names if this is bound to an Object node
  954. bool checkForDuplicateName = animNode2->IsBoundToEditorObjects();
  955. bool retryRename = false;
  956. QString newName;
  957. do
  958. {
  959. const auto ret = inputDialog.exec();
  960. if (ret == QDialog::Accepted)
  961. {
  962. QString name = inputDialog.textValue();
  963. // Bail out early if user is trying to rename to the same name, can treat as a no-op
  964. if (name == oldName)
  965. {
  966. return;
  967. }
  968. if (checkForDuplicateName)
  969. {
  970. AzToolsFramework::EntitySearchFilter filter;
  971. const auto nameUtf8 = name.toUtf8();
  972. const AZStd::string searchName(nameUtf8.constData(), nameUtf8.length());
  973. filter.m_names.push_back(searchName);
  974. AzToolsFramework::EntityIdList matchingEntities;
  975. AzToolsFramework::EditorEntitySearchBus::BroadcastResult(matchingEntities, &AzToolsFramework::EditorEntitySearchRequests::SearchEntities, filter);
  976. if (!matchingEntities.empty())
  977. {
  978. QMessageBox::warning(this, tr("Entity already exists"), QString(tr("Entity named '%1' already exists.\n\nPlease choose another unique name.")).arg(name));
  979. retryRename = true;
  980. }
  981. else
  982. {
  983. newName = name;
  984. retryRename = false;
  985. }
  986. }
  987. else
  988. {
  989. newName = name;
  990. }
  991. }
  992. else
  993. {
  994. retryRename = false;
  995. }
  996. } while (retryRename);
  997. if(!GetNodeRecord(animNode2)) {
  998. QMessageBox::warning(
  999. this,
  1000. tr("Entity does not exist"),
  1001. tr("Entity has been deleted.\n\nUnable to rename entity"));
  1002. }
  1003. else if (!newName.isEmpty())
  1004. {
  1005. const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
  1006. sequenceManager->RenameNode(animNode2, newName.toUtf8().data());
  1007. UpdateNodeRecord(record);
  1008. }
  1009. }
  1010. }
  1011. else if (cmd == eMI_SetAsActiveDirector)
  1012. {
  1013. if (pNode && pNode->GetNodeType() == eTVNT_AnimNode)
  1014. {
  1015. CTrackViewAnimNode* animNode2 = static_cast<CTrackViewAnimNode*>(pNode);
  1016. animNode2->SetAsActiveDirector();
  1017. }
  1018. }
  1019. else if (cmd >= eMI_AddTrackBase && cmd < eMI_AddTrackBase + 1000)
  1020. {
  1021. if (animNode)
  1022. {
  1023. unsigned int menuId = cmd - eMI_AddTrackBase;
  1024. if (animNode->GetType() != AnimNodeType::AzEntity)
  1025. {
  1026. // add track
  1027. auto findIter = m_menuParamTypeMap.find(menuId);
  1028. if (findIter != m_menuParamTypeMap.end())
  1029. {
  1030. AzToolsFramework::ScopedUndoBatch undoBatch("Add TrackView Track");
  1031. animNode->CreateTrack(findIter->second);
  1032. undoBatch.MarkEntityDirty(animNode->GetSequence()->GetSequenceComponentEntityId());
  1033. }
  1034. }
  1035. }
  1036. }
  1037. else if (cmd == eMI_RemoveTrack)
  1038. {
  1039. if (pTrack)
  1040. {
  1041. AzToolsFramework::ScopedUndoBatch undoBatch("Remove TrackView Track");
  1042. pTrack->GetAnimNode()->RemoveTrack(pTrack);
  1043. undoBatch.MarkEntityDirty(pTrack->GetSequence()->GetSequenceComponentEntityId());
  1044. }
  1045. }
  1046. else if (cmd >= eMI_ShowHideBase && cmd < eMI_ShowHideBase + 100)
  1047. {
  1048. if (animNode)
  1049. {
  1050. unsigned int childIndex = cmd - eMI_ShowHideBase;
  1051. if (childIndex < animNode->GetChildCount())
  1052. {
  1053. CTrackViewNode* pChild = animNode->GetChild(childIndex);
  1054. pChild->SetHidden(!pChild->IsHidden());
  1055. }
  1056. }
  1057. }
  1058. else if (cmd == eMI_CopyKeys)
  1059. {
  1060. sequence->CopyKeysToClipboard(false, true);
  1061. }
  1062. else if (cmd == eMI_CopySelectedKeys)
  1063. {
  1064. sequence->CopyKeysToClipboard(true, true);
  1065. }
  1066. else if (cmd == eMI_PasteKeys)
  1067. {
  1068. CUndo undo("Paste TrackView Keys");
  1069. sequence->PasteKeysFromClipboard(animNode, pTrack);
  1070. }
  1071. else if (cmd == eMI_CopyNodes)
  1072. {
  1073. if (animNode)
  1074. {
  1075. animNode->CopyNodesToClipboard(false, this);
  1076. }
  1077. else
  1078. {
  1079. sequence->CopyNodesToClipboard(false, this);
  1080. }
  1081. }
  1082. else if (cmd == eMI_CopySelectedNodes)
  1083. {
  1084. sequence->CopyNodesToClipboard(true, this);
  1085. }
  1086. else if (cmd == eMI_SelectInViewport)
  1087. {
  1088. CUndo undo("Select TrackView Nodes in Viewport");
  1089. sequence->SelectSelectedNodesInViewport();
  1090. }
  1091. else if (cmd >= eMI_SelectSubmaterialBase && cmd < eMI_SelectSubmaterialBase + 100)
  1092. {
  1093. if (animNode)
  1094. {
  1095. QString matName;
  1096. GetMatNameAndSubMtlIndexFromName(matName, animNode->GetName().c_str());
  1097. QString newMatName;
  1098. newMatName = tr("%1.[%2]").arg(matName).arg(cmd - eMI_SelectSubmaterialBase + 1);
  1099. CUndo undo("Rename TrackView node");
  1100. animNode->SetName(newMatName.toUtf8().data());
  1101. animNode->SetSelected(true);
  1102. UpdateNodeRecord(record);
  1103. }
  1104. }
  1105. else if (cmd >= eMI_SetAnimationLayerBase && cmd < eMI_SetAnimationLayerBase + 100)
  1106. {
  1107. if (pNode && pNode->GetNodeType() == eTVNT_Track)
  1108. {
  1109. CTrackViewTrack* pTrack2 = static_cast<CTrackViewTrack*>(pNode);
  1110. pTrack2->SetAnimationLayerIndex(cmd - eMI_SetAnimationLayerBase);
  1111. }
  1112. }
  1113. else if (cmd == eMI_Disable)
  1114. {
  1115. if (pNode)
  1116. {
  1117. CTrackViewSequence* sequence2 = pNode->GetSequence();
  1118. AZ_Assert(nullptr != sequence2, "Expected valid sequence");
  1119. AzToolsFramework::ScopedUndoBatch undoBatch("Node Set Disabled");
  1120. pNode->SetDisabled(!pNode->IsDisabled());
  1121. undoBatch.MarkEntityDirty(sequence2->GetSequenceComponentEntityId());
  1122. }
  1123. }
  1124. else if (cmd == eMI_Mute)
  1125. {
  1126. if (pTrack)
  1127. {
  1128. pTrack->SetMuted(!pTrack->IsMuted());
  1129. }
  1130. }
  1131. else if (cmd == eMI_CustomizeTrackColor)
  1132. {
  1133. CustomizeTrackColor(pTrack);
  1134. }
  1135. else if (cmd == eMI_ClearCustomTrackColor)
  1136. {
  1137. if (pTrack)
  1138. {
  1139. pTrack->ClearCustomColor();
  1140. }
  1141. }
  1142. if (cmd)
  1143. {
  1144. RestoreVerticalScrollPos(scrollPos);
  1145. }
  1146. }
  1147. //////////////////////////////////////////////////////////////////////////
  1148. void CTrackViewNodesCtrl::OnItemDblClick(QTreeWidgetItem* item, int)
  1149. {
  1150. CRecord* record = (CRecord*)item;
  1151. if (record && record->GetNode())
  1152. {
  1153. CTrackViewNode* pNode = record->GetNode();
  1154. if (pNode->GetNodeType() == eTVNT_AnimNode)
  1155. {
  1156. CTrackViewAnimNode* animNode = static_cast<CTrackViewAnimNode*>(pNode);
  1157. if (const AZ::EntityId entityId = animNode->GetAzEntityId();
  1158. entityId.IsValid())
  1159. {
  1160. CUndo undo("Select Object");
  1161. AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
  1162. &AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities,
  1163. AzToolsFramework::EntityIdList{animNode->GetAzEntityId()});
  1164. }
  1165. }
  1166. }
  1167. }
  1168. //////////////////////////////////////////////////////////////////////////
  1169. void CTrackViewNodesCtrl::EditEvents()
  1170. {
  1171. CTVEventsDialog dlg;
  1172. dlg.exec();
  1173. }
  1174. //////////////////////////////////////////////////////////////////////////
  1175. void CTrackViewNodesCtrl::CreateFolder(CTrackViewAnimNode* groupNode)
  1176. {
  1177. // Change Group of the node.
  1178. QString name = QInputDialog::getText(this, tr("Enter Folder Name"), QString());
  1179. if (!name.isEmpty())
  1180. {
  1181. CUndo undo("Create folder");
  1182. if (!groupNode->CreateSubNode(name, AnimNodeType::Group))
  1183. {
  1184. QMessageBox::critical(this, QString(), tr("The name already exists. Use another."));
  1185. }
  1186. }
  1187. }
  1188. //////////////////////////////////////////////////////////////////////////
  1189. struct STrackMenuTreeNode
  1190. {
  1191. QMenu menu;
  1192. CAnimParamType paramType;
  1193. std::map<QString, std::unique_ptr<STrackMenuTreeNode> > children;
  1194. };
  1195. //////////////////////////////////////////////////////////////////////////
  1196. struct SContextMenu
  1197. {
  1198. QMenu main;
  1199. QMenu expandSub;
  1200. QMenu collapseSub;
  1201. QMenu setLayerSub;
  1202. STrackMenuTreeNode addTrackSub;
  1203. QMenu addComponentSub;
  1204. };
  1205. //////////////////////////////////////////////////////////////////////////
  1206. void CTrackViewNodesCtrl::AddGroupNodeAddItems(SContextMenu& contextMenu, CTrackViewAnimNode* animNode)
  1207. {
  1208. contextMenu.main.addAction("Create Folder")->setData(eMI_CreateFolder);
  1209. AzToolsFramework::EntityIdList entityIds;
  1210. AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
  1211. entityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
  1212. if (!entityIds.empty())
  1213. {
  1214. const char* msg = entityIds.size() == 1 ? "Add Selected Entity" : "Add Selected Entities";
  1215. contextMenu.main.addAction(msg)->setData(eMI_AddSelectedEntities);
  1216. }
  1217. const bool bIsDirectorOrSequence = (animNode->GetType() == AnimNodeType::Director || animNode->GetNodeType() == eTVNT_Sequence);
  1218. CTrackViewAnimNode* pDirector = bIsDirectorOrSequence ? animNode : animNode->GetDirector();
  1219. if (pDirector->GetAnimNodesByType(AnimNodeType::RadialBlur).GetCount() == 0)
  1220. {
  1221. contextMenu.main.addAction("Add Radial Blur Node")->setData(eMI_AddRadialBlur);
  1222. }
  1223. if (pDirector->GetAnimNodesByType(AnimNodeType::ColorCorrection).GetCount() == 0)
  1224. {
  1225. contextMenu.main.addAction("Add Color Correction Node")->setData(eMI_AddColorCorrection);
  1226. }
  1227. if (pDirector->GetAnimNodesByType(AnimNodeType::DepthOfField).GetCount() == 0)
  1228. {
  1229. contextMenu.main.addAction("Add Depth of Field Node")->setData(eMI_AddDOF);
  1230. }
  1231. if (pDirector->GetAnimNodesByType(AnimNodeType::ScreenFader).GetCount() == 0)
  1232. {
  1233. contextMenu.main.addAction("Add Screen Fader")->setData(eMI_AddScreenfader);
  1234. }
  1235. if (pDirector->GetAnimNodesByType(AnimNodeType::ShadowSetup).GetCount() == 0)
  1236. {
  1237. contextMenu.main.addAction("Add Shadows Setup Node")->setData(eMI_AddShadowSetup);
  1238. }
  1239. // A director node cannot have another director node as a child.
  1240. if (animNode->GetType() != AnimNodeType::Director)
  1241. {
  1242. contextMenu.main.addAction("Add Director(Scene) Node")->setData(eMI_AddDirectorNode);
  1243. }
  1244. contextMenu.main.addAction("Add Comment Node")->setData(eMI_AddCommentNode);
  1245. contextMenu.main.addAction("Add Console Variable Node")->setData(eMI_AddConsoleVariable);
  1246. contextMenu.main.addAction("Add Script Variable Node")->setData(eMI_AddScriptVariable);
  1247. contextMenu.main.addAction("Add Event Node")->setData(eMI_AddEvent);
  1248. }
  1249. //////////////////////////////////////////////////////////////////////////
  1250. void CTrackViewNodesCtrl::AddMenuSeperatorConditional(QMenu& menu, bool& bAppended)
  1251. {
  1252. if (bAppended)
  1253. {
  1254. menu.addSeparator();
  1255. }
  1256. bAppended = false;
  1257. }
  1258. //////////////////////////////////////////////////////////////////////////
  1259. int CTrackViewNodesCtrl::ShowPopupMenuSingleSelection(SContextMenu& contextMenu, CTrackViewSequence* sequence, CTrackViewNode* pNode)
  1260. {
  1261. bool bAppended = false;
  1262. bool isOnComponentNode = false;
  1263. bool isOnAzEntityNode = false;
  1264. const bool bOnSequence = pNode->GetNodeType() == eTVNT_Sequence;
  1265. const bool bOnNode = pNode->GetNodeType() == eTVNT_AnimNode;
  1266. const bool bOnTrack = pNode->GetNodeType() == eTVNT_Track;
  1267. const bool bIsLightAnimationSet = sequence->GetFlags() & IAnimSequence::eSeqFlags_LightAnimationSet;
  1268. // Get track & anim node pointers
  1269. CTrackViewTrack* pTrack = bOnTrack ? static_cast<CTrackViewTrack*>(pNode) : nullptr;
  1270. const bool bOnTrackNotSub = bOnTrack && !pTrack->IsSubTrack();
  1271. if (bOnNode)
  1272. {
  1273. AnimNodeType nodeType = static_cast<CTrackViewAnimNode*>(pNode)->GetType();
  1274. if (nodeType == AnimNodeType::Component)
  1275. {
  1276. isOnComponentNode = true;
  1277. }
  1278. else if (nodeType == AnimNodeType::AzEntity)
  1279. {
  1280. isOnAzEntityNode = true;
  1281. }
  1282. }
  1283. CTrackViewAnimNode* animNode = nullptr;
  1284. if (bOnSequence || bOnNode)
  1285. {
  1286. animNode = static_cast<CTrackViewAnimNode*>(pNode);
  1287. }
  1288. else if (bOnTrack)
  1289. {
  1290. animNode = pTrack->GetAnimNode();
  1291. }
  1292. bool isOnDirector = (animNode && animNode->GetType() == AnimNodeType::Director);
  1293. bool isOnAzEntity = (animNode && animNode->GetType() == AnimNodeType::AzEntity);
  1294. bool isOnSequence = (animNode && animNode->GetNodeType() == eTVNT_Sequence);
  1295. if (isOnSequence)
  1296. {
  1297. contextMenu.main.addAction("Select In Viewport")->setData(eMI_SelectInViewport);
  1298. contextMenu.main.addSeparator();
  1299. }
  1300. // Entity
  1301. if (bOnNode && !bIsLightAnimationSet && animNode->IsBoundToAzEntity())
  1302. {
  1303. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1304. contextMenu.main.addAction("Select In Viewport")->setData(eMI_SelectInViewport);
  1305. bAppended = true;
  1306. }
  1307. {
  1308. bool bCopyPasteRenameAppended = false;
  1309. // Copy & paste nodes
  1310. if ((bOnNode || bOnSequence) && !isOnComponentNode)
  1311. {
  1312. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1313. contextMenu.main.addAction("Copy")->setData(eMI_CopyNodes);
  1314. bCopyPasteRenameAppended = true;
  1315. }
  1316. if (pNode->IsGroupNode() && !isOnAzEntity)
  1317. {
  1318. contextMenu.main.addAction("Paste")->setData(eMI_PasteNodes);
  1319. bCopyPasteRenameAppended = true;
  1320. }
  1321. if ((bOnNode || bOnSequence || bOnTrackNotSub) && !isOnComponentNode)
  1322. {
  1323. contextMenu.main.addAction("Delete")->setData(bOnTrackNotSub ? eMI_RemoveTrack : eMI_RemoveSelected);
  1324. bCopyPasteRenameAppended = true;
  1325. }
  1326. // Renaming
  1327. if (pNode->CanBeRenamed())
  1328. {
  1329. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1330. contextMenu.main.addAction("Rename")->setData(eMI_Rename);
  1331. bCopyPasteRenameAppended = true;
  1332. }
  1333. bAppended = bAppended || bCopyPasteRenameAppended;
  1334. }
  1335. if (bOnTrack)
  1336. {
  1337. // Copy & paste keys
  1338. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1339. contextMenu.main.addAction("Copy Keys")->setData(eMI_CopyKeys);
  1340. contextMenu.main.addAction("Copy Selected Keys")->setData(eMI_CopySelectedKeys);
  1341. contextMenu.main.addAction("Paste Keys")->setData(eMI_PasteKeys);
  1342. bAppended = true;
  1343. }
  1344. // Flags
  1345. {
  1346. bool bFlagAppended = false;
  1347. if (!bOnSequence)
  1348. {
  1349. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1350. QAction* a = contextMenu.main.addAction("Disabled");
  1351. a->setData(eMI_Disable);
  1352. a->setCheckable(true);
  1353. a->setChecked(pNode->IsDisabled());
  1354. // If the node is not currently allowed to be enabled, disable the check box.
  1355. if (pNode->IsDisabled() && !pNode->CanBeEnabled())
  1356. {
  1357. a->setEnabled(false);
  1358. }
  1359. bFlagAppended = true;
  1360. }
  1361. if (bOnTrack)
  1362. {
  1363. if (pTrack->GetParameterType() == AnimParamType::Sound)
  1364. {
  1365. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1366. bool bMuted = pTrack->GetFlags() & IAnimTrack::eAnimTrackFlags_Muted;
  1367. QAction* a = contextMenu.main.addAction("Muted");
  1368. a->setData(eMI_Mute);
  1369. a->setCheckable(true);
  1370. a->setChecked(bMuted);
  1371. bFlagAppended = true;
  1372. }
  1373. }
  1374. // In case that it's a director node instead of a normal group node,
  1375. if (bOnNode && animNode->GetType() == AnimNodeType::Director)
  1376. {
  1377. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1378. QAction* a = contextMenu.main.addAction("Active Director");
  1379. a->setData(eMI_SetAsActiveDirector);
  1380. a->setCheckable(true);
  1381. a->setChecked(animNode->IsActiveDirector());
  1382. bFlagAppended = true;
  1383. }
  1384. bAppended = bAppended || bFlagAppended;
  1385. }
  1386. // Expand / collapse
  1387. if (bOnSequence || pNode->IsGroupNode())
  1388. {
  1389. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1390. contextMenu.expandSub.addAction("Expand all")->setData(eMI_ExpandAll);
  1391. contextMenu.collapseSub.addAction("Collapse all")->setData(eMI_CollapseAll);
  1392. if (!isOnAzEntity)
  1393. {
  1394. contextMenu.expandSub.addAction("Expand Folders")->setData(eMI_ExpandFolders);
  1395. contextMenu.collapseSub.addAction("Collapse Folders")->setData(eMI_CollapseFolders);
  1396. contextMenu.expandSub.addAction("Expand Entities")->setData(eMI_ExpandEntities);
  1397. contextMenu.collapseSub.addAction("Collapse Entities")->setData(eMI_CollapseEntities);
  1398. contextMenu.expandSub.addAction("Expand Events")->setData(eMI_ExpandEvents);
  1399. contextMenu.collapseSub.addAction("Collapse Events")->setData(eMI_CollapseEvents);
  1400. }
  1401. contextMenu.expandSub.setTitle("Expand");
  1402. contextMenu.main.addMenu(&contextMenu.expandSub);
  1403. contextMenu.collapseSub.setTitle("Collapse");
  1404. contextMenu.main.addMenu(&contextMenu.collapseSub);
  1405. bAppended = true;
  1406. }
  1407. // Add/Remove
  1408. {
  1409. if (bOnSequence || (pNode->IsGroupNode() && !isOnAzEntity) )
  1410. {
  1411. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1412. AddGroupNodeAddItems(contextMenu, animNode);
  1413. bAppended = true;
  1414. }
  1415. if (bOnNode)
  1416. {
  1417. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1418. if (!isOnAzEntity)
  1419. {
  1420. // Create 'Add Tracks' submenu
  1421. m_menuParamTypeMap.clear();
  1422. const QString addTracksMenuName = "Add Tracks";
  1423. if (FillAddTrackMenu(contextMenu.addTrackSub, animNode))
  1424. {
  1425. // add script table properties -> tracks available for adding
  1426. unsigned int currentId = 0;
  1427. CreateAddTrackMenuRec(contextMenu.main, addTracksMenuName, animNode, contextMenu.addTrackSub, currentId);
  1428. }
  1429. else
  1430. {
  1431. // no tracks available for adding -> add empty disabled submenu for UI consistency
  1432. contextMenu.main.addMenu(addTracksMenuName)->setEnabled(false);
  1433. }
  1434. }
  1435. bAppended = true;
  1436. }
  1437. }
  1438. bool isLegacySequence = (sequence && sequence->GetSequenceType() == SequenceType::Legacy);
  1439. if (isLegacySequence && bOnNode && !bIsLightAnimationSet && !isOnDirector && !isOnComponentNode && !isOnAzEntityNode)
  1440. {
  1441. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1442. contextMenu.main.addAction("Import FBX File...")->setData(eMI_ImportFromFBX);
  1443. contextMenu.main.addAction("Export FBX File...")->setData(eMI_SaveToFBX);
  1444. bAppended = true;
  1445. }
  1446. // Events
  1447. if (bOnSequence || pNode->IsGroupNode() && !bIsLightAnimationSet && !isOnAzEntity)
  1448. {
  1449. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1450. contextMenu.main.addAction("Edit Events...")->setData(eMI_EditEvents);
  1451. bAppended = true;
  1452. }
  1453. // Delete track menu
  1454. if (bOnTrackNotSub)
  1455. {
  1456. if (pTrack->GetParameterType() == AnimParamType::Animation || pTrack->GetParameterType() == AnimParamType::LookAt || pTrack->GetValueType() == AnimValueType::CharacterAnim)
  1457. {
  1458. // Add the set-animation-layer pop-up menu.
  1459. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1460. CreateSetAnimationLayerPopupMenu(contextMenu.setLayerSub, pTrack);
  1461. contextMenu.setLayerSub.setTitle("Set Animation Layer");
  1462. contextMenu.main.addMenu(&contextMenu.setLayerSub);
  1463. bAppended = true;
  1464. }
  1465. }
  1466. // Track color
  1467. if (bOnTrack)
  1468. {
  1469. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1470. contextMenu.main.addAction("Customize Track Color...")->setData(eMI_CustomizeTrackColor);
  1471. if (pTrack->HasCustomColor())
  1472. {
  1473. contextMenu.main.addAction("Clear Custom Track Color")->setData(eMI_ClearCustomTrackColor);
  1474. }
  1475. bAppended = true;
  1476. }
  1477. // Track hide/unhide flags
  1478. if (bOnNode && !pNode->IsGroupNode())
  1479. {
  1480. AddMenuSeperatorConditional(contextMenu.main, bAppended);
  1481. const QString manageTracksMenuName = "Toggle Tracks";
  1482. auto manageTracksAction = contextMenu.main.addAction(manageTracksMenuName);
  1483. bool bAppendedTrackFlag = false;
  1484. const unsigned int numChildren = animNode->GetChildCount();
  1485. for (unsigned int childIndex = 0; childIndex < numChildren; ++childIndex)
  1486. {
  1487. CTrackViewNode* pChild = animNode->GetChild(childIndex);
  1488. if (pChild->GetNodeType() == eTVNT_Track)
  1489. {
  1490. CTrackViewTrack* pTrack2 = static_cast<CTrackViewTrack*>(pChild);
  1491. if (pTrack2->IsSubTrack())
  1492. {
  1493. continue;
  1494. }
  1495. QAction* a = contextMenu.main.addAction(QString(" %1").arg(pTrack2->GetName().c_str()));
  1496. a->setData(eMI_ShowHideBase + childIndex);
  1497. a->setCheckable(true);
  1498. a->setChecked(!pTrack2->IsHidden());
  1499. bAppendedTrackFlag = true;
  1500. }
  1501. }
  1502. manageTracksAction->setEnabled(bAppendedTrackFlag); // Disable this submenu if no tracks were added.
  1503. bAppended = bAppendedTrackFlag || bAppended;
  1504. }
  1505. return 0;
  1506. }
  1507. //////////////////////////////////////////////////////////////////////////
  1508. int CTrackViewNodesCtrl::ShowPopupMenuMultiSelection(SContextMenu& contextMenu)
  1509. {
  1510. QList<QTreeWidgetItem*> records = ui->treeWidget->selectedItems();
  1511. bool bNodeSelected = false;
  1512. for (int currentNode = 0; currentNode < records.size(); ++currentNode)
  1513. {
  1514. CRecord* pItemInfo = (CRecord*)records[currentNode];
  1515. if (pItemInfo->GetNode()->GetNodeType() == eTVNT_AnimNode)
  1516. {
  1517. bNodeSelected = true;
  1518. }
  1519. }
  1520. if (bNodeSelected)
  1521. {
  1522. contextMenu.main.addAction("Copy Selected Nodes")->setData(eMI_CopySelectedNodes);
  1523. }
  1524. contextMenu.main.addAction("Remove Selected Nodes/Tracks")->setData(eMI_RemoveSelected);
  1525. if (bNodeSelected)
  1526. {
  1527. contextMenu.main.addSeparator();
  1528. contextMenu.main.addAction("Select In Viewport")->setData(eMI_SelectInViewport);
  1529. // Importing FBX is currently only supported on legacy entities. Legacy
  1530. // sequences contain only legacy Cry entities and no AZ component entities.
  1531. CAnimationContext* context = GetIEditor()->GetAnimation();
  1532. AZ_Assert(context, "Expected valid GetIEditor()->GetAnimation()");
  1533. if (context)
  1534. {
  1535. CTrackViewSequence* sequence = context->GetSequence();
  1536. if (sequence && sequence->GetSequenceType() == SequenceType::Legacy)
  1537. {
  1538. contextMenu.main.addAction("Import From FBX File")->setData(eMI_ImportFromFBX);
  1539. contextMenu.main.addAction("Save To FBX File")->setData(eMI_SaveToFBX);
  1540. }
  1541. }
  1542. }
  1543. return 0;
  1544. }
  1545. //////////////////////////////////////////////////////////////////////////
  1546. int CTrackViewNodesCtrl::ShowPopupMenu([[maybe_unused]] QPoint point, const CRecord* record)
  1547. {
  1548. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1549. if (!sequence)
  1550. {
  1551. return 0;
  1552. }
  1553. SContextMenu contextMenu;
  1554. CTrackViewNode* pNode = record ? record->GetNode() : nullptr;
  1555. if (!pNode)
  1556. {
  1557. return 0;
  1558. }
  1559. if (ui->treeWidget->selectedItems().size() > 1)
  1560. {
  1561. ShowPopupMenuMultiSelection(contextMenu);
  1562. }
  1563. else if (pNode)
  1564. {
  1565. ShowPopupMenuSingleSelection(contextMenu, sequence, pNode);
  1566. }
  1567. if (m_bEditLock)
  1568. {
  1569. SetPopupMenuLock(&contextMenu.main);
  1570. }
  1571. QAction* action = contextMenu.main.exec(QCursor::pos());
  1572. int ret = action ? action->data().toInt() : 0;
  1573. return ret;
  1574. }
  1575. //////////////////////////////////////////////////////////////////////////
  1576. // Add tracks that can be added to the given animation node to the
  1577. // internal track menu tree data structure rooted at menuAddTrack
  1578. bool CTrackViewNodesCtrl::FillAddTrackMenu(STrackMenuTreeNode& menuAddTrack, const CTrackViewAnimNode* animNode)
  1579. {
  1580. bool bTracksToAdd = false;
  1581. const AnimNodeType nodeType = animNode->GetType();
  1582. int paramCount = 0;
  1583. IAnimNode::AnimParamInfos animatableProperties;
  1584. CTrackViewNode* parentNode = animNode->GetParentNode();
  1585. // all AZ::Entity entities are animated through components. Component nodes always have a parent - the containing AZ::Entity
  1586. if (nodeType == AnimNodeType::Component && parentNode)
  1587. {
  1588. // component node - query all the animatable tracks via an EBus request
  1589. // all AnimNodeType::Component are parented to AnimNodeType::AzEntityNodes - get the parent to get it's AZ::EntityId to use for the EBus request
  1590. if (parentNode->GetNodeType() == eTVNT_AnimNode)
  1591. {
  1592. // this cast is safe because we check that the type is eTVNT_AnimNode
  1593. const AZ::EntityId azEntityId = static_cast<CTrackViewAnimNode*>(parentNode)->GetAzEntityId();
  1594. // query the animatable component properties from the Sequence Component
  1595. Maestro::EditorSequenceComponentRequestBus::Event(const_cast<CTrackViewAnimNode*>(animNode)->GetSequence()->GetSequenceComponentEntityId(),
  1596. &Maestro::EditorSequenceComponentRequestBus::Events::GetAllAnimatablePropertiesForComponent,
  1597. animatableProperties, azEntityId, animNode->GetComponentId());
  1598. paramCount = static_cast<int>(animatableProperties.size());
  1599. }
  1600. }
  1601. else
  1602. {
  1603. // legacy Entity
  1604. paramCount = animNode->GetParamCount();
  1605. }
  1606. for (int i = 0; i < paramCount; ++i)
  1607. {
  1608. CAnimParamType paramType;
  1609. QString name;
  1610. // get the animatable param name
  1611. if (nodeType == AnimNodeType::Component)
  1612. {
  1613. // Skip over any hidden params
  1614. if (animatableProperties[i].flags & IAnimNode::ESupportedParamFlags::eSupportedParamFlags_Hidden)
  1615. {
  1616. continue;
  1617. }
  1618. paramType = animatableProperties[i].paramType;
  1619. }
  1620. else
  1621. {
  1622. // legacy node
  1623. paramType = animNode->GetParamType(i);
  1624. if (paramType == AnimParamType::Invalid)
  1625. {
  1626. continue;
  1627. }
  1628. IAnimNode::ESupportedParamFlags paramFlags = animNode->GetParamFlags(paramType);
  1629. CTrackViewTrack* pTrack = animNode->GetTrackForParameter(paramType);
  1630. if (pTrack && !(paramFlags & IAnimNode::eSupportedParamFlags_MultipleTracks))
  1631. {
  1632. continue;
  1633. }
  1634. }
  1635. name = QString::fromUtf8(animNode->GetParamName(paramType).c_str());
  1636. QStringList splitName = name.split("/", Qt::SkipEmptyParts);
  1637. STrackMenuTreeNode* pCurrentNode = &menuAddTrack;
  1638. for (const QString& segment : splitName)
  1639. {
  1640. auto findIter = pCurrentNode->children.find(segment);
  1641. if (findIter != pCurrentNode->children.end())
  1642. {
  1643. pCurrentNode = findIter->second.get();
  1644. }
  1645. //else {} - keep current node to avoid unnecessary nesting
  1646. }
  1647. // only add tracks to the that STrackMenuTreeNode tree that haven't already been added
  1648. CTrackViewTrackBundle matchedTracks = animNode->GetTracksByParam(paramType);
  1649. if (matchedTracks.GetCount() == 0 && !splitName.isEmpty())
  1650. {
  1651. STrackMenuTreeNode* pParamNode = new STrackMenuTreeNode;
  1652. pCurrentNode->children[splitName.back()] = std::unique_ptr<STrackMenuTreeNode>(pParamNode);
  1653. pParamNode->paramType = paramType;
  1654. bTracksToAdd = true;
  1655. }
  1656. }
  1657. return bTracksToAdd;
  1658. }
  1659. //////////////////////////////////////////////////////////////////////////
  1660. //
  1661. // FillAddTrackMenu fills the data structure for tracks to add (a STrackMenuTreeNode tree)
  1662. // CreateAddTrackMenuRec actually creates the Qt submenu from this data structure
  1663. //
  1664. void CTrackViewNodesCtrl::CreateAddTrackMenuRec(QMenu& parent, const QString& name, CTrackViewAnimNode* animNode, struct STrackMenuTreeNode& node, unsigned int& currentId)
  1665. {
  1666. if (node.paramType.GetType() == AnimParamType::Invalid)
  1667. {
  1668. node.menu.setTitle(name);
  1669. parent.addMenu(&node.menu);
  1670. for (auto iter = node.children.begin(); iter != node.children.end(); ++iter)
  1671. {
  1672. CreateAddTrackMenuRec(node.menu, iter->first, animNode, *iter->second.get(), currentId);
  1673. }
  1674. }
  1675. else
  1676. {
  1677. m_menuParamTypeMap[currentId] = node.paramType;
  1678. QVariant paramTypeMenuID;
  1679. paramTypeMenuID.setValue(eMI_AddTrackBase + currentId);
  1680. parent.addAction(name)->setData(paramTypeMenuID);
  1681. ++currentId;
  1682. }
  1683. }
  1684. //////////////////////////////////////////////////////////////////////////
  1685. void CTrackViewNodesCtrl::SetPopupMenuLock(QMenu* menu)
  1686. {
  1687. if (!m_bEditLock || !menu)
  1688. {
  1689. return;
  1690. }
  1691. UINT count = menu->actions().size();
  1692. for (UINT i = 0; i < count; ++i)
  1693. {
  1694. QAction* a = menu->actions()[i];
  1695. QString menuString = a->text();
  1696. if (menuString != "Expand" && menuString != "Collapse")
  1697. {
  1698. a->setEnabled(false);
  1699. }
  1700. }
  1701. }
  1702. //////////////////////////////////////////////////////////////////////////
  1703. float CTrackViewNodesCtrl::SaveVerticalScrollPos() const
  1704. {
  1705. int sbMin = ui->treeWidget->verticalScrollBar()->minimum();
  1706. int sbMax = ui->treeWidget->verticalScrollBar()->maximum();
  1707. return float(ui->treeWidget->verticalScrollBar()->value() - sbMin) / std::max(float(sbMax - sbMin), 1.0f);
  1708. }
  1709. //////////////////////////////////////////////////////////////////////////
  1710. void CTrackViewNodesCtrl::RestoreVerticalScrollPos(float fScrollPos)
  1711. {
  1712. int sbMin = ui->treeWidget->verticalScrollBar()->minimum();
  1713. int sbMax = ui->treeWidget->verticalScrollBar()->maximum();
  1714. int newScrollPos = qRound(fScrollPos * (sbMax - sbMin)) + sbMin;
  1715. ui->treeWidget->verticalScrollBar()->setValue(newScrollPos);
  1716. }
  1717. //////////////////////////////////////////////////////////////////////////
  1718. void CTrackViewNodesCtrl::FillAutoCompletionListForFilter()
  1719. {
  1720. QStringList strings;
  1721. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1722. if (sequence)
  1723. {
  1724. ui->noitems->hide();
  1725. ui->treeWidget->show();
  1726. ui->searchField->show();
  1727. ui->searchCount->show();
  1728. CTrackViewAnimNodeBundle animNodes = sequence->GetAllAnimNodes();
  1729. const unsigned int animNodeCount = animNodes.GetCount();
  1730. for (unsigned int i = 0; i < animNodeCount; ++i)
  1731. {
  1732. strings << QString::fromUtf8(animNodes.GetNode(i)->GetName().c_str());
  1733. }
  1734. }
  1735. else
  1736. {
  1737. ui->noitems->show();
  1738. ui->treeWidget->hide();
  1739. ui->searchField->hide();
  1740. ui->searchCount->hide();
  1741. }
  1742. QCompleter* c = new QCompleter(strings, this);
  1743. c->setCaseSensitivity(Qt::CaseInsensitive);
  1744. c->setCompletionMode(QCompleter::InlineCompletion);
  1745. ui->searchField->setCompleter(c);
  1746. }
  1747. //////////////////////////////////////////////////////////////////////////
  1748. void CTrackViewNodesCtrl::OnFilterChange(const QString& text)
  1749. {
  1750. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1751. if (sequence)
  1752. {
  1753. m_currentMatchIndex = 0; // Reset the match index
  1754. m_matchCount = 0; // and the count.
  1755. if (!text.isEmpty())
  1756. {
  1757. QList<QTreeWidgetItem*> items = ui->treeWidget->findItems(text, Qt::MatchContains | Qt::MatchRecursive);
  1758. CTrackViewAnimNodeBundle animNodes = sequence->GetAllAnimNodes();
  1759. m_matchCount = items.size(); // and the count.
  1760. if (!items.empty())
  1761. {
  1762. ui->treeWidget->selectionModel()->clear();
  1763. items.front()->setSelected(true);
  1764. }
  1765. }
  1766. QString matchCountText = QString("%1/%2").arg(m_matchCount == 0 ? 0 : 1).arg(m_matchCount); // One-based indexing
  1767. ui->searchCount->setText(matchCountText);
  1768. }
  1769. }
  1770. //////////////////////////////////////////////////////////////////////////
  1771. int CTrackViewNodesCtrl::GetMatNameAndSubMtlIndexFromName(QString& matName, const char* nodeName)
  1772. {
  1773. if (const char* pCh = strstr(nodeName, ".["))
  1774. {
  1775. char matPath[MAX_PATH];
  1776. azstrncpy(matPath, AZ_ARRAY_SIZE(matPath), nodeName, (size_t)(pCh - nodeName));
  1777. matName = matPath;
  1778. pCh += 2;
  1779. if ((*pCh) != 0)
  1780. {
  1781. const int index = atoi(pCh) - 1;
  1782. return index;
  1783. }
  1784. }
  1785. else
  1786. {
  1787. matName = nodeName;
  1788. }
  1789. return -1;
  1790. }
  1791. //////////////////////////////////////////////////////////////////////////
  1792. void CTrackViewNodesCtrl::ShowNextResult()
  1793. {
  1794. if (m_matchCount > 1)
  1795. {
  1796. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1797. if (sequence && !ui->searchField->text().isEmpty())
  1798. {
  1799. QList<QTreeWidgetItem*> items = ui->treeWidget->findItems(ui->searchField->text(), Qt::MatchContains | Qt::MatchRecursive);
  1800. CTrackViewAnimNodeBundle animNodes = sequence->GetAllAnimNodes();
  1801. m_matchCount = items.size(); // and the count.
  1802. if (!items.empty())
  1803. {
  1804. ++m_currentMatchIndex;
  1805. m_currentMatchIndex = m_currentMatchIndex % m_matchCount;
  1806. ui->treeWidget->selectionModel()->clear();
  1807. items[m_currentMatchIndex]->setSelected(true);
  1808. }
  1809. QString matchCountText = QString("%1/%2").arg(m_currentMatchIndex + 1).arg(m_matchCount); // One-based indexing
  1810. ui->searchCount->setText(matchCountText);
  1811. }
  1812. }
  1813. }
  1814. void CTrackViewNodesCtrl::Update()
  1815. {
  1816. // Update the Track UI elements with the latest names of the Tracks.
  1817. // In some cases the Track names (param names) may not be available at the time
  1818. // of the Sequence activation because they come from the animated entities (which may not be active).
  1819. // So just update them once a frame to make sure they are the latest.
  1820. for (auto iter = m_nodeToRecordMap.begin(); iter != m_nodeToRecordMap.end(); ++iter)
  1821. {
  1822. const CTrackViewNode* node = iter->first;
  1823. CTrackViewNodesCtrl::CRecord* record = iter->second;
  1824. if (node && record)
  1825. {
  1826. if (node->GetNodeType() == eTVNT_Track)
  1827. {
  1828. const CTrackViewAnimNode* track = static_cast<const CTrackViewAnimNode*>(node);
  1829. if (track)
  1830. {
  1831. record->setText(0, QString::fromUtf8(track->GetName().c_str()));
  1832. }
  1833. }
  1834. }
  1835. }
  1836. }
  1837. void CTrackViewNodesCtrl::keyPressEvent(QKeyEvent* event)
  1838. {
  1839. // HAVE TO INCLUDE CASES FOR THESE IN THE ShortcutOverride handler in ::event() below
  1840. switch (event->key())
  1841. {
  1842. case Qt::Key_Z:
  1843. if (event->modifiers() == Qt::ControlModifier)
  1844. {
  1845. GetIEditor()->Undo();
  1846. event->accept();
  1847. }
  1848. break;
  1849. default:
  1850. break;
  1851. }
  1852. }
  1853. bool CTrackViewNodesCtrl::event(QEvent* e)
  1854. {
  1855. if (e->type() == QEvent::ShortcutOverride)
  1856. {
  1857. // since we respond to the following things, let Qt know so that shortcuts don't override us
  1858. bool respondsToEvent = false;
  1859. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
  1860. if (keyEvent->key() == Qt::Key_Z && keyEvent->modifiers() == Qt::ControlModifier)
  1861. {
  1862. respondsToEvent = true;
  1863. }
  1864. if (respondsToEvent)
  1865. {
  1866. e->accept();
  1867. return true;
  1868. }
  1869. }
  1870. return QWidget::event(e);
  1871. }
  1872. void CTrackViewNodesCtrl::CreateSetAnimationLayerPopupMenu(QMenu& menuSetLayer, CTrackViewTrack* pTrack) const
  1873. {
  1874. // First collect layers already in use.
  1875. std::vector<int> layersInUse;
  1876. CTrackViewTrackBundle lookAtTracks = pTrack->GetAnimNode()->GetTracksByParam(AnimParamType::LookAt);
  1877. assert(lookAtTracks.GetCount() <= 1);
  1878. if (lookAtTracks.GetCount() > 0)
  1879. {
  1880. const int kDefaultLookIKLayer = 15;
  1881. int lookIKLayerIndex = lookAtTracks.GetTrack(0)->GetAnimationLayerIndex();
  1882. if (lookIKLayerIndex < 0) // Not set before, use the default instead.
  1883. {
  1884. lookIKLayerIndex = kDefaultLookIKLayer;
  1885. }
  1886. layersInUse.push_back(lookIKLayerIndex);
  1887. }
  1888. CTrackViewTrackBundle animationTracks = pTrack->GetAnimNode()->GetTracksByParam(AnimParamType::Animation);
  1889. const unsigned int numAnimationTracks = animationTracks.GetCount();
  1890. for (unsigned int i = 0; i < numAnimationTracks; ++i)
  1891. {
  1892. CTrackViewTrack* pAnimationTrack = animationTracks.GetTrack(i);
  1893. if (pAnimationTrack)
  1894. {
  1895. const int kAdditiveLayerOffset = 6;
  1896. int layerIndex = pAnimationTrack->GetAnimationLayerIndex();
  1897. if (layerIndex < 0) // Not set before, use the default instead.
  1898. {
  1899. layerIndex = i == 0 ? 0 : kAdditiveLayerOffset + i;
  1900. }
  1901. layersInUse.push_back(layerIndex);
  1902. }
  1903. }
  1904. // Add layer items.
  1905. for (int i = 0; i < 16; ++i)
  1906. {
  1907. QString layerText = QString("Layer #%1").arg(i);
  1908. QAction* a = menuSetLayer.addAction(layerText);
  1909. a->setData(eMI_SetAnimationLayerBase + i);
  1910. a->setCheckable(true);
  1911. a->setChecked(pTrack->GetAnimationLayerIndex() == i);
  1912. a->setEnabled(!stl::find(layersInUse, i));
  1913. }
  1914. }
  1915. //////////////////////////////////////////////////////////////////////////
  1916. void CTrackViewNodesCtrl::CustomizeTrackColor(CTrackViewTrack* pTrack)
  1917. {
  1918. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1919. if (!sequence)
  1920. {
  1921. return;
  1922. }
  1923. AZ::Color defaultColor;
  1924. if (pTrack->HasCustomColor())
  1925. {
  1926. ColorB customColor = pTrack->GetCustomColor();
  1927. defaultColor = AZ::Color(customColor.r, customColor.g, customColor.b, customColor.a);
  1928. }
  1929. const AZ::Color color = AzQtComponents::ColorPicker::getColor(AzQtComponents::ColorPicker::Configuration::RGB, defaultColor, tr("Select Color"));
  1930. if (color != defaultColor)
  1931. {
  1932. AzToolsFramework::ScopedUndoBatch undoBatch("Customize Track Color");
  1933. pTrack->SetCustomColor(ColorB(color.GetR8(), color.GetG8(), color.GetB8()));
  1934. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1935. UpdateDopeSheet();
  1936. }
  1937. }
  1938. //////////////////////////////////////////////////////////////////////////
  1939. void CTrackViewNodesCtrl::ClearCustomTrackColor(CTrackViewTrack* pTrack)
  1940. {
  1941. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1942. if (!sequence)
  1943. {
  1944. return;
  1945. }
  1946. AzToolsFramework::ScopedUndoBatch undoBatch("Clear Custom Track Color");
  1947. pTrack->ClearCustomColor();
  1948. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1949. UpdateDopeSheet();
  1950. }
  1951. //////////////////////////////////////////////////////////////////////////
  1952. void CTrackViewNodesCtrl::paintEvent(QPaintEvent* event)
  1953. {
  1954. QWidget::paintEvent(event);
  1955. UpdateDopeSheet();
  1956. }
  1957. //////////////////////////////////////////////////////////////////////////
  1958. CTrackViewNodesCtrl::CRecord* CTrackViewNodesCtrl::GetNodeRecord(const CTrackViewNode* pNode) const
  1959. {
  1960. auto findIter = m_nodeToRecordMap.find(pNode);
  1961. if (findIter == m_nodeToRecordMap.end())
  1962. {
  1963. return nullptr;
  1964. }
  1965. assert (findIter->second->GetNode() == pNode);
  1966. return findIter->second;
  1967. }
  1968. //////////////////////////////////////////////////////////////////////////
  1969. void CTrackViewNodesCtrl::UpdateDopeSheet()
  1970. {
  1971. UpdateRecordVisibility();
  1972. if (m_pDopeSheet)
  1973. {
  1974. m_pDopeSheet->update();
  1975. }
  1976. }
  1977. //////////////////////////////////////////////////////////////////////////
  1978. // Workaround: CXTPReportRecord::IsVisible is
  1979. // unreliable after the last visible element
  1980. //////////////////////////////////////////////////////////////////////////
  1981. void CTrackViewNodesCtrl::UpdateRecordVisibility()
  1982. {
  1983. // Mark all records invisible
  1984. for (auto iter = m_nodeToRecordMap.begin(); iter != m_nodeToRecordMap.end(); ++iter)
  1985. {
  1986. iter->second->m_bVisible = ui->treeWidget->visualItemRect(iter->second).isValid();
  1987. }
  1988. }
  1989. //////////////////////////////////////////////////////////////////////////
  1990. void CTrackViewNodesCtrl::OnNodeChanged(CTrackViewNode* pNode, ITrackViewSequenceListener::ENodeChangeType type)
  1991. {
  1992. if (pNode->GetSequence() != GetIEditor()->GetAnimation()->GetSequence())
  1993. {
  1994. return;
  1995. }
  1996. if (!m_bIgnoreNotifications)
  1997. {
  1998. CTrackViewNode* pParentNode = pNode->GetParentNode();
  1999. CRecord* pNodeRecord = GetNodeRecord(pNode);
  2000. CRecord* pParentNodeRecord = pParentNode ? GetNodeRecord(pParentNode) : nullptr;
  2001. float storedScrollPosition = SaveVerticalScrollPos();
  2002. switch (type)
  2003. {
  2004. case ITrackViewSequenceListener::eNodeChangeType_Added:
  2005. case ITrackViewSequenceListener::eNodeChangeType_Unhidden:
  2006. if (pParentNodeRecord)
  2007. {
  2008. AddNodeRecord(pParentNodeRecord, pNode);
  2009. }
  2010. break;
  2011. case ITrackViewSequenceListener::eNodeChangeType_Removed:
  2012. case ITrackViewSequenceListener::eNodeChangeType_Hidden:
  2013. if (pNodeRecord)
  2014. {
  2015. EraseNodeRecordRec(pNode);
  2016. delete pNodeRecord;
  2017. }
  2018. break;
  2019. case ITrackViewSequenceListener::eNodeChangeType_Expanded:
  2020. if (pNodeRecord)
  2021. {
  2022. pNodeRecord->setExpanded(true);
  2023. }
  2024. break;
  2025. case ITrackViewSequenceListener::eNodeChangeType_Collapsed:
  2026. if (pNodeRecord)
  2027. {
  2028. pNodeRecord->setExpanded(false);
  2029. }
  2030. break;
  2031. case ITrackViewSequenceListener::eNodeChangeType_Disabled:
  2032. case ITrackViewSequenceListener::eNodeChangeType_Enabled:
  2033. case ITrackViewSequenceListener::eNodeChangeType_Muted:
  2034. case ITrackViewSequenceListener::eNodeChangeType_Unmuted:
  2035. case ITrackViewSequenceListener::eNodeChangeType_NodeOwnerChanged:
  2036. if (pNodeRecord)
  2037. {
  2038. UpdateNodeRecord(pNodeRecord);
  2039. }
  2040. }
  2041. switch (type)
  2042. {
  2043. case ITrackViewSequenceListener::eNodeChangeType_Added:
  2044. case ITrackViewSequenceListener::eNodeChangeType_Unhidden:
  2045. case ITrackViewSequenceListener::eNodeChangeType_Removed:
  2046. case ITrackViewSequenceListener::eNodeChangeType_Hidden:
  2047. case ITrackViewSequenceListener::eNodeChangeType_Expanded:
  2048. case ITrackViewSequenceListener::eNodeChangeType_Collapsed:
  2049. update();
  2050. RestoreVerticalScrollPos(storedScrollPosition);
  2051. break;
  2052. case eNodeChangeType_SetAsActiveDirector:
  2053. update();
  2054. break;
  2055. }
  2056. }
  2057. else
  2058. {
  2059. m_bNeedReload = true;
  2060. }
  2061. }
  2062. //////////////////////////////////////////////////////////////////////////
  2063. void CTrackViewNodesCtrl::OnNodeRenamed(CTrackViewNode* pNode, [[maybe_unused]] const char* pOldName)
  2064. {
  2065. if (!m_bIgnoreNotifications)
  2066. {
  2067. CRecord* pNodeRecord = GetNodeRecord(pNode);
  2068. pNodeRecord->setText(0, QString::fromUtf8(pNode->GetName().c_str()));
  2069. update();
  2070. }
  2071. else
  2072. {
  2073. m_bNeedReload = true;
  2074. }
  2075. }
  2076. //////////////////////////////////////////////////////////////////////////
  2077. void CTrackViewNodesCtrl::BeginUndoTransaction()
  2078. {
  2079. m_bNeedReload = false;
  2080. m_bIgnoreNotifications = true;
  2081. m_storedScrollPosition = SaveVerticalScrollPos();
  2082. }
  2083. //////////////////////////////////////////////////////////////////////////
  2084. void CTrackViewNodesCtrl::EndUndoTransaction()
  2085. {
  2086. m_bIgnoreNotifications = false;
  2087. if (m_bNeedReload)
  2088. {
  2089. Reload();
  2090. RestoreVerticalScrollPos(m_storedScrollPosition);
  2091. m_bNeedReload = false;
  2092. }
  2093. UpdateDopeSheet();
  2094. }
  2095. QIcon CTrackViewNodesCtrl::TrackViewIcon(const CTrackViewTrack* pTrack)
  2096. {
  2097. const QIcon defaultIcon(QStringLiteral(":/nodes/tvnodes-13.png"));
  2098. if (!pTrack)
  2099. {
  2100. return defaultIcon;
  2101. }
  2102. const CAnimParamType paramType = pTrack->GetParameterType();
  2103. const AnimValueType valueType = pTrack->GetValueType();
  2104. const AnimNodeType nodeType = pTrack->GetAnimNode()->GetType();
  2105. if (nodeType == AnimNodeType::RadialBlur || nodeType == AnimNodeType::ColorCorrection || nodeType == AnimNodeType::DepthOfField ||
  2106. nodeType == AnimNodeType::ShadowSetup)
  2107. {
  2108. return defaultIcon;
  2109. }
  2110. switch (valueType)
  2111. {
  2112. case AnimValueType::CharacterAnim:
  2113. case AnimValueType::AssetBlend:
  2114. return QIcon(QStringLiteral(":/nodes/tvnodes-10.png"));
  2115. }
  2116. AnimParamType type = paramType.GetType();
  2117. switch (type)
  2118. {
  2119. case AnimParamType::Position:
  2120. return QIcon(QStringLiteral(":/nodes/tvnodes-03.png"));
  2121. case AnimParamType::Rotation:
  2122. return QIcon(QStringLiteral(":/nodes/tvnodes-04.png"));
  2123. case AnimParamType::Scale:
  2124. return QIcon(QStringLiteral(":/nodes/tvnodes-05.png"));
  2125. case AnimParamType::Event:
  2126. case AnimParamType::TrackEvent:
  2127. return QIcon(QStringLiteral(":/nodes/tvnodes-06.png"));
  2128. case AnimParamType::Visibility:
  2129. return QIcon(QStringLiteral(":/nodes/tvnodes-07.png"));
  2130. case AnimParamType::Camera:
  2131. return QIcon(QStringLiteral(":/nodes/tvnodes-08.png"));
  2132. case AnimParamType::Sound:
  2133. return QIcon(QStringLiteral(":/nodes/tvnodes-09.png"));
  2134. case AnimParamType::Animation:
  2135. case AnimParamType::TimeRanges:
  2136. return QIcon(QStringLiteral(":/nodes/tvnodes-10.png"));
  2137. case AnimParamType::Sequence:
  2138. return QIcon(QStringLiteral(":/nodes/tvnodes-11.png"));
  2139. case AnimParamType::Capture:
  2140. return QIcon(QStringLiteral(":/nodes/tvnodes-25.png"));
  2141. case AnimParamType::Console:
  2142. return QIcon(QStringLiteral(":/nodes/tvnodes-15.png"));
  2143. case AnimParamType::LookAt:
  2144. return QIcon(QStringLiteral(":/nodes/tvnodes-17.png"));
  2145. case AnimParamType::TimeWarp:
  2146. return QIcon(QStringLiteral(":/nodes/tvnodes-22.png"));
  2147. case AnimParamType::CommentText:
  2148. return QIcon(QStringLiteral(":/nodes/tvnodes-23.png"));
  2149. case AnimParamType::ShakeMultiplier:
  2150. [[fallthrough]];
  2151. case AnimParamType::TransformNoise:
  2152. return QIcon(QStringLiteral(":/nodes/tvnodes-28.png"));
  2153. default:
  2154. case AnimParamType::Float:
  2155. break;
  2156. }
  2157. return defaultIcon;
  2158. }
  2159. QIcon CTrackViewNodesCtrl::TrackViewNodeIcon(AnimNodeType type)
  2160. {
  2161. switch (type)
  2162. {
  2163. case AnimNodeType::AzEntity:
  2164. return QIcon(QStringLiteral(":/nodes/tvnodes-29.png"));
  2165. case AnimNodeType::Director:
  2166. return QIcon(QStringLiteral(":/nodes/tvnodes-27.png"));
  2167. case AnimNodeType::CVar:
  2168. return QIcon(QStringLiteral(":/nodes/tvnodes-15.png"));
  2169. case AnimNodeType::ScriptVar:
  2170. return QIcon(QStringLiteral(":/nodes/tvnodes-14.png"));
  2171. case AnimNodeType::Material:
  2172. return QIcon(QStringLiteral(":/nodes/tvnodes-16.png"));
  2173. case AnimNodeType::Event:
  2174. return QIcon(QStringLiteral(":/nodes/tvnodes-06.png"));
  2175. case AnimNodeType::Group:
  2176. return QIcon(QStringLiteral(":/nodes/tvnodes-01.png"));
  2177. case AnimNodeType::Layer:
  2178. return QIcon(QStringLiteral(":/nodes/tvnodes-20.png"));
  2179. case AnimNodeType::Comment:
  2180. return QIcon(QStringLiteral(":/nodes/tvnodes-23.png"));
  2181. case AnimNodeType::Light:
  2182. return QIcon(QStringLiteral(":/nodes/tvnodes-18.png"));
  2183. case AnimNodeType::ShadowSetup:
  2184. return QIcon(QStringLiteral(":/nodes/tvnodes-24.png"));
  2185. }
  2186. return QIcon(QStringLiteral(":/nodes/tvnodes-21.png"));
  2187. }
  2188. //////////////////////////////////////////////////////////////////////////
  2189. QIcon CTrackViewNodesCtrl::GetIconForTrack(const CTrackViewTrack* pTrack)
  2190. {
  2191. return TrackViewIcon(pTrack);
  2192. }
  2193. //////////////////////////////////////////////////////////////////////////
  2194. void CTrackViewNodesCtrl::OnKeysChanged(CTrackViewSequence* sequence)
  2195. {
  2196. if (!m_bIgnoreNotifications && sequence && sequence == GetIEditor()->GetAnimation()->GetSequence())
  2197. {
  2198. UpdateDopeSheet();
  2199. }
  2200. }
  2201. //////////////////////////////////////////////////////////////////////////
  2202. void CTrackViewNodesCtrl::OnKeySelectionChanged(CTrackViewSequence* sequence)
  2203. {
  2204. OnKeysChanged(sequence);
  2205. }
  2206. //////////////////////////////////////////////////////////////////////////
  2207. void CTrackViewNodesCtrl::OnNodeSelectionChanged(CTrackViewSequence* sequence)
  2208. {
  2209. if (m_bSelectionChanging)
  2210. {
  2211. return;
  2212. }
  2213. if (!m_bIgnoreNotifications && sequence && sequence == GetIEditor()->GetAnimation()->GetSequence())
  2214. {
  2215. UpdateDopeSheet();
  2216. CTrackViewAnimNodeBundle animNodes = sequence->GetAllAnimNodes();
  2217. const uint numNodes = animNodes.GetCount();
  2218. for (uint i = 0; i < numNodes; ++i)
  2219. {
  2220. CTrackViewAnimNode* pNode = animNodes.GetNode(i);
  2221. if (pNode->IsSelected())
  2222. {
  2223. SelectRow(pNode, false, false);
  2224. }
  2225. else
  2226. {
  2227. DeselectRow(pNode);
  2228. }
  2229. }
  2230. }
  2231. }
  2232. //////////////////////////////////////////////////////////////////////////
  2233. void CTrackViewNodesCtrl::SelectRow(CTrackViewNode* pNode, const bool bEnsureVisible, const bool bDeselectOtherRows)
  2234. {
  2235. std::unordered_map<const CTrackViewNode*, CRecord*>::const_iterator it = m_nodeToRecordMap.find(pNode);
  2236. if (it != m_nodeToRecordMap.end())
  2237. {
  2238. if (bDeselectOtherRows)
  2239. {
  2240. ui->treeWidget->selectionModel()->clear();
  2241. }
  2242. (*it).second->setSelected(true);
  2243. if (bEnsureVisible)
  2244. {
  2245. ui->treeWidget->scrollToItem((*it).second);
  2246. }
  2247. }
  2248. }
  2249. //////////////////////////////////////////////////////////////////////////
  2250. void CTrackViewNodesCtrl::DeselectRow(CTrackViewNode* pNode)
  2251. {
  2252. std::unordered_map<const CTrackViewNode*, CRecord*>::const_iterator it = m_nodeToRecordMap.find(pNode);
  2253. if (it != m_nodeToRecordMap.end())
  2254. {
  2255. (*it).second->setSelected(false);
  2256. }
  2257. }
  2258. //////////////////////////////////////////////////////////////////////////
  2259. void CTrackViewNodesCtrl::EraseNodeRecordRec(CTrackViewNode* pNode)
  2260. {
  2261. m_nodeToRecordMap.erase(pNode);
  2262. const unsigned int numChildren = pNode->GetChildCount();
  2263. for (unsigned int i = 0; i < numChildren; ++i)
  2264. {
  2265. EraseNodeRecordRec(pNode->GetChild(i));
  2266. }
  2267. }
  2268. #include <TrackView/moc_TrackViewNodes.cpp>