MotionSetMotionIdHandler.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Editor/PropertyWidgets/MotionSetMotionIdHandler.h>
  9. #include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MotionSetSelectionWindow.h>
  10. #include <Editor/AnimGraphEditorBus.h>
  11. #include <QHBoxLayout>
  12. #include <QMessageBox>
  13. #include <QLocale>
  14. #include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
  15. #include <AzQtComponents/Components/Widgets/ElidingLabel.h>
  16. namespace EMotionFX
  17. {
  18. float MotionSelectionIdWidgetController::s_displayedRoundingError = 0.0f;
  19. AZ_CLASS_ALLOCATOR_IMPL(MotionSetMotionIdPicker, EditorAllocator)
  20. AZ_CLASS_ALLOCATOR_IMPL(MotionIdRandomSelectionWeightsHandler, EditorAllocator)
  21. AZ_CLASS_ALLOCATOR_IMPL(MotionSetMultiMotionIdHandler, EditorAllocator)
  22. AZ_CLASS_ALLOCATOR_IMPL(MotionSelectionIdWidgetController, EditorAllocator)
  23. const float MotionSetMotionIdPicker::s_defaultWeight = 1.0f;
  24. void MotionSelectionIdWidgetController::ResetDisplayedRoundingError()
  25. {
  26. s_displayedRoundingError = 0.0f;
  27. }
  28. MotionSelectionIdWidgetController::MotionSelectionIdWidgetController(QGridLayout* layout,
  29. int graphicLayoutRowIndex,
  30. const IRandomMotionSelectionDataContainer* dataContainer,
  31. bool displayMotionSelectionWeight)
  32. : m_dataContainer(dataContainer),
  33. m_displayMotionSelectionWeight(displayMotionSelectionWeight)
  34. {
  35. int column = 0;
  36. // Motion name
  37. m_labelMotion = new QLabel();
  38. m_labelMotion->setObjectName("m_labelMotion");
  39. m_labelMotion->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
  40. layout->addWidget(m_labelMotion, graphicLayoutRowIndex, column);
  41. column++;
  42. // Motion position x
  43. QHBoxLayout* layoutX = new QHBoxLayout();
  44. layoutX->setAlignment(Qt::AlignRight);
  45. layoutX->setSpacing(2);
  46. layoutX->setMargin(2);
  47. m_randomWeightSpinbox = new AzQtComponents::DoubleSpinBox();
  48. m_randomWeightSpinbox->setSingleStep(0.1);
  49. m_randomWeightSpinbox->setDecimals(1);
  50. m_randomWeightSpinbox->setRange(0, FLT_MAX);
  51. layoutX->addWidget(m_randomWeightSpinbox);
  52. layout->addLayout(layoutX, graphicLayoutRowIndex, column);
  53. column++;
  54. m_normalizedProbabilityText = new QLineEdit();
  55. // The read only text for the normalized probabilities does not need the space for the SpinBox buttons
  56. m_normalizedProbabilityText->setMaximumWidth(aznumeric_cast<int>(m_randomWeightSpinbox->maximumWidth() * 0.5));
  57. m_normalizedProbabilityText->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
  58. m_normalizedProbabilityText->setEnabled(false);
  59. layout->addWidget(m_normalizedProbabilityText, graphicLayoutRowIndex, column);
  60. column++;
  61. const int iconSize = 20;
  62. // Remove motion
  63. m_removeButton = new QPushButton();
  64. m_removeButton->setToolTip("Remove motion");
  65. m_removeButton->setMinimumSize(iconSize, iconSize);
  66. m_removeButton->setMaximumSize(iconSize, iconSize);
  67. m_removeButton->setIcon(QIcon(":/EMotionFX/Trash.svg"));
  68. layout->addWidget(m_removeButton, graphicLayoutRowIndex, column);
  69. if (!m_displayMotionSelectionWeight)
  70. {
  71. m_randomWeightSpinbox->setVisible(false);
  72. m_normalizedProbabilityText->setVisible(false);
  73. }
  74. }
  75. void MotionSelectionIdWidgetController::Hide()
  76. {
  77. m_labelMotion->hide();
  78. if (m_displayMotionSelectionWeight)
  79. {
  80. m_randomWeightSpinbox->hide();
  81. m_normalizedProbabilityText->hide();
  82. }
  83. m_removeButton->hide();
  84. }
  85. void MotionSelectionIdWidgetController::Show()
  86. {
  87. m_labelMotion->show();
  88. if (m_displayMotionSelectionWeight)
  89. {
  90. m_randomWeightSpinbox->show();
  91. m_normalizedProbabilityText->show();
  92. }
  93. m_removeButton->show();
  94. }
  95. void MotionSelectionIdWidgetController::UpdateId(size_t id)
  96. {
  97. m_id = id;
  98. }
  99. void MotionSelectionIdWidgetController::DestroyGuis()
  100. {
  101. m_labelMotion->deleteLater();
  102. m_removeButton->deleteLater();
  103. m_randomWeightSpinbox->deleteLater();
  104. m_normalizedProbabilityText->deleteLater();
  105. }
  106. size_t MotionSelectionIdWidgetController::GetId() const
  107. {
  108. return m_id;
  109. }
  110. void MotionSelectionIdWidgetController::Update()
  111. {
  112. const double weight = m_dataContainer->GetWeight(m_id);
  113. m_randomWeightSpinbox->setValue(weight);
  114. const double actualPercentage = 100.0 * weight / m_dataContainer->GetWeightSum();
  115. const double compensatedValue = actualPercentage - s_displayedRoundingError;
  116. const double roundedValue = qRound(compensatedValue);
  117. s_displayedRoundingError = aznumeric_cast<float>(roundedValue - compensatedValue);
  118. QString str = m_normalizedProbabilityText->locale().toString(roundedValue, 'f', 1);
  119. m_normalizedProbabilityText->setText(str);
  120. m_labelMotion->setText(m_dataContainer->GetMotionId(m_id).c_str());
  121. }
  122. MotionSetMotionIdPicker::MotionSetMotionIdPicker(QWidget* parent, bool displaySelectionWeights)
  123. : QWidget(parent)
  124. , m_displaySelectionWeights(displaySelectionWeights)
  125. {
  126. QVBoxLayout* vLayout = new QVBoxLayout();
  127. vLayout->setMargin(0);
  128. setLayout(vLayout);
  129. }
  130. MotionSetMotionIdPicker::~MotionSetMotionIdPicker()
  131. {
  132. // Destroying the dynamically allocated controller of each row
  133. m_motionWidgetControllers.clear();
  134. }
  135. void MotionSetMotionIdPicker::SetMotionIds(const AZStd::vector<AZStd::string>& motionIds)
  136. {
  137. HandleSelectedMotionsUpdate(motionIds);
  138. InitializeWidgets();
  139. UpdateGui();
  140. }
  141. void MotionSetMotionIdPicker::SetMotions(const AZStd::vector<AZStd::pair<AZStd::string, float>>& motions)
  142. {
  143. // Display the weights (not the cumulative non normalized probability that is passed from the serialized data)
  144. float cumulativeWeight = 0.0f;
  145. m_motions.clear();
  146. m_motions.reserve(motions.size());
  147. for (unsigned int i = 0; i < motions.size(); ++i)
  148. {
  149. m_motions.emplace_back(motions[i].first, motions[i].second - cumulativeWeight);
  150. cumulativeWeight = motions[i].second;
  151. }
  152. m_weightsSum = cumulativeWeight;
  153. InitializeWidgets();
  154. UpdateGui();
  155. }
  156. const AZStd::vector<AZStd::pair<AZStd::string, float>>& MotionSetMotionIdPicker::GetMotions() const
  157. {
  158. return m_motions;
  159. }
  160. AZStd::vector<AZStd::string> MotionSetMotionIdPicker::GetMotionIds() const
  161. {
  162. AZStd::vector<AZStd::string> motionIds;
  163. motionIds.reserve(m_motions.size());
  164. for (const auto& motionPair : m_motions)
  165. {
  166. motionIds.emplace_back(motionPair.first);
  167. }
  168. return motionIds;
  169. }
  170. /// This method updates the motion random selection weights
  171. /// setting default weights for those motions which were not in the data
  172. /// and deletes the motions that have not been selected if they were in the data
  173. /// exsisting motions will keep their current weight as set by the user with the GUI
  174. void MotionSetMotionIdPicker::HandleSelectedMotionsUpdate(const AZStd::vector<AZStd::string>& motionIds)
  175. {
  176. AZStd::unordered_map<AZStd::string, float> tmpRandomWeightsTable;
  177. for (size_t i = 0; i < m_motions.size(); ++i)
  178. {
  179. tmpRandomWeightsTable.emplace(m_motions[i]);
  180. }
  181. m_weightsSum = 0;
  182. m_motions.clear();
  183. m_motions.reserve(motionIds.size());
  184. AZStd::unordered_map<AZStd::string, float>::iterator tmpWeightTableIterator;
  185. for (const AZStd::string& motionId : motionIds)
  186. {
  187. float weight = s_defaultWeight;
  188. tmpWeightTableIterator = tmpRandomWeightsTable.find(motionId);
  189. if (tmpWeightTableIterator != tmpRandomWeightsTable.end())
  190. {
  191. weight = tmpWeightTableIterator->second;
  192. }
  193. m_weightsSum += weight;
  194. m_motions.emplace_back(motionId, weight);
  195. }
  196. }
  197. void MotionSetMotionIdPicker::OnPickClicked()
  198. {
  199. EMotionFX::MotionSet* motionSet = nullptr;
  200. AnimGraphEditorRequestBus::BroadcastResult(motionSet, &AnimGraphEditorRequests::GetSelectedMotionSet);
  201. if (!motionSet)
  202. {
  203. QMessageBox::warning(this, "No Motion Set", "Cannot open motion selection window. No valid motion set selected.");
  204. return;
  205. }
  206. // Create and show the motion picker window
  207. m_motionPickWindow = new EMStudio::MotionSetSelectionWindow(this);
  208. m_motionPickWindow->GetHierarchyWidget()->SetSelectionMode(false);
  209. m_motionPickWindow->Update(motionSet);
  210. m_motionPickWindow->setModal(true);
  211. AZStd::vector<AZStd::string> motionIds;
  212. motionIds.reserve(m_motions.size());
  213. for (const auto& motionIdRandomWeightPair : m_motions)
  214. {
  215. motionIds.emplace_back(motionIdRandomWeightPair.first);
  216. }
  217. m_motionPickWindow->Select(motionIds, motionSet);
  218. m_motionPickWindow->setAttribute(Qt::WA_DeleteOnClose);
  219. connect(m_motionPickWindow, &QDialog::accepted, this, &MotionSetMotionIdPicker::OnPickDialogAccept);
  220. connect(m_motionPickWindow, &QDialog::rejected, this, &MotionSetMotionIdPicker::OnPickDialogReject);
  221. m_motionPickWindow->open();
  222. }
  223. void MotionSetMotionIdPicker::OnPickDialogAccept()
  224. {
  225. EMotionFX::MotionSet* motionSet = nullptr;
  226. AnimGraphEditorRequestBus::BroadcastResult(motionSet, &AnimGraphEditorRequests::GetSelectedMotionSet);
  227. if (!motionSet)
  228. {
  229. QMessageBox::warning(this, "No Motion Set", "Cannot open motion selection window. No valid motion set selected.");
  230. m_motionPickWindow->close();
  231. m_motionPickWindow = nullptr;
  232. return;
  233. }
  234. HandleSelectedMotionsUpdate(m_motionPickWindow->GetHierarchyWidget()->GetSelectedMotionIds(motionSet));
  235. InitializeWidgets();
  236. UpdateGui();
  237. emit SelectionChanged();
  238. m_motionPickWindow->close();
  239. m_motionPickWindow = nullptr;
  240. }
  241. void MotionSetMotionIdPicker::OnPickDialogReject()
  242. {
  243. m_motionPickWindow->close();
  244. m_motionPickWindow = nullptr;
  245. }
  246. void MotionSetMotionIdPicker::InitializeWidgets()
  247. {
  248. if (!m_containerWidget)
  249. {
  250. m_containerWidget = new QWidget();
  251. m_containerWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
  252. QVBoxLayout* widgetLayout = new QVBoxLayout();
  253. QHBoxLayout* topRowLayout = new QHBoxLayout();
  254. // Add helper label left of the add button.
  255. m_addMotionsLabel = new QLineEdit("");
  256. m_addMotionsLabel->setEnabled(false);
  257. topRowLayout->addWidget(m_addMotionsLabel);
  258. m_pickButton = new QPushButton(this);
  259. EMStudio::EMStudioManager::MakeTransparentButton(m_pickButton, "Images/Icons/Plus.svg", "Add motions to blend space");
  260. m_pickButton->setObjectName("EMFX.MotionSetMotionIdPicker.PickButton");
  261. connect(m_pickButton, &QPushButton::clicked, this, &MotionSetMotionIdPicker::OnPickClicked);
  262. topRowLayout->addWidget(m_pickButton);
  263. m_pickButton->setToolTip(QString("Add motions"));
  264. widgetLayout->addLayout(topRowLayout);
  265. QWidget* motionsWidget = new QWidget(m_containerWidget);
  266. motionsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
  267. QGridLayout* motionsLayout = new QGridLayout();
  268. motionsLayout->setHorizontalSpacing(0);
  269. AzQtComponents::ElidingLabel* labelColumn0 = new AzQtComponents::ElidingLabel();
  270. motionsLayout->addWidget(labelColumn0, 0, 0);
  271. AzQtComponents::ElidingLabel* labelColumn1 = new AzQtComponents::ElidingLabel("Probability weight");
  272. motionsLayout->addWidget(labelColumn1, 0, 1);
  273. AzQtComponents::ElidingLabel* labelColumn2 = new AzQtComponents::ElidingLabel("Probability (100%)");
  274. motionsLayout->addWidget(labelColumn2, 0, 2);
  275. if (!m_displaySelectionWeights)
  276. {
  277. labelColumn0->setVisible(false);
  278. labelColumn1->setVisible(false);
  279. labelColumn2->setVisible(false);
  280. }
  281. motionsWidget->setLayout(motionsLayout);
  282. widgetLayout->addWidget(motionsWidget);
  283. m_motionsLayout = motionsLayout;
  284. m_containerWidget->setLayout(widgetLayout);
  285. layout()->addWidget(m_containerWidget);
  286. }
  287. size_t layoutRowIndex = m_motionWidgetControllers.size();
  288. if (!m_displaySelectionWeights)
  289. {
  290. m_motionsLayout->setAlignment(Qt::AlignLeft);
  291. }
  292. else
  293. {
  294. // Making room for the grid header row
  295. layoutRowIndex++;
  296. }
  297. // Build more rows if needed
  298. if (m_motions.size() > m_motionWidgetControllers.size())
  299. {
  300. for (size_t widgetcontrollerCounter = m_motions.size() - m_motionWidgetControllers.size(); widgetcontrollerCounter > 0; --widgetcontrollerCounter)
  301. {
  302. m_motionWidgetControllers.emplace_back(AZStd::make_unique<MotionSelectionIdWidgetController>(m_motionsLayout, static_cast<int>(layoutRowIndex++), this, m_displaySelectionWeights));
  303. MotionSelectionIdWidgetController* motionWidget = m_motionWidgetControllers.back().get();
  304. connect(motionWidget->m_randomWeightSpinbox, qOverload<double>(&QDoubleSpinBox::valueChanged), this,
  305. [this, motionWidget](double value)
  306. {
  307. OnRandomWeightChanged(motionWidget->GetId(), value);
  308. }
  309. );
  310. connect(motionWidget->m_removeButton, &QPushButton::clicked, [this, motionWidget]()
  311. {
  312. OnRemoveMotion(motionWidget->GetId());
  313. });
  314. }
  315. }
  316. // Bind the row to the data and hide those that are not needed
  317. size_t id = 0;
  318. for(auto widgetsControllerIterator = m_motionWidgetControllers.begin(); widgetsControllerIterator != m_motionWidgetControllers.end(); ++widgetsControllerIterator)
  319. {
  320. if (id < m_motions.size())
  321. {
  322. (*widgetsControllerIterator)->UpdateId(id++);
  323. (*widgetsControllerIterator)->Show();
  324. }
  325. else
  326. {
  327. (*widgetsControllerIterator)->Hide();
  328. }
  329. }
  330. }
  331. void MotionSetMotionIdPicker::OnRandomWeightChanged(size_t id, double value)
  332. {
  333. m_weightsSum = aznumeric_cast<float>(m_weightsSum + (value - m_motions[id].second));
  334. m_motions[id].second = aznumeric_cast<float>(value);
  335. UpdateGui();
  336. emit SelectionChanged();
  337. }
  338. float MotionSetMotionIdPicker::GetWeight(size_t id) const
  339. {
  340. return m_motions[id].second;
  341. }
  342. float MotionSetMotionIdPicker::GetWeightSum() const
  343. {
  344. return m_weightsSum;
  345. }
  346. const AZStd::string& MotionSetMotionIdPicker::GetMotionId(size_t id) const
  347. {
  348. return m_motions[id].first;
  349. }
  350. void MotionSetMotionIdPicker::UpdateGui()
  351. {
  352. MotionSelectionIdWidgetController::ResetDisplayedRoundingError();
  353. auto widgetsIterator = m_motionWidgetControllers.begin();
  354. size_t validGuisCount = 0;
  355. for(; validGuisCount < m_motions.size() && widgetsIterator != m_motionWidgetControllers.end(); ++widgetsIterator, ++validGuisCount)
  356. {
  357. (*widgetsIterator)->Update();
  358. }
  359. if (m_motions.size() > 0)
  360. {
  361. m_addMotionsLabel->setText(AZStd::string::format("%zu motions selected", m_motions.size()).c_str());
  362. }
  363. else
  364. {
  365. m_addMotionsLabel->setText("Select motions");
  366. }
  367. }
  368. void MotionSetMotionIdPicker::OnRemoveMotion(size_t id)
  369. {
  370. m_weightsSum -= m_motions[id].second;
  371. m_motions.erase(m_motions.begin() + id);
  372. MotionSelectionIdWidgetController::ResetDisplayedRoundingError();
  373. InitializeWidgets();
  374. UpdateGui();
  375. emit SelectionChanged();
  376. }
  377. //---------------------------------------------------------------------------------------------------------------------------------------------------------
  378. AZ::u32 MotionIdRandomSelectionWeightsHandler::GetHandlerName() const
  379. {
  380. return AZ_CRC("MotionSetMotionIdsRandomSelectionWeights", 0xc882da3c);
  381. }
  382. QWidget* MotionIdRandomSelectionWeightsHandler::CreateGUI(QWidget* parent)
  383. {
  384. MotionSetMotionIdPicker* picker = aznew MotionSetMotionIdPicker(parent, true);
  385. connect(picker, &MotionSetMotionIdPicker::SelectionChanged, this, [picker]()
  386. {
  387. AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(
  388. &AzToolsFramework::PropertyEditorGUIMessages::Bus::Events::RequestWrite, picker);
  389. });
  390. return picker;
  391. }
  392. void MotionIdRandomSelectionWeightsHandler::ConsumeAttribute(MotionSetMotionIdPicker* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
  393. {
  394. if (attrib == AZ::Edit::Attributes::ReadOnly)
  395. {
  396. bool value;
  397. if (attrValue->Read<bool>(value))
  398. {
  399. GUI->setEnabled(!value);
  400. }
  401. }
  402. }
  403. void MotionIdRandomSelectionWeightsHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, MotionSetMotionIdPicker* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
  404. {
  405. // Please Note: the values stored in the serialized data that will be used to randomly select the motion to play
  406. // contain the cumulative non normalized probability
  407. // whilst the data in the GUIs contain the randomselection weights
  408. instance.clear();
  409. const auto& motions = GUI->GetMotions();
  410. instance.reserve(motions.size());
  411. // Store in the node's data the cumulative non normalized probability (not the weights)
  412. float cumulativeWeight = 0.0f;
  413. for (size_t i = 0; i < motions.size(); ++i)
  414. {
  415. cumulativeWeight += motions[i].second;
  416. instance.emplace_back(motions[i].first, cumulativeWeight);
  417. }
  418. }
  419. bool MotionIdRandomSelectionWeightsHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, MotionSetMotionIdPicker* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
  420. {
  421. QSignalBlocker signalBlocker(GUI);
  422. GUI->SetMotions(instance);
  423. return true;
  424. }
  425. //---------------------------------------------------------------------------------------------------------------------------------------------------------
  426. AZ::u32 MotionSetMultiMotionIdHandler::GetHandlerName() const
  427. {
  428. return AZ_CRC("MotionSetMotionIds", 0x8695c0fa);
  429. }
  430. QWidget* MotionSetMultiMotionIdHandler::CreateGUI(QWidget* parent)
  431. {
  432. MotionSetMotionIdPicker* picker = aznew MotionSetMotionIdPicker(parent, false);
  433. connect(picker, &MotionSetMotionIdPicker::SelectionChanged, this, [picker]()
  434. {
  435. AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(
  436. &AzToolsFramework::PropertyEditorGUIMessages::Bus::Events::RequestWrite, picker);
  437. });
  438. return picker;
  439. }
  440. void MotionSetMultiMotionIdHandler::ConsumeAttribute(MotionSetMotionIdPicker* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
  441. {
  442. if (attrib == AZ::Edit::Attributes::ReadOnly)
  443. {
  444. bool value;
  445. if (attrValue->Read<bool>(value))
  446. {
  447. GUI->setEnabled(!value);
  448. }
  449. }
  450. }
  451. void MotionSetMultiMotionIdHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, MotionSetMotionIdPicker* GUI, property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
  452. {
  453. instance = GUI->GetMotionIds();
  454. }
  455. bool MotionSetMultiMotionIdHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, MotionSetMotionIdPicker* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
  456. {
  457. QSignalBlocker signalBlocker(GUI);
  458. GUI->SetMotionIds(instance);
  459. return true;
  460. }
  461. } // namespace EMotionFX
  462. #include <Source/Editor/PropertyWidgets/moc_MotionSetMotionIdHandler.cpp>