/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "stdafx.h" #include "EditorDefs.h" #include "Resource.h" #include "UiEditorAnimationBus.h" #include "UiAnimViewNodes.h" #include "UiAnimViewDopeSheetBase.h" #include "UiAnimViewUndo.h" #include "StringDlg.h" #include "UiAnimViewDialog.h" #include "UiAVEventsDialog.h" #include "Clipboard.h" #include #include "Objects/EntityObject.h" #include "ViewManager.h" #include "RenderViewport.h" #include "Export/ExportManager.h" #include "Objects/ObjectLayerManager.h" #include "QtUtilWin.h" #include "QtUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include CUiAnimViewNodesCtrl::CRecord::CRecord(CUiAnimViewNode* pNode /*= nullptr*/) : m_pNode(pNode) , m_bVisible(false) { if (pNode) { QVariant v; v.setValue(pNode); setData(0, Qt::UserRole, v); } } class CUiAnimViewNodesCtrlDelegate : public QStyledItemDelegate { public: CUiAnimViewNodesCtrlDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { bool enabled = index.data(CUiAnimViewNodesCtrl::CRecord::EnableRole).toBool(); QStyleOptionViewItem opt = option; if (!enabled) { opt.state &= ~QStyle::State_Enabled; } QStyledItemDelegate::paint(painter, opt, index); } }; class CUiAnimViewNodesTreeWidget : public QTreeWidget { public: CUiAnimViewNodesTreeWidget(QWidget* parent) : QTreeWidget(parent) , m_controller(nullptr) { setItemDelegate(new CUiAnimViewNodesCtrlDelegate(this)); } void setController(CUiAnimViewNodesCtrl* p) { m_controller = p; } protected: void dragMoveEvent(QDragMoveEvent* event) { // For now we do not support any drag and drop in the Nodes pane return; CUiAnimViewNodesCtrl::CRecord* pRecord = (CUiAnimViewNodesCtrl::CRecord*) itemAt(event->pos()); if (!pRecord) { return; } CUiAnimViewNode* pTargetNode = pRecord->GetNode(); QTreeWidget::dragMoveEvent(event); if (!event->isAccepted()) { return; } if (pTargetNode && pTargetNode->IsGroupNode() /*&& !m_draggedNodes.DoesContain(pTargetNode)*/) { CUiAnimViewAnimNode* pDragTarget = static_cast(pTargetNode); bool bAllValidReparenting = true; QList nodes = draggedNodes(event); Q_FOREACH(CUiAnimViewAnimNode * pDraggedNode, nodes) { if (!pDraggedNode->IsValidReparentingTo(pDragTarget)) { bAllValidReparenting = false; break; } } if (!bAllValidReparenting) { event->ignore(); } return; } } void dropEvent(QDropEvent* event) { // For now we do not support any drag and drop in the Nodes pane return; CUiAnimViewNodesCtrl::CRecord* pRecord = (CUiAnimViewNodesCtrl::CRecord*) itemAt(event->pos()); if (!pRecord) { return; } CUiAnimViewNode* pTargetNode = pRecord->GetNode(); QTreeWidget::dropEvent(event); if (!event->isAccepted()) { return; } if (pTargetNode && pTargetNode->IsGroupNode() /*&& !m_draggedNodes.DoesContain(pTargetNode)*/) { CUiAnimViewAnimNode* pDragTarget = static_cast(pTargetNode); bool bAllValidReparenting = true; QList nodes = draggedNodes(event); Q_FOREACH(CUiAnimViewAnimNode * pDraggedNode, nodes) { if (!pDraggedNode->IsValidReparentingTo(pDragTarget)) { bAllValidReparenting = false; break; } } if (bAllValidReparenting) { UiAnimUndo undo("Drag and Drop UiAnimView Nodes"); Q_FOREACH(CUiAnimViewAnimNode * pDraggedNode, nodes) { pDraggedNode->SetNewParent(pDragTarget); } } } } void keyPressEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_Tab: if (m_controller) { m_controller->ShowNextResult(); event->accept(); } return; default: break; } QTreeWidget::keyPressEvent(event); } bool focusNextPrevChild(bool next) { return false; // so we get the tab key } private: QList draggedNodes(QDropEvent* event) { QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist"); QDataStream stream(&encoded, QIODevice::ReadOnly); QList nodes; while (!stream.atEnd()) { int row, col; QMap roleDataMap; stream >> row >> col >> roleDataMap; QVariant v = roleDataMap[Qt::UserRole]; if (v.isValid()) { CUiAnimViewNode* pNode = v.value(); if (pNode && pNode->GetNodeType() == eUiAVNT_AnimNode) { nodes << (CUiAnimViewAnimNode*)pNode; } } } return nodes; } CUiAnimViewNodesCtrl* m_controller; }; QDataStream& operator<<(QDataStream& out, const CUiAnimViewNodePtr& obj) { out.writeRawData((const char*) &obj, sizeof(obj)); return out; } QDataStream& operator>>(QDataStream& in, CUiAnimViewNodePtr& obj) { in.readRawData((char*) &obj, sizeof(obj)); return in; } enum EMenuItem { eMI_SelectInViewport = 603, eMI_RemoveSelected = 10, eMI_CopyKeys = 599, eMI_CopySelectedKeys = 600, eMI_PasteKeys = 601, eMI_AddTrackBase = 1000, eMI_RemoveTrack = 299, eMI_ExpandAll = 650, eMI_CollapseAll = 659, eMI_ExpandFolders = 660, eMI_CollapseFolders = 661, eMI_ExpandEntities = 651, eMI_CollapseEntities = 652, eMI_ExpandCameras = 653, eMI_CollapseCameras = 654, eMI_ExpandMaterials = 655, eMI_CollapseMaterials = 656, eMI_ExpandEvents = 657, eMI_CollapseEvents = 658, eMI_AddDirectorNode = 501, eMI_AddConsoleVariable = 502, eMI_AddScriptVariable = 503, eMI_AddMaterial = 504, eMI_AddEvent = 505, eMI_AddCommentNode = 507, eMI_AddRadialBlur = 508, eMI_AddColorCorrection = 509, eMI_AddDOF = 510, eMI_AddScreenfader = 511, eMI_AddHDRSetup = 512, eMI_AddShadowSetup = 513, eMI_AddEnvironment = 514, eMI_AddScreenDropsSetup = 515, eMI_AddSelectedUiElements = 516, eMI_EditEvents = 550, eMI_SetAsViewCamera = 13, eMI_SetAsActiveDirector = 15, eMI_Disable = 17, eMI_Mute = 18, eMI_CustomizeTrackColor = 19, eMI_ClearCustomTrackColor = 20, eMI_ShowHideBase = 100, eMI_SelectSubmaterialBase = 2000, eMI_SetAnimationLayerBase = 3000, }; // The 'MI' represents a Menu Item. #include ////////////////////////////////////////////////////////////////////////// CUiAnimViewNodesCtrl::CUiAnimViewNodesCtrl(QWidget* hParentWnd, CUiAnimViewDialog* parent /* = 0 */) : QWidget(hParentWnd) , m_bIgnoreNotifications(false) , m_bNeedReload(false) , m_bSelectionChanging(false) , ui(new Ui::CUiAnimViewNodesCtrl) , m_pUiAnimViewDialog(parent) { ui->setupUi(this); m_pDopeSheet = 0; m_currentMatchIndex = 0; m_matchCount = 0; qRegisterMetaType("CUiAnimViewNodePtr"); qRegisterMetaTypeStreamOperators("CUiAnimViewNodePtr"); ui->treeWidget->hide(); ui->searchField->hide(); ui->searchCount->hide(); ui->searchField->installEventFilter(this); ui->treeWidget->setController(this); ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this, &CUiAnimViewNodesCtrl::OnNMRclick); connect(ui->treeWidget, &QTreeWidget::itemExpanded, this, &CUiAnimViewNodesCtrl::OnItemExpanded); connect(ui->treeWidget, &QTreeWidget::itemCollapsed, this, &CUiAnimViewNodesCtrl::OnItemExpanded); connect(ui->treeWidget, &QTreeWidget::itemSelectionChanged, this, &CUiAnimViewNodesCtrl::OnSelectionChanged); connect(ui->treeWidget, &QTreeWidget::itemDoubleClicked, this, &CUiAnimViewNodesCtrl::OnItemDblClick); connect(ui->searchField, &QLineEdit::textChanged, this, &CUiAnimViewNodesCtrl::OnFilterChange); for (int i = 0; i <= 28; i++) { QIcon icon(QString(":/nodes/tvnodes-%1.png").arg(i, 2, 10, QLatin1Char('0'))); if (!icon.isNull()) { m_imageList[i] = icon; } } m_bEditLock = false; m_arrowCursor = Qt::ArrowCursor; m_noIcon = Qt::ForbiddenCursor; UiAnimUndoManager::Get()->AddListener(this); // Create an action and shortcut to handle the delete key for the nodes ctrl. // We can't do this using keyPressEvent because the UI editor window has a shortcut // for delete and that will override any keyPressEvent in child widgets. // So we need our own shortcut to override the Editor window one when the focus // is in this widget. setFocusPolicy(Qt::StrongFocus); QAction* action = new QAction(QString("Delete"), this); action->setShortcut(QKeySequence::Delete); action->setShortcutContext(Qt::WidgetWithChildrenShortcut); QObject::connect(action, &QAction::triggered, this, [this](bool checked) { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (pSequence) { UiAnimUndo undo("Delete selected UiAnimView Nodes/Tracks"); BeginUndoTransaction(); pSequence->DeleteSelectedNodes(); EndUndoTransaction(); } }); addAction(action); }; ////////////////////////////////////////////////////////////////////////// CUiAnimViewNodesCtrl::~CUiAnimViewNodesCtrl() { UiAnimUndoManager::Get()->RemoveListener(this); } bool CUiAnimViewNodesCtrl::eventFilter(QObject* o, QEvent* e) { if (o == ui->searchField && e->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast(e); if (keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) { ShowNextResult(); return true; } } return QWidget::eventFilter(o, e); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnSequenceChanged() { assert(m_pUiAnimViewDialog); m_nodeToRecordMap.clear(); ui->treeWidget->clear(); FillAutoCompletionListForFilter(); Reload(); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::SetDopeSheet(CUiAnimViewDopeSheetBase* pDopeSheet) { m_pDopeSheet = pDopeSheet; } ////////////////////////////////////////////////////////////////////////// int CUiAnimViewNodesCtrl::GetIconIndexForTrack(const CUiAnimViewTrack* pTrack) const { int nImage = 13; // Default return nImage; } ////////////////////////////////////////////////////////////////////////// int CUiAnimViewNodesCtrl::GetIconIndexForNode(EUiAnimNodeType type) const { int nImage = 0; if (type == eUiAnimNodeType_Entity) { // TODO: Get specific icons for lights, particles etc. nImage = 21; } else if (type == eUiAnimNodeType_Director) { nImage = 27; } else if (type == eUiAnimNodeType_Camera) { nImage = 8; } else if (type == eUiAnimNodeType_CVar) { nImage = 15; } else if (type == eUiAnimNodeType_ScriptVar) { nImage = 14; } else if (type == eUiAnimNodeType_Material) { nImage = 16; } else if (type == eUiAnimNodeType_Event) { nImage = 6; } else if (type == eUiAnimNodeType_Group) { nImage = 1; } else if (type == eUiAnimNodeType_Layer) { nImage = 20; } else if (type == eUiAnimNodeType_Comment) { nImage = 23; } else if (type == eUiAnimNodeType_Light) { nImage = 18; } else if (type == eUiAnimNodeType_HDRSetup) { nImage = 26; } else if (type == eUiAnimNodeType_ShadowSetup) { nImage = 24; } return nImage; } ////////////////////////////////////////////////////////////////////////// CUiAnimViewNodesCtrl::CRecord* CUiAnimViewNodesCtrl::AddAnimNodeRecord(CRecord* pParentRecord, CUiAnimViewAnimNode* pAnimNode) { CRecord* pNewRecord = new CRecord(pAnimNode); pNewRecord->setText(0, QtUtil::ToQString(pAnimNode->GetName())); UpdateUiAnimNodeRecord(pNewRecord, pAnimNode); pParentRecord->insertChild(GetInsertPosition(pParentRecord, pAnimNode), pNewRecord); FillNodesRec(pNewRecord, pAnimNode); return pNewRecord; } ////////////////////////////////////////////////////////////////////////// CUiAnimViewNodesCtrl::CRecord* CUiAnimViewNodesCtrl::AddTrackRecord(CRecord* pParentRecord, CUiAnimViewTrack* pTrack) { const CUiAnimViewAnimNode* pAnimNode = pTrack->GetAnimNode(); CRecord* pNewTrackRecord = new CRecord(pTrack); pNewTrackRecord->setSizeHint(0, QSize(30, 18)); pNewTrackRecord->setText(0, QtUtil::ToQString(pTrack->GetName())); UpdateTrackRecord(pNewTrackRecord, pTrack); pParentRecord->insertChild(GetInsertPosition(pParentRecord, pTrack), pNewTrackRecord); FillNodesRec(pNewTrackRecord, pTrack); return pNewTrackRecord; } ////////////////////////////////////////////////////////////////////////// int CUiAnimViewNodesCtrl::GetInsertPosition(CRecord* pParentRecord, CUiAnimViewNode* pNode) { // Search for insert position const int siblingCount = pParentRecord->childCount(); for (int i = 0; i < siblingCount; ++i) { CRecord* pRecord = static_cast(pParentRecord->child(i)); CUiAnimViewNode* pSiblingNode = pRecord->GetNode(); if (*pNode < *pSiblingNode) { return i; } } return siblingCount; } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::AddNodeRecord(CRecord* pRecord, CUiAnimViewNode* pNode) { assert(m_nodeToRecordMap.find(pNode) == m_nodeToRecordMap.end()); if (m_nodeToRecordMap.find(pNode) != m_nodeToRecordMap.end()) { // For safety. Shouldn't happen return; } CRecord* pNewRecord = nullptr; if (pNode->IsHidden()) { return; } switch (pNode->GetNodeType()) { case eUiAVNT_AnimNode: pNewRecord = AddAnimNodeRecord(pRecord, static_cast(pNode)); break; case eUiAVNT_Track: pNewRecord = AddTrackRecord(pRecord, static_cast(pNode)); break; } if (pNewRecord) { if (!pNode->IsGroupNode() && pNode->GetChildCount() == 0) // groups and compound tracks are draggable { pNewRecord->setFlags(pNewRecord->flags() & ~Qt::ItemIsDragEnabled); } if (!pNode->IsGroupNode()) // only groups can be dropped into { pNewRecord->setFlags(pNewRecord->flags() & ~Qt::ItemIsDropEnabled); } if (pNode->IsExpanded()) { pNewRecord->setExpanded(true); } if (pNode->IsSelected()) { m_bIgnoreNotifications = true; SelectRow(pNode, false, false); m_bIgnoreNotifications = false; } m_nodeToRecordMap[pNode] = pNewRecord; } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::FillNodesRec(CRecord* pRecord, CUiAnimViewNode* pCurrentNode) { const unsigned int childCount = pCurrentNode->GetChildCount(); for (unsigned int childIndex = 0; childIndex < childCount; ++childIndex) { CUiAnimViewNode* pNode = pCurrentNode->GetChild(childIndex); if (!pNode->IsHidden()) { AddNodeRecord(pRecord, pNode); } } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::UpdateNodeRecord(CRecord* pRecord) { CUiAnimViewNode* pNode = pRecord->GetNode(); if (pNode) { switch (pNode->GetNodeType()) { case eUiAVNT_AnimNode: UpdateUiAnimNodeRecord(pRecord, static_cast(pNode)); break; case eUiAVNT_Track: UpdateTrackRecord(pRecord, static_cast(pNode)); break; } } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::UpdateTrackRecord(CRecord* pRecord, CUiAnimViewTrack* pTrack) { int nImage = GetIconIndexForTrack(pTrack); assert(m_imageList.contains(nImage)); pRecord->setIcon(0, m_imageList[nImage]); // Check if parameter is valid for non sub tracks CUiAnimViewAnimNode* pAnimNode = pTrack->GetAnimNode(); const bool isParamValid = pTrack->IsSubTrack() || pAnimNode->IsParamValid(pTrack->GetParameterType()); // Check if disabled or muted const bool bDisabledOrMuted = pTrack->IsDisabled() || pTrack->IsMuted(); // If track is not valid and disabled/muted color node in grey pRecord->setData(0, CRecord::EnableRole, !bDisabledOrMuted && isParamValid); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::UpdateUiAnimNodeRecord(CRecord* pRecord, CUiAnimViewAnimNode* pAnimNode) { const QColor TEXT_COLOR_FOR_NORMAL_ENTITY_NODE(220, 220, 220); const QColor TEXT_COLOR_FOR_MISSING_ENTITY(255, 0, 0); const QColor TEXT_COLOR_FOR_INVALID_MATERIAL(255, 0, 0); const QColor BACK_COLOR_FOR_ACTIVE_DIRECTOR(192, 192, 255); const QColor BACK_COLOR_FOR_INACTIVE_DIRECTOR(224, 224, 224); QFont f = font(); f.setBold(true); pRecord->setFont(0, f); EUiAnimNodeType nodeType = pAnimNode->GetType(); int nNodeImage = GetIconIndexForNode(nodeType); assert(m_imageList.contains(nNodeImage)); QString nodeName = pAnimNode->GetName(); pRecord->setIcon(0, m_imageList[nNodeImage]); const bool bDisabled = pAnimNode->IsDisabled(); pRecord->setData(0, CRecord::EnableRole, !bDisabled); if (nodeType == eUiAnimNodeType_AzEntity) { AZ::Entity* azEntity = pAnimNode->GetNodeEntityAz(); if (azEntity) { pRecord->setForeground(0, TEXT_COLOR_FOR_NORMAL_ENTITY_NODE); } else { pRecord->setForeground(0, TEXT_COLOR_FOR_MISSING_ENTITY); } } else if (nodeType == eUiAnimNodeType_Group) { pRecord->setBackground(0, QColor(220, 255, 220)); pRecord->setSizeHint(0, QSize(30, 20)); } else if (nodeType == eUiAnimNodeType_Material) { // Check if a valid material can be found by the node name. _smart_ptr pMaterial = nullptr; QString matName; int subMtlIndex = GetMatNameAndSubMtlIndexFromName(matName, pAnimNode->GetName()); pMaterial = gEnv->p3DEngine->GetMaterialManager()->FindMaterial(matName.toUtf8().data()); if (pMaterial) { bool bMultiMat = pMaterial->GetSubMtlCount() > 0; bool bMultiMatWithoutValidIndex = bMultiMat && (subMtlIndex < 0 || subMtlIndex >= pMaterial->GetSubMtlCount()); bool bLeafMatWithIndex = !bMultiMat && subMtlIndex != -1; if (bMultiMatWithoutValidIndex || bLeafMatWithIndex) { pMaterial = nullptr; } } if (!pMaterial) { pRecord->setForeground(0, TEXT_COLOR_FOR_INVALID_MATERIAL); } } // Mark the active director and other directors properly. if (pAnimNode->IsActiveDirector()) { pRecord->setBackground(0, BACK_COLOR_FOR_ACTIVE_DIRECTOR); } else if (nodeType == eUiAnimNodeType_Director) { pRecord->setBackground(0, BACK_COLOR_FOR_INACTIVE_DIRECTOR); } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::Reload() { ui->treeWidget->clear(); OnFillItems(); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnFillItems() { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (pSequence) { CUiAnimViewSequenceNotificationContext context(pSequence); m_nodeToRecordMap.clear(); CRecord* pRootGroupRec = new CRecord(pSequence); pRootGroupRec->setText(0, QtUtil::ToQString(pSequence->GetName())); QFont f = font(); f.setBold(true); pRootGroupRec->setData(0, Qt::FontRole, f); pRootGroupRec->setSizeHint(0, QSize(width(), 24)); m_nodeToRecordMap[pSequence] = pRootGroupRec; ui->treeWidget->addTopLevelItem(pRootGroupRec); FillNodesRec(pRootGroupRec, pSequence); pRootGroupRec->setExpanded(pSequence->IsExpanded()); // Additional empty record like space for scrollbar in key control CRecord* pGroupRec = new CRecord(); pGroupRec->setSizeHint(0, QSize(width(), 18)); ui->treeWidget->addTopLevelItem(pRootGroupRec); } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnItemExpanded(QTreeWidgetItem* item) { CRecord* pRecord = (CRecord*) item; if (pRecord && pRecord->GetNode()) { pRecord->GetNode()->SetExpanded(item->isExpanded()); } UpdateDopeSheet(); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnSelectionChanged() { // Need to avoid the second call to this, because GetSelectedRows is broken // with multi selection if (m_bSelectionChanging) { return; } m_bSelectionChanging = true; CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (pSequence) { CUiAnimViewSequenceNotificationContext context(pSequence); pSequence->ClearSelection(); QList items = ui->treeWidget->selectedItems(); int nCount = items.count(); for (int i = 0; i < nCount; i++) { CRecord* pRecord = (CRecord*)items.at(i); if (pRecord && pRecord->GetNode()) { if (!pRecord->GetNode()->IsSelected()) { pRecord->GetNode()->SetSelected(true); ui->treeWidget->setCurrentItem(pRecord); } } } } m_bSelectionChanging = false; UpdateDopeSheet(); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnNMRclick(QPoint point) { CRecord* pRecord = 0; CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (!pSequence) { return; } CUiAnimViewSequenceNotificationContext context(pSequence); // Find node under mouse. // Select the item that is at the point myPoint. pRecord = static_cast(ui->treeWidget->itemAt(point)); CUiAnimViewAnimNode* pGroupNode = nullptr; CUiAnimViewNode* pNode = nullptr; CUiAnimViewAnimNode* pAnimNode = nullptr; CUiAnimViewTrack* pTrack = nullptr; if (pRecord && pRecord->GetNode()) { pNode = pRecord->GetNode(); if (pNode) { EUiAnimViewNodeType nodeType = pNode->GetNodeType(); if (nodeType == eUiAVNT_AnimNode) { pAnimNode = static_cast(pNode); if (pAnimNode->GetType() == eUiAnimNodeType_Director || pAnimNode->GetType() == eUiAnimNodeType_Group) { pGroupNode = pAnimNode; } } else if (nodeType == eUiAVNT_Sequence) { pGroupNode = pSequence; } else if (nodeType == eUiAVNT_Track) { pTrack = static_cast(pNode); pAnimNode = pTrack->GetAnimNode(); } } } else { pNode = pSequence; pGroupNode = pSequence; pRecord = m_nodeToRecordMap[pSequence]; } int cmd = ShowPopupMenu(point, pRecord); float scrollPos = SaveVerticalScrollPos(); if (cmd == eMI_RemoveSelected) { UiAnimUndo undo("Delete selected UiAnimView Nodes/Tracks"); BeginUndoTransaction(); pSequence->DeleteSelectedNodes(); EndUndoTransaction(); } if (pGroupNode) { if (cmd == eMI_AddSelectedUiElements) { UiAnimUndo undo("Add UI Elements to Animation"); pGroupNode->AddSelectedUiElements(); //pGroupNode->BindToEditorObjects(); // this causes problems (since it causes multiple registers with components?) } else if (cmd == eMI_AddScreenfader) { UiAnimUndo undo("Add UiAnimView Screen Fader Node"); pGroupNode->CreateSubNode("ScreenFader", eUiAnimNodeType_ScreenFader); } else if (cmd == eMI_AddCommentNode) { UiAnimUndo undo("Add UiAnimView Comment Node"); pGroupNode->CreateSubNode("Comment", eUiAnimNodeType_Comment); } else if (cmd == eMI_AddRadialBlur) { UiAnimUndo undo("Add UiAnimView Radial Blur Node"); pGroupNode->CreateSubNode("RadialBlur", eUiAnimNodeType_RadialBlur); } else if (cmd == eMI_AddColorCorrection) { UiAnimUndo undo("Add UiAnimView Color Correction Node"); pGroupNode->CreateSubNode("ColorCorrection", eUiAnimNodeType_ColorCorrection); } else if (cmd == eMI_AddDOF) { UiAnimUndo undo("Add UiAnimView Depth of Field Node"); pGroupNode->CreateSubNode("DepthOfField", eUiAnimNodeType_DepthOfField); } else if (cmd == eMI_AddHDRSetup) { UiAnimUndo undo("Add UiAnimView HDR Setup Node"); pGroupNode->CreateSubNode("HdrSetup", eUiAnimNodeType_HDRSetup); } else if (cmd == eMI_AddShadowSetup) { UiAnimUndo undo("Add UiAnimView Shadow Setup Node"); pGroupNode->CreateSubNode("ShadowsSetup", eUiAnimNodeType_ShadowSetup); } else if (cmd == eMI_AddScreenDropsSetup) { UiAnimUndo undo("Add UiAnimView Screen Drops Setup Node"); pGroupNode->CreateSubNode("ScreenDropsSetup", eUiAnimNodeType_ScreenDropsSetup); } else if (cmd == eMI_AddEnvironment) { UiAnimUndo undo("Add UiAnimView Environment Node"); pGroupNode->CreateSubNode("Environment", eUiAnimNodeType_Environment); } else if (cmd == eMI_AddDirectorNode) { UiAnimUndo undo("Add UiAnimView Director Node"); QString name = pGroupNode->GetAvailableNodeNameStartingWith("Director"); pGroupNode->CreateSubNode(name, eUiAnimNodeType_Director); } else if (cmd == eMI_AddEvent) { UiAnimUndo undo("Add UiAnimView Event Node"); pGroupNode->CreateSubNode("Events", eUiAnimNodeType_Event); } #if UI_ANIMATION_REMOVED // CStringDlg unresolved else if (cmd == eMI_AddConsoleVariable) { CStringDlg dlg(_T("Console Variable Name")); if (dlg.DoModal() == IDOK && !dlg.GetString().IsEmpty()) { UiAnimUndo undo("Add UiAnimView Console (CVar) Node"); CString name = pGroupNode->GetAvailableNodeNameStartingWith(dlg.GetString()); pGroupNode->CreateSubNode(name, eUiAnimNodeType_CVar); } } else if (cmd == eMI_AddScriptVariable) { CStringDlg dlg(_T("Script Variable Name")); if (dlg.DoModal() == IDOK && !dlg.GetString().IsEmpty()) { UiAnimUndo undo("Add UiAnimView Script Variable Node"); CString name = pGroupNode->GetAvailableNodeNameStartingWith(dlg.GetString()); pGroupNode->CreateSubNode(name, eUiAnimNodeType_ScriptVar); } } else if (cmd == eMI_AddMaterial) { CStringDlg dlg(_T("Material Name")); if (dlg.DoModal() == IDOK && !dlg.GetString().IsEmpty()) { if (pGroupNode->GetAnimNodesByName(dlg.GetString()).GetCount() == 0) { UiAnimUndo undo("Add UiAnimView Material Node"); pGroupNode->CreateSubNode(dlg.GetString(), eUiAnimNodeType_Material); } } } #endif } if (cmd == eMI_EditEvents) { EditEvents(); } else if (cmd == eMI_SetAsActiveDirector) { if (pNode && pNode->GetNodeType() == eUiAVNT_AnimNode) { CUiAnimViewAnimNode* pAnimNode = static_cast(pNode); pAnimNode->SetAsActiveDirector(); } } else if (cmd == eMI_RemoveTrack) { if (pTrack) { UiAnimUndo undo("Remove Animation Track"); pTrack->GetAnimNode()->RemoveTrack(pTrack); } } else if (cmd >= eMI_ShowHideBase && cmd < eMI_ShowHideBase + 100) { if (pAnimNode) { unsigned int childIndex = cmd - eMI_ShowHideBase; if (childIndex < pAnimNode->GetChildCount()) { CUiAnimViewNode* pChild = pAnimNode->GetChild(childIndex); pChild->SetHidden(!pChild->IsHidden()); } } } else if (cmd == eMI_CopyKeys) { pSequence->CopyKeysToClipboard(false, true); } else if (cmd == eMI_CopySelectedKeys) { pSequence->CopyKeysToClipboard(true, true); } else if (cmd == eMI_PasteKeys) { UiAnimUndo undo("Paste Animation Keys"); pSequence->PasteKeysFromClipboard(pAnimNode, pTrack); } else if (cmd == eMI_ExpandAll) { if (pGroupNode) { BeginUndoTransaction(); pGroupNode->GetAllAnimNodes().ExpandAll(); EndUndoTransaction(); } } else if (cmd == eMI_CollapseAll) { if (pGroupNode) { BeginUndoTransaction(); pGroupNode->GetAllAnimNodes().CollapseAll(); EndUndoTransaction(); } } else if (cmd == eMI_ExpandFolders) { if (pGroupNode) { BeginUndoTransaction(); pGroupNode->GetAnimNodesByType(eUiAnimNodeType_Group).ExpandAll(); pGroupNode->GetAnimNodesByType(eUiAnimNodeType_Director).ExpandAll(); EndUndoTransaction(); } } else if (cmd == eMI_CollapseFolders) { if (pGroupNode) { BeginUndoTransaction(); pGroupNode->GetAnimNodesByType(eUiAnimNodeType_Group).CollapseAll(); pGroupNode->GetAnimNodesByType(eUiAnimNodeType_Director).CollapseAll(); EndUndoTransaction(); } } else if (cmd == eMI_ExpandEntities) { if (pGroupNode) { BeginUndoTransaction(); pGroupNode->GetAnimNodesByType(eUiAnimNodeType_Entity).ExpandAll(); EndUndoTransaction(); } } else if (cmd == eMI_CollapseEntities) { if (pGroupNode) { BeginUndoTransaction(); pGroupNode->GetAnimNodesByType(eUiAnimNodeType_Entity).CollapseAll(); EndUndoTransaction(); } } else if (cmd == eMI_SelectInViewport) { UiAnimUndo undo("Select Animation Nodes in Viewport"); pSequence->SelectSelectedNodesInViewport(); } else if (cmd >= eMI_SelectSubmaterialBase && cmd < eMI_SelectSubmaterialBase + 100) { if (pAnimNode) { QString matName; int subMtlIndex = GetMatNameAndSubMtlIndexFromName(matName, pAnimNode->GetName()); QString newMatName; newMatName = QStringLiteral("%1.[%2]").arg(matName).arg(cmd - eMI_SelectSubmaterialBase + 1); UiAnimUndo undo("Rename Animation node"); pAnimNode->SetName(newMatName.toUtf8().data()); pAnimNode->SetSelected(true); UpdateNodeRecord(pRecord); } } else if (cmd >= eMI_SetAnimationLayerBase && cmd < eMI_SetAnimationLayerBase + 100) { if (pNode && pNode->GetNodeType() == eUiAVNT_Track) { CUiAnimViewTrack* pTrack = static_cast(pNode); pTrack->SetAnimationLayerIndex(cmd - eMI_SetAnimationLayerBase); } } else if (cmd == eMI_Disable) { if (pNode) { pNode->SetDisabled(!pNode->IsDisabled()); } } else if (cmd == eMI_Mute) { if (pTrack) { pTrack->SetMuted(!pTrack->IsMuted()); } } else if (cmd == eMI_CustomizeTrackColor) { CustomizeTrackColor(pTrack); } else if (cmd == eMI_ClearCustomTrackColor) { if (pTrack) { pTrack->ClearCustomColor(); } } if (cmd) { RestoreVerticalScrollPos(scrollPos); } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnItemDblClick(QTreeWidgetItem* item, int) { CRecord* pRecord = (CRecord*)item; if (pRecord && pRecord->GetNode()) { CUiAnimViewNode* pNode = pRecord->GetNode(); if (pNode->GetNodeType() == eUiAVNT_AnimNode) { #if UI_ANIMATION_REMOVED // uses Cry Entity CUiAnimViewAnimNode* pAnimNode = static_cast(pNode); CEntityObject* pEntity = pAnimNode->GetNodeEntity(); if (pEntity) { UiAnimUndo undo("Select Object"); GetIEditor()->ClearSelection(); GetIEditor()->SelectObject(pEntity); } #endif } } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::EditEvents() { CUiAVEventsDialog dlg; dlg.exec(); } ////////////////////////////////////////////////////////////////////////// struct UiAnimTrackMenuTreeNode { QMenu menu; CUiAnimParamType paramType; std::map > children; }; ////////////////////////////////////////////////////////////////////////// struct UiAnimContextMenu { QMenu main; QMenu expandSub; QMenu collapseSub; QMenu setLayerSub; UiAnimTrackMenuTreeNode addTrackSub; }; ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::AddGroupNodeAddItems(UiAnimContextMenu& contextMenu, CUiAnimViewAnimNode* pAnimNode) { // only want this item on sequence node if (pAnimNode->GetNodeType() == eUiAVNT_Sequence) { contextMenu.main.addAction("Add Selected UI Element(s)")->setData(eMI_AddSelectedUiElements); contextMenu.main.addAction("Add Event Node")->setData(eMI_AddEvent); } const bool bIsDirectorOrSequence = (pAnimNode->GetType() == eUiAnimNodeType_Director || pAnimNode->GetNodeType() == eUiAVNT_Sequence); CUiAnimViewAnimNode* pDirector = bIsDirectorOrSequence ? pAnimNode : pAnimNode->GetDirector(); #if UI_ANIMATION_REMOVED contextMenu.main.addAction("Add Comment Node")->setData(eMI_AddCommentNode); contextMenu.main.addAction("Add Console Variable")->setData(eMI_AddConsoleVariable); contextMenu.main.addAction("Add Script Variable")->setData(eMI_AddScriptVariable); contextMenu.main.addAction("Add Material")->setData(eMI_AddMaterial); #endif } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::AddMenuSeperatorConditional(QMenu& menu, bool& bAppended) { if (bAppended) { menu.addSeparator(); } bAppended = false; } ////////////////////////////////////////////////////////////////////////// int CUiAnimViewNodesCtrl::ShowPopupMenuSingleSelection(UiAnimContextMenu& contextMenu, CUiAnimViewSequence* pSequence, CUiAnimViewNode* pNode) { bool bAppended = false; const bool bOnSequence = pNode->GetNodeType() == eUiAVNT_Sequence; const bool bOnNode = pNode->GetNodeType() == eUiAVNT_AnimNode; const bool bOnTrack = pNode->GetNodeType() == eUiAVNT_Track; const bool bIsLightAnimationSet = pSequence->GetFlags() & IUiAnimSequence::eSeqFlags_LightAnimationSet; // Get track & anim node pointers CUiAnimViewTrack* pTrack = bOnTrack ? static_cast(pNode) : nullptr; const bool bOnTrackNotSub = bOnTrack && !pTrack->IsSubTrack(); CUiAnimViewAnimNode* pAnimNode = nullptr; if (bOnSequence || bOnNode) { pAnimNode = static_cast(pNode); } else if (bOnTrack) { pAnimNode = pTrack->GetAnimNode(); } #if UI_ANIMATION_REMOVED // Entity if (bOnNode && pAnimNode->GetEntity() != nullptr && !bIsLightAnimationSet) { AddMenuSeperatorConditional(contextMenu.main, bAppended); contextMenu.main.addAction("Select In Viewport")->setData(eMI_SelectInViewport); if (pAnimNode->GetType() == eUiAnimNodeType_Camera) { contextMenu.main.addAction("Set As View Camera")->setData(eMI_SetAsViewCamera); } bAppended = true; } #endif { if (bOnNode || bOnSequence || bOnTrackNotSub) { contextMenu.main.addAction("Delete")->setData(bOnTrackNotSub ? eMI_RemoveTrack : eMI_RemoveSelected); bAppended = true; } } if (bOnTrack) { // Copy & paste keys AddMenuSeperatorConditional(contextMenu.main, bAppended); contextMenu.main.addAction("Copy Keys")->setData(eMI_CopyKeys); contextMenu.main.addAction("Copy Selected Keys")->setData(eMI_CopySelectedKeys); contextMenu.main.addAction("Paste Keys")->setData(eMI_PasteKeys); bAppended = true; } // Flags { bool bFlagAppended = false; if (!bOnSequence) { AddMenuSeperatorConditional(contextMenu.main, bAppended); QAction* a = contextMenu.main.addAction("Disabled"); a->setData(eMI_Disable); a->setCheckable(true); a->setChecked(pNode->IsDisabled()); bFlagAppended = true; } bAppended = bAppended || bFlagAppended; } // Add/Remove { if (bOnSequence || pNode->IsGroupNode()) { AddMenuSeperatorConditional(contextMenu.main, bAppended); AddGroupNodeAddItems(contextMenu, pAnimNode); bAppended = true; } } // Events if (bOnSequence || pNode->IsGroupNode() && !bIsLightAnimationSet) { AddMenuSeperatorConditional(contextMenu.main, bAppended); contextMenu.main.addAction("Edit Events...")->setData(eMI_EditEvents); bAppended = true; } // Sub material menu if (bOnNode && pAnimNode->GetType() == eUiAnimNodeType_Material) { QString matName; int subMtlIndex = GetMatNameAndSubMtlIndexFromName(matName, pAnimNode->GetName()); _smart_ptr pMtl = gEnv->p3DEngine->GetMaterialManager()->FindMaterial(matName.toUtf8().data()); bool bMultMatNode = pMtl ? pMtl->GetSubMtlCount() > 0 : false; bool bMatAppended = false; if (bMultMatNode) { for (int k = 0; k < pMtl->GetSubMtlCount(); ++k) { _smart_ptr pSubMaterial = pMtl->GetSubMtl(k); if (pSubMaterial) { QString subMaterialName = pSubMaterial->GetName(); if (!subMaterialName.isEmpty()) { AddMenuSeperatorConditional(contextMenu.main, bAppended); QString subMatName = QString("[%1] %2").arg(k + 1).arg(subMaterialName); QAction* a = contextMenu.main.addAction(subMatName); a->setData(eMI_SelectSubmaterialBase + k); a->setCheckable(true); a->setChecked(k == subMtlIndex); bMatAppended = true; } } } } bAppended = bAppended || bMatAppended; } #if UI_ANIMATION_REMOVED // We have removed support for saving out the custom colors per track // it may be added back at some point // Track color if (bOnTrack) { AddMenuSeperatorConditional(contextMenu.main, bAppended); contextMenu.main.addAction("Customize Track Color...")->setData(eMI_CustomizeTrackColor); if (pTrack->HasCustomColor()) { contextMenu.main.addAction("Clear Custom Track Color")->setData(eMI_ClearCustomTrackColor); } bAppended = true; } #endif // Track hide/unhide flags if (bOnNode && !pNode->IsGroupNode()) { AddMenuSeperatorConditional(contextMenu.main, bAppended); QString string = QString("%1 Tracks").arg(pAnimNode->GetName()); contextMenu.main.addAction(string)->setEnabled(false); bool bAppendedTrackFlag = false; const unsigned int numChildren = pAnimNode->GetChildCount(); for (unsigned int childIndex = 0; childIndex < numChildren; ++childIndex) { CUiAnimViewNode* pChild = pAnimNode->GetChild(childIndex); if (pChild->GetNodeType() == eUiAVNT_Track) { CUiAnimViewTrack* pTrack = static_cast(pChild); if (pTrack->IsSubTrack()) { continue; } QAction* a = contextMenu.main.addAction(QString(" %1").arg(QtUtil::ToQString(pTrack->GetName()))); a->setData(eMI_ShowHideBase + childIndex); a->setCheckable(true); a->setChecked(!pTrack->IsHidden()); bAppendedTrackFlag = true; } } bAppended = bAppendedTrackFlag || bAppended; } return 0; } ////////////////////////////////////////////////////////////////////////// int CUiAnimViewNodesCtrl::ShowPopupMenuMultiSelection(UiAnimContextMenu& contextMenu) { QList records = ui->treeWidget->selectedItems(); bool bNodeSelected = false; for (int currentNode = 0; currentNode < records.size(); ++currentNode) { CRecord* pItemInfo = (CRecord*)records[currentNode]; EUiAnimViewNodeType type = pItemInfo->GetNode()->GetNodeType(); if (pItemInfo->GetNode()->GetNodeType() == eUiAVNT_AnimNode) { bNodeSelected = true; } } contextMenu.main.addAction("Remove Selected Nodes/Tracks")->setData(eMI_RemoveSelected); if (bNodeSelected) { contextMenu.main.addSeparator(); contextMenu.main.addAction("Select In Viewport")->setData(eMI_SelectInViewport); } return 0; } ////////////////////////////////////////////////////////////////////////// int CUiAnimViewNodesCtrl::ShowPopupMenu(QPoint point, const CRecord* pRecord) { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (!pSequence) { return 0; } UiAnimContextMenu contextMenu; CUiAnimViewNode* pNode = pRecord ? pRecord->GetNode() : nullptr; if (!pNode) { return 0; } if (ui->treeWidget->selectedItems().size() > 1) { ShowPopupMenuMultiSelection(contextMenu); } else if (pNode) { ShowPopupMenuSingleSelection(contextMenu, pSequence, pNode); } if (m_bEditLock) { SetPopupMenuLock(&contextMenu.main); } QAction* action = contextMenu.main.exec(QCursor::pos()); int ret = action ? action->data().toInt() : 0; return ret; } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::SetPopupMenuLock(QMenu* menu) { if (!m_bEditLock || !menu) { return; } UINT count = menu->actions().size(); for (UINT i = 0; i < count; ++i) { QAction* a = menu->actions().at(i); QString menuString = a->text(); if (menuString != "Expand" && menuString != "Collapse") { a->setEnabled(false); } } } ////////////////////////////////////////////////////////////////////////// float CUiAnimViewNodesCtrl::SaveVerticalScrollPos() const { const QScrollBar* scrollBar = ui->treeWidget->verticalScrollBar(); const int sbMin = scrollBar->minimum(); const int sbMax = scrollBar->maximum(); return float(scrollBar->value()) / std::max(float(sbMax - sbMin), 1.0f); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::RestoreVerticalScrollPos(float fScrollPos) { QScrollBar* scrollBar = ui->treeWidget->verticalScrollBar(); const int sbMin = scrollBar->minimum(); const int sbMax = scrollBar->maximum(); const int newScrollPos = FloatToIntRet(fScrollPos * (sbMax - sbMin) + sbMin); scrollBar->setValue(newScrollPos); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::FillAutoCompletionListForFilter() { QStringList strings; CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (pSequence) { ui->noitems->hide(); ui->treeWidget->show(); ui->searchField->show(); ui->searchCount->show(); CUiAnimViewAnimNodeBundle animNodes = pSequence->GetAllAnimNodes(); const unsigned int animNodeCount = animNodes.GetCount(); for (unsigned int i = 0; i < animNodeCount; ++i) { strings << QtUtil::ToQString(animNodes.GetNode(i)->GetName()); } } else { ui->noitems->show(); ui->treeWidget->hide(); ui->searchField->hide(); ui->searchCount->hide(); } QCompleter* c = new QCompleter(strings, this); c->setCaseSensitivity(Qt::CaseInsensitive); c->setCompletionMode(QCompleter::InlineCompletion); ui->searchField->setCompleter(c); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnFilterChange(const QString& text) { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (pSequence) { m_currentMatchIndex = 0; // Reset the match index m_matchCount = 0; // and the count. if (!text.isEmpty()) { QList items = ui->treeWidget->findItems(text, Qt::MatchContains | Qt::MatchRecursive); CUiAnimViewAnimNodeBundle animNodes = pSequence->GetAllAnimNodes(); m_matchCount = items.size(); // and the count. if (!items.empty()) { ui->treeWidget->selectionModel()->clear(); items.front()->setSelected(true); } } QString matchCountText = QString("%1/%2").arg(m_matchCount == 0 ? 0 : 1).arg(m_matchCount); // One-based indexing ui->searchCount->setText(matchCountText); } } ////////////////////////////////////////////////////////////////////////// int CUiAnimViewNodesCtrl::GetMatNameAndSubMtlIndexFromName(QString& matName, const char* nodeName) { if (const char* pCh = strstr(nodeName, ".[")) { char matPath[MAX_PATH]; cry_strcpy(matPath, nodeName, (size_t)(pCh - nodeName)); matName = matPath; pCh += 2; if ((*pCh) != 0) { const int index = atoi(pCh) - 1; return index; } } else { matName = nodeName; } return -1; } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::ShowNextResult() { if (m_matchCount > 1) { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (pSequence && !ui->searchField->text().isEmpty()) { QList items = ui->treeWidget->findItems(ui->searchField->text(), Qt::MatchContains | Qt::MatchRecursive); CUiAnimViewAnimNodeBundle animNodes = pSequence->GetAllAnimNodes(); m_matchCount = items.size(); // and the count. if (!items.empty()) { m_currentMatchIndex = ++m_currentMatchIndex % m_matchCount; ui->treeWidget->selectionModel()->clear(); items[m_currentMatchIndex]->setSelected(true); } QString matchCountText = QString("%1/%2").arg(m_currentMatchIndex + 1).arg(m_matchCount); // One-based indexing ui->searchCount->setText(matchCountText); } } } void CUiAnimViewNodesCtrl::keyPressEvent(QKeyEvent* event) { } void CUiAnimViewNodesCtrl::CreateSetAnimationLayerPopupMenu(QMenu& menuSetLayer, CUiAnimViewTrack* pTrack) const { // UI_ANIMATION_REVISIT : not used } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::CustomizeTrackColor(CUiAnimViewTrack* pTrack) { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (!pSequence) { return; } AZ::Color defaultColor(0.0f, 0.0f, 0.0f, 1.0f); if (pTrack->HasCustomColor()) { ColorB customColor = pTrack->GetCustomColor(); defaultColor = AZ::Color(customColor.r, customColor.g, customColor.b, 255); } const AZ::Color color = AzQtComponents::ColorPicker::getColor(AzQtComponents::ColorPicker::Configuration::RGB, defaultColor, QObject::tr("Select Color")); if (color != defaultColor) { UiAnimUndo undo("Customize Track Color"); UiAnimUndo::Record(new CUndoTrackObject(pTrack, pSequence)); pTrack->SetCustomColor(ColorB(color.GetR8(), color.GetG8(), color.GetB8())); UpdateDopeSheet(); } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::ClearCustomTrackColor(CUiAnimViewTrack* pTrack) { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (!pSequence) { return; } UiAnimUndo undo("Clear Custom Track Color"); UiAnimUndo::Record(new CUndoTrackObject(pTrack, pSequence)); pTrack->ClearCustomColor(); UpdateDopeSheet(); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::paintEvent(QPaintEvent* event) { QWidget::paintEvent(event); UpdateDopeSheet(); } ////////////////////////////////////////////////////////////////////////// CUiAnimViewNodesCtrl::CRecord* CUiAnimViewNodesCtrl::GetNodeRecord(const CUiAnimViewNode* pNode) const { auto findIter = m_nodeToRecordMap.find(pNode); if (findIter == m_nodeToRecordMap.end()) { return nullptr; } CRecord* pRecord = findIter->second; assert (pRecord->GetNode() == pNode); return findIter->second; } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::UpdateDopeSheet() { UpdateRecordVisibility(); if (m_pDopeSheet) { m_pDopeSheet->update(); } } ////////////////////////////////////////////////////////////////////////// // Workaround: CXTPReportRecord::IsVisible is // unreliable after the last visible element ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::UpdateRecordVisibility() { // Mark all records invisible for (auto iter = m_nodeToRecordMap.begin(); iter != m_nodeToRecordMap.end(); ++iter) { iter->second->m_bVisible = ui->treeWidget->visualItemRect(iter->second).isValid(); } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::UpdateAllNodesForElementChanges() { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (pSequence) { CUiAnimViewAnimNodeBundle animNodes = pSequence->GetAllAnimNodes(); const unsigned int animNodeCount = animNodes.GetCount(); for (unsigned int i = 0; i < animNodeCount; ++i) { CRecord* pNodeRecord = GetNodeRecord(animNodes.GetNode(i)); UpdateNodeRecord(pNodeRecord); } } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnNodeChanged(CUiAnimViewNode* pNode, IUiAnimViewSequenceListener::ENodeChangeType type) { CUiAnimViewSequence* pSequence = nullptr; EBUS_EVENT_RESULT(pSequence, UiEditorAnimationBus, GetCurrentSequence); if (!pSequence) { return; } if (!m_bIgnoreNotifications) { CUiAnimViewNode* pParentNode = pNode->GetParentNode(); CRecord* pNodeRecord = GetNodeRecord(pNode); CRecord* pParentNodeRecord = pParentNode ? GetNodeRecord(pParentNode) : nullptr; float storedScrollPosition = SaveVerticalScrollPos(); switch (type) { case IUiAnimViewSequenceListener::eNodeChangeType_Added: case IUiAnimViewSequenceListener::eNodeChangeType_Unhidden: if (pParentNodeRecord) { AddNodeRecord(pParentNodeRecord, pNode); } break; case IUiAnimViewSequenceListener::eNodeChangeType_Removed: case IUiAnimViewSequenceListener::eNodeChangeType_Hidden: if (pNodeRecord) { EraseNodeRecordRec(pNode); delete pNodeRecord; } break; case IUiAnimViewSequenceListener::eNodeChangeType_Expanded: if (pNodeRecord) { pNodeRecord->setExpanded(true); } break; case IUiAnimViewSequenceListener::eNodeChangeType_Collapsed: if (pNodeRecord) { pNodeRecord->setExpanded(false); } break; case IUiAnimViewSequenceListener::eNodeChangeType_Disabled: case IUiAnimViewSequenceListener::eNodeChangeType_Enabled: case IUiAnimViewSequenceListener::eNodeChangeType_Muted: case IUiAnimViewSequenceListener::eNodeChangeType_Unmuted: case IUiAnimViewSequenceListener::eNodeChangeType_NodeOwnerChanged: if (pNodeRecord) { UpdateNodeRecord(pNodeRecord); } } switch (type) { case IUiAnimViewSequenceListener::eNodeChangeType_Added: case IUiAnimViewSequenceListener::eNodeChangeType_Unhidden: case IUiAnimViewSequenceListener::eNodeChangeType_Removed: case IUiAnimViewSequenceListener::eNodeChangeType_Hidden: case IUiAnimViewSequenceListener::eNodeChangeType_Expanded: case IUiAnimViewSequenceListener::eNodeChangeType_Collapsed: update(); RestoreVerticalScrollPos(storedScrollPosition); break; case eNodeChangeType_SetAsActiveDirector: update(); break; } } else { m_bNeedReload = true; } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnNodeRenamed(CUiAnimViewNode* pNode, const char* pOldName) { if (!m_bIgnoreNotifications) { CRecord* pNodeRecord = GetNodeRecord(pNode); pNodeRecord->setText(0, QtUtil::ToQString(pNode->GetName())); update(); } else { m_bNeedReload = true; } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::BeginUndoTransaction() { m_bNeedReload = false; m_bIgnoreNotifications = true; m_storedScrollPosition = SaveVerticalScrollPos(); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::EndUndoTransaction() { m_bIgnoreNotifications = false; if (m_bNeedReload) { Reload(); RestoreVerticalScrollPos(m_storedScrollPosition); m_bNeedReload = false; } UpdateDopeSheet(); } ////////////////////////////////////////////////////////////////////////// QIcon CUiAnimViewNodesCtrl::GetIconForTrack(const CUiAnimViewTrack* pTrack) { int r = GetIconIndexForTrack(pTrack); return m_imageList.contains(r) ? m_imageList[r] : QIcon(); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnKeysChanged(CUiAnimViewSequence* pSequence) { CUiAnimViewSequence* pCurrentSequence = nullptr; EBUS_EVENT_RESULT(pCurrentSequence, UiEditorAnimationBus, GetCurrentSequence); if (!m_bIgnoreNotifications && pSequence && pSequence == pCurrentSequence) { UpdateDopeSheet(); } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnKeySelectionChanged(CUiAnimViewSequence* pSequence) { OnKeysChanged(pSequence); } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::OnNodeSelectionChanged(CUiAnimViewSequence* pSequence) { if (m_bSelectionChanging) { return; } CUiAnimViewSequence* pCurrentSequence = nullptr; EBUS_EVENT_RESULT(pCurrentSequence, UiEditorAnimationBus, GetCurrentSequence); if (!m_bIgnoreNotifications && pSequence && pSequence == pCurrentSequence) { UpdateDopeSheet(); CUiAnimViewAnimNodeBundle animNodes = pSequence->GetAllAnimNodes(); const uint numNodes = animNodes.GetCount(); for (uint i = 0; i < numNodes; ++i) { CUiAnimViewAnimNode* pNode = animNodes.GetNode(i); if (pNode->IsSelected()) { SelectRow(pNode, false, false); } else { DeselectRow(pNode); } } } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::SelectRow(CUiAnimViewNode* pNode, const bool bEnsureVisible, const bool bDeselectOtherRows) { std::unordered_map::const_iterator it = m_nodeToRecordMap.find(pNode); if (it != m_nodeToRecordMap.end()) { if (bDeselectOtherRows) { ui->treeWidget->selectionModel()->clear(); } (*it).second->setSelected(true); if (bEnsureVisible) { ui->treeWidget->scrollToItem((*it).second); } } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::DeselectRow(CUiAnimViewNode* pNode) { std::unordered_map::const_iterator it = m_nodeToRecordMap.find(pNode); if (it != m_nodeToRecordMap.end()) { (*it).second->setSelected(false); } } ////////////////////////////////////////////////////////////////////////// void CUiAnimViewNodesCtrl::EraseNodeRecordRec(CUiAnimViewNode* pNode) { m_nodeToRecordMap.erase(pNode); const unsigned int numChildren = pNode->GetChildCount(); for (unsigned int i = 0; i < numChildren; ++i) { EraseNodeRecordRec(pNode->GetChild(i)); } } #include