You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/LyShine/Code/Editor/Animation/UiAnimViewNodes.cpp

1822 lines
56 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#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 <LyShine/Animation/IUiAnimation.h>
#include "Objects/EntityObject.h"
#include "ViewManager.h"
#include "Export/ExportManager.h"
#include <Editor/Util/fastlib.h>
#include "QtUtilWin.h"
#include "QtUtil.h"
#include <QHeaderView>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QMenu>
#include <QDragMoveEvent>
#include <QMimeData>
#include <QDebug>
#include <QTreeWidget>
#include <QCompleter>
#include <QStyledItemDelegate>
#include <QStyleOptionViewItem>
#include <QScrollBar>
#include <AzQtComponents/Components/Widgets/ColorPicker.h>
#include <AzQtComponents/Utilities/Conversions.h>
CUiAnimViewNodesCtrl::CRecord::CRecord(CUiAnimViewNode* pNode /*= nullptr*/)
: m_pNode(pNode)
, m_bVisible(false)
{
if (pNode)
{
QVariant v;
v.setValue<CUiAnimViewNodePtr>(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([[maybe_unused]] QDragMoveEvent* event)
{
// For now we do not support any drag and drop in the Nodes pane
return;
}
void dropEvent([[maybe_unused]] QDropEvent* event)
{
// For now we do not support any drag and drop in the Nodes pane
return;
}
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([[maybe_unused]] bool next)
{
return false; // so we get the tab key
}
private:
QList<CUiAnimViewAnimNode*> draggedNodes(QDropEvent* event)
{
QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded, QIODevice::ReadOnly);
QList<CUiAnimViewAnimNode*> nodes;
while (!stream.atEnd())
{
int row, col;
QMap<int, QVariant> roleDataMap;
stream >> row >> col >> roleDataMap;
QVariant v = roleDataMap[Qt::UserRole];
if (v.isValid())
{
CUiAnimViewNode* pNode = v.value<CUiAnimViewNodePtr>();
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 <Editor/Animation/ui_UiAnimViewNodes.h>
//////////////////////////////////////////////////////////////////////////
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>("CUiAnimViewNodePtr");
qRegisterMetaTypeStreamOperators<CUiAnimViewNodePtr>("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]([[maybe_unused]] 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<QKeyEvent*>(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([[maybe_unused]] 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, QString::fromUtf8(pAnimNode->GetName().c_str()));
UpdateUiAnimNodeRecord(pNewRecord, pAnimNode);
pParentRecord->insertChild(GetInsertPosition(pParentRecord, pAnimNode), pNewRecord);
FillNodesRec(pNewRecord, pAnimNode);
return pNewRecord;
}
//////////////////////////////////////////////////////////////////////////
CUiAnimViewNodesCtrl::CRecord* CUiAnimViewNodesCtrl::AddTrackRecord(CRecord* pParentRecord, CUiAnimViewTrack* pTrack)
{
CRecord* pNewTrackRecord = new CRecord(pTrack);
pNewTrackRecord->setSizeHint(0, QSize(30, 18));
pNewTrackRecord->setText(0, QString::fromUtf8(pTrack->GetName().c_str()));
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<CRecord*>(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<CUiAnimViewAnimNode*>(pNode));
break;
case eUiAVNT_Track:
pNewRecord = AddTrackRecord(pRecord, static_cast<CUiAnimViewTrack*>(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<CUiAnimViewAnimNode*>(pNode));
break;
case eUiAVNT_Track:
UpdateTrackRecord(pRecord, static_cast<CUiAnimViewTrack*>(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 = QString::fromUtf8(pAnimNode->GetName().c_str());
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)
{
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, QString::fromUtf8(pSequence->GetName().c_str()));
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(pGroupRec);
}
}
//////////////////////////////////////////////////////////////////////////
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<QTreeWidgetItem*> 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<CRecord*>(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<CUiAnimViewAnimNode*>(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<CUiAnimViewTrack*>(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* pAnimNode2 = static_cast<CUiAnimViewAnimNode*>(pNode);
pAnimNode2->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;
GetMatNameAndSubMtlIndexFromName(matName, pAnimNode->GetName().c_str());
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* pTrack2 = static_cast<CUiAnimViewTrack*>(pNode);
pTrack2->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<CUiAnimViewAnimNode*>(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<QString, std::unique_ptr<UiAnimTrackMenuTreeNode> > 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);
#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<CUiAnimViewTrack*>(pNode) : nullptr;
const bool bOnTrackNotSub = bOnTrack && !pTrack->IsSubTrack();
CUiAnimViewAnimNode* pAnimNode = nullptr;
if (bOnSequence || bOnNode)
{
pAnimNode = static_cast<CUiAnimViewAnimNode*>(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;
}
#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(QString::fromUtf8(pAnimNode->GetName().c_str()));
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* pTrack2 = static_cast<CUiAnimViewTrack*>(pChild);
if (pTrack2->IsSubTrack())
{
continue;
}
QAction* a = contextMenu.main.addAction(QString(" %1").arg(QString::fromUtf8(pTrack2->GetName().c_str())));
a->setData(eMI_ShowHideBase + childIndex);
a->setCheckable(true);
a->setChecked(!pTrack2->IsHidden());
bAppendedTrackFlag = true;
}
}
bAppended = bAppendedTrackFlag || bAppended;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
int CUiAnimViewNodesCtrl::ShowPopupMenuMultiSelection(UiAnimContextMenu& contextMenu)
{
QList<QTreeWidgetItem*> records = ui->treeWidget->selectedItems();
bool bNodeSelected = false;
for (int currentNode = 0; currentNode < records.size(); ++currentNode)
{
CRecord* pItemInfo = (CRecord*)records[currentNode];
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([[maybe_unused]] 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 << QString::fromUtf8(animNodes.GetNode(i)->GetName().c_str());
}
}
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<QTreeWidgetItem*> 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];
azstrncpy(matPath, AZ_ARRAY_SIZE(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<QTreeWidgetItem*> 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_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([[maybe_unused]] QKeyEvent* event)
{
}
void CUiAnimViewNodesCtrl::CreateSetAnimationLayerPopupMenu([[maybe_unused]] QMenu& menuSetLayer, [[maybe_unused]] 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;
}
assert (findIter->second->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, [[maybe_unused]] const char* pOldName)
{
if (!m_bIgnoreNotifications)
{
CRecord* pNodeRecord = GetNodeRecord(pNode);
pNodeRecord->setText(0, QString::fromUtf8(pNode->GetName().c_str()));
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 CUiAnimViewNode*, CRecord*>::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 CUiAnimViewNode*, CRecord*>::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 <Animation/moc_UiAnimViewNodes.cpp>