/* * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(AnimGraphTransitionIdPicker, AZ::SystemAllocator, 0) AZ_CLASS_ALLOCATOR_IMPL(AnimGraphMultiTransitionIdHandler, AZ::SystemAllocator, 0) const QColor AnimGraphTransitionIdSelector::s_graphWindowBorderOverwriteColor = QColor(255, 133, 0); const float AnimGraphTransitionIdSelector::s_graphWindowBorderOverwriteWidth = 5.0f; AnimGraphTransitionIdSelector::AnimGraphTransitionIdSelector() { } AnimGraphTransitionIdSelector::~AnimGraphTransitionIdSelector() { ResetUI(); } void AnimGraphTransitionIdSelector::StartSelection(AnimGraphStateMachine* stateMachine, const AZStd::vector& transitionIds) { EMStudio::EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID); EMStudio::AnimGraphPlugin* animGraphPlugin = static_cast(plugin); if (animGraphPlugin) { animGraphPlugin->GetAttributesWindow()->Lock(); animGraphPlugin->SetActionFilter(EMStudio::AnimGraphActionFilter::CreateDisallowAll()); EMStudio::AnimGraphModel& model = animGraphPlugin->GetAnimGraphModel(); QItemSelectionModel& selectionModel = model.GetSelectionModel(); selectionModel.clear(); for (const AZ::u64 id : transitionIds) { AnimGraphStateTransition* transition = stateMachine->FindTransitionById(id); const QModelIndex transitionModelIndex = model.FindFirstModelIndex(transition); if (transitionModelIndex.isValid()) { selectionModel.select(transitionModelIndex, QItemSelectionModel::Rows | QItemSelectionModel::Select); } } EMStudio::BlendGraphWidget* graphWidget = animGraphPlugin->GetGraphWidget(); graphWidget->EnableBorderOverwrite(s_graphWindowBorderOverwriteColor, s_graphWindowBorderOverwriteWidth); graphWidget->SetTitleBarText("Select interrupting transitions"); } m_isSelecting = true; } void AnimGraphTransitionIdSelector::StopSelection(AnimGraphStateTransition* transition) { EMStudio::EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID); EMStudio::AnimGraphPlugin* animGraphPlugin = static_cast(plugin); if (animGraphPlugin) { // Reset selection to the current transition before unlocking the attributes window, so that we don't update it. EMStudio::AnimGraphModel& model = animGraphPlugin->GetAnimGraphModel(); QItemSelectionModel& selectionModel = model.GetSelectionModel(); selectionModel.clear(); const QModelIndex transitionModelIndex = model.FindFirstModelIndex(transition); if (transitionModelIndex.isValid()) { selectionModel.select(transitionModelIndex, QItemSelectionModel::Rows | QItemSelectionModel::Select); } ResetUI(); } m_isSelecting = false; } void AnimGraphTransitionIdSelector::ResetUI() { EMStudio::EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID); EMStudio::AnimGraphPlugin* animGraphPlugin = static_cast(plugin); if (animGraphPlugin) { EMStudio::AttributesWindow* attributesWindow = animGraphPlugin->GetAttributesWindow(); EMStudio::BlendGraphWidget* graphWidget = animGraphPlugin->GetGraphWidget(); attributesWindow->Unlock(); animGraphPlugin->SetActionFilter(EMStudio::AnimGraphActionFilter()); graphWidget->DisableBorderOverwrite(); graphWidget->SetTitleBarText(QString()); } } AnimGraphTransitionIdPicker::AnimGraphTransitionIdPicker(QWidget* parent) : QWidget(parent) { m_mainLayout = new QVBoxLayout(); setLayout(m_mainLayout); EMStudio::EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID); EMStudio::AnimGraphPlugin* animGraphPlugin = static_cast(plugin); if (animGraphPlugin) { connect(&animGraphPlugin->GetAnimGraphModel(), &QAbstractItemModel::rowsAboutToBeRemoved, this, &AnimGraphTransitionIdPicker::OnAboutToBeRemoved); } } void AnimGraphTransitionIdPicker::OnAboutToBeRemoved(const QModelIndex &parent, int first, int last) { EMStudio::EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID); EMStudio::AnimGraphPlugin* animGraphPlugin = static_cast(plugin); if (animGraphPlugin) { EMStudio::AttributesWindow* attributesWindow = animGraphPlugin->GetAttributesWindow(); for (int i = first; i <= last; ++i) { const QModelIndex modelIndex = parent.model()->index(i, 0, parent); if (modelIndex == attributesWindow->GetModelIndex()) { m_transitionSelector.ResetUI(); attributesWindow->Init(); } } } } AnimGraphTransitionIdPicker::~AnimGraphTransitionIdPicker() { } void AnimGraphTransitionIdPicker::SetTransition(AnimGraphStateTransition* transition) { m_transition = transition; Reinit(); } QString AnimGraphTransitionIdPicker::GetTransitionNameById(const AnimGraphConnectionId transitionId) { AnimGraphStateMachine* stateMachine = GetStateMachine(); if (!stateMachine) { AZ_Error("EMotionFX", false, "Cannot get transition name as state machine is not valid."); return QString(); } AnimGraphStateTransition* transition = stateMachine->FindTransitionById(transitionId); if (!transition) { AZ_Error("EMotionFX", false, "Cannot get transition name as transition cannot be found in state machine '%s'.", stateMachine->GetName()); return QString(); } return EMStudio::AnimGraphModel::GetTransitionName(transition); } void AnimGraphTransitionIdPicker::Reinit() { if (m_widget) { m_widget->deleteLater(); } m_widget = new QWidget(); m_mainLayout->addWidget(m_widget); QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setMargin(0); m_widget->setLayout(vLayout); m_label = new QLabel(); vLayout->addWidget(m_label); QGridLayout* transitionLayout = new QGridLayout(); transitionLayout->setMargin(0); vLayout->addLayout(transitionLayout); { m_removeButtons.clear(); int row = 0; for (const AZ::u64 id : m_transitionIds) { QLineEdit* transitionLineEdit = new QLineEdit(); transitionLineEdit->setText(GetTransitionNameById(id)); transitionLineEdit->setReadOnly(true); transitionLayout->addWidget(transitionLineEdit, row, 0); QPushButton* removeTransitionButton = new QPushButton(); EMStudio::EMStudioManager::MakeTransparentButton(removeTransitionButton, "/Images/Icons/Trash.svg", "Remove transition from list"); connect(removeTransitionButton, &QPushButton::clicked, this, [this, removeTransitionButton, id]() { m_transitionIds.erase(AZStd::remove(m_transitionIds.begin(), m_transitionIds.end(), id), m_transitionIds.end()); Reinit(); emit SelectionChanged(); }); m_removeButtons.emplace_back(removeTransitionButton); transitionLayout->addWidget(removeTransitionButton, row, 1); row++; } } m_pickButton = new QPushButton(this); connect(m_pickButton, &QPushButton::clicked, this, &AnimGraphTransitionIdPicker::OnPickClicked); vLayout->addWidget(m_pickButton); UpdateInterface(); } void AnimGraphTransitionIdPicker::UpdateInterface() { const size_t numTransitions = m_transitionIds.size(); if (numTransitions == 0) { m_label->setText("All transitions"); } else { m_label->setText(QString("%1 Transition%2").arg(QString::number(numTransitions), numTransitions != 1 ? "s" : "")); } const bool isSelecting = m_transitionSelector.IsSelecting(); if (isSelecting) { m_pickButton->setText("Leave selection mode"); } else { m_pickButton->setText("Select transitions"); } QString tooltip; for (const AZ::u64 id : m_transitionIds) { if (!tooltip.isEmpty()) { tooltip += "\n"; } tooltip += QString("%1").arg(GetTransitionNameById(id)); } m_label->setToolTip(tooltip); m_pickButton->setToolTip(tooltip); for (QPushButton* removeTransitionButton : m_removeButtons) { removeTransitionButton->setDisabled(isSelecting); } } void AnimGraphTransitionIdPicker::SetTransitionIds(const AZStd::vector& transitionIds) { m_transitionIds = transitionIds; Reinit(); } const AZStd::vector& AnimGraphTransitionIdPicker::GetTransitionIds() const { return m_transitionIds; } AnimGraphStateMachine* AnimGraphTransitionIdPicker::GetStateMachine() const { if (!m_transition) { AZ_Error("EMotionFX", false, "Expecting valid state machine."); return nullptr; } return m_transition->GetStateMachine(); } void AnimGraphTransitionIdPicker::OnPickClicked() { if (!m_transition) { return; } EMStudio::EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID); EMStudio::AnimGraphPlugin* animGraphPlugin = static_cast(plugin); if (!animGraphPlugin) { return; } if (!m_transitionSelector.IsSelecting()) { AnimGraphStateMachine* stateMachine = GetStateMachine(); if (stateMachine) { m_transitionSelector.StartSelection(stateMachine, m_transitionIds); UpdateInterface(); } } else { m_transitionIds.clear(); AZStd::unordered_map > selectedTransitionByAnimGraph = animGraphPlugin->GetAnimGraphModel().GetSelectedObjectsOfType(); const AZStd::vector& selectedTransitions = selectedTransitionByAnimGraph[m_transition->GetAnimGraph()]; const AnimGraphNode* sourceState = m_transition->GetSourceNode(); for (const EMotionFX::AnimGraphStateTransition* transition : selectedTransitions) { // Only add transitions that share the same source state or wildcards. if (transition != m_transition && (transition->GetSourceNode() == sourceState || transition->GetIsWildcardTransition() || m_transition->GetIsWildcardTransition())) { m_transitionIds.emplace_back(transition->GetId()); } } emit SelectionChanged(); m_transitionSelector.StopSelection(m_transition); UpdateInterface(); } Reinit(); } //--------------------------------------------------------------------------------------------------------------------------------------------------------- AnimGraphMultiTransitionIdHandler::AnimGraphMultiTransitionIdHandler() : QObject() , AzToolsFramework::PropertyHandler, AnimGraphTransitionIdPicker>() { } AZ::u32 AnimGraphMultiTransitionIdHandler::GetHandlerName() const { return AZ_CRC("AnimGraphStateTransitionIds", 0x7b2468f7); } QWidget* AnimGraphMultiTransitionIdHandler::CreateGUI(QWidget* parent) { AnimGraphTransitionIdPicker* picker = aznew AnimGraphTransitionIdPicker(parent); connect(picker, &AnimGraphTransitionIdPicker::SelectionChanged, this, [picker]() { EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, picker); }); return picker; } void AnimGraphMultiTransitionIdHandler::ConsumeAttribute(AnimGraphTransitionIdPicker* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) { if (attrValue) { AnimGraphStateTransition* transition = static_cast(attrValue->GetInstancePointer()); m_transition = transition; GUI->SetTransition(m_transition); } if (attrib == AZ::Edit::Attributes::ReadOnly) { bool value; if (attrValue->Read(value)) { GUI->setEnabled(!value); } } } void AnimGraphMultiTransitionIdHandler::WriteGUIValuesIntoProperty(size_t index, AnimGraphTransitionIdPicker* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) { instance = GUI->GetTransitionIds(); } bool AnimGraphMultiTransitionIdHandler::ReadValuesIntoGUI(size_t index, AnimGraphTransitionIdPicker* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) { QSignalBlocker signalBlocker(GUI); GUI->SetTransitionIds(instance); return true; } } // namespace EMotionFX