ReflectedPropertyCtrl.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  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 "EditorDefs.h"
  9. #include "ReflectedPropertyCtrl.h"
  10. // Qt
  11. #include <QScopedValueRollback>
  12. #include <QHBoxLayout>
  13. #include <QVBoxLayout>
  14. #include <QLabel>
  15. #include <QLineEdit>
  16. #include <QMenu>
  17. #include <QScrollArea>
  18. // AzToolsFramework
  19. #include <AzToolsFramework/UI/PropertyEditor/ComponentEditorHeader.hxx>
  20. #include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
  21. #include <AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx>
  22. #include <AzToolsFramework/UI/SearchWidget/SearchCriteriaWidget.hxx>
  23. #include <AzToolsFramework/Editor/EditorSettingsAPIBus.h>
  24. //AzCore
  25. #include <AzCore/Component/ComponentApplicationBus.h>
  26. // Editor
  27. #include "Clipboard.h"
  28. ReflectedPropertyControl::ReflectedPropertyControl(QWidget *parent /*= nullptr*/, Qt::WindowFlags windowFlags /*= Qt::WindowFlags()*/)
  29. : QWidget(parent, windowFlags)
  30. , AzToolsFramework::IPropertyEditorNotify()
  31. , m_filterLineEdit(nullptr)
  32. , m_filterWidget(nullptr)
  33. , m_titleLabel(nullptr)
  34. , m_bEnableCallback(true)
  35. , m_updateVarFunc(nullptr)
  36. , m_updateObjectFunc(nullptr)
  37. , m_selChangeFunc(nullptr)
  38. , m_undoFunc(nullptr)
  39. , m_bStoreUndoByItems(true)
  40. , m_bForceModified(false)
  41. , m_groupProperties(false)
  42. , m_sortProperties(false)
  43. , m_bSendCallbackOnNonModified(true)
  44. , m_initialized(false)
  45. , m_isTwoColumnSection(false)
  46. {
  47. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  48. AZ_Assert(m_serializeContext, "Serialization context not available");
  49. qRegisterMetaType<IVariable*>("IVariablePtr");
  50. m_editor = new AzToolsFramework::ReflectedPropertyEditor(nullptr);
  51. m_editor->SetAutoResizeLabels(true);
  52. m_titleLabel = new QLabel;
  53. m_titleLabel->hide();
  54. m_filterWidget = new QWidget;
  55. QLabel *label = new QLabel(tr("Search"));
  56. m_filterLineEdit = new QLineEdit;
  57. QHBoxLayout *filterlayout = new QHBoxLayout(m_filterWidget);
  58. filterlayout->addWidget(label);
  59. filterlayout->addWidget(m_filterLineEdit);
  60. connect(m_filterLineEdit, &QLineEdit::textChanged, this, &ReflectedPropertyControl::RestrictToItemsContaining);
  61. QVBoxLayout *mainLayout = new QVBoxLayout(this);
  62. mainLayout->setContentsMargins(0, 0, 0, 0);
  63. mainLayout->addWidget(m_titleLabel, 0, Qt::AlignHCenter);
  64. mainLayout->addWidget(m_filterWidget);
  65. mainLayout->addWidget(m_editor, 1);
  66. SetShowFilterWidget(false);
  67. setMinimumSize(330, 0);
  68. }
  69. void ReflectedPropertyControl::Setup(bool showScrollbars /*= true*/, int labelWidth /*= 150*/)
  70. {
  71. if (!m_initialized)
  72. {
  73. m_editor->Setup(m_serializeContext, this, showScrollbars, labelWidth);
  74. m_initialized = true;
  75. }
  76. }
  77. QSize ReflectedPropertyControl::sizeHint() const
  78. {
  79. return m_editor->sizeHint();
  80. }
  81. ReflectedPropertyItem* ReflectedPropertyControl::AddVarBlock(CVarBlock *varBlock, const char *szCategory /*= nullptr*/)
  82. {
  83. AZ_Assert(m_initialized, "ReflectedPropertyControl not initialized. Setup must be called first.");
  84. if (!varBlock)
  85. return nullptr;
  86. m_pVarBlock = varBlock;
  87. if (!m_root)
  88. {
  89. m_root = new ReflectedPropertyItem(this, nullptr);
  90. m_rootContainer.reset(new CPropertyContainer(szCategory ? AZStd::string(szCategory) : AZStd::string()));
  91. m_rootContainer->SetAutoExpand(true);
  92. m_editor->AddInstance(m_rootContainer.get());
  93. }
  94. AZStd::vector<IVariable*> variables(varBlock->GetNumVariables());
  95. //Copy variables into vector
  96. int n = 0;
  97. std::generate(variables.begin(), variables.end(), [&n, varBlock]{return varBlock->GetVariable(n++); });
  98. //filter list based on search string
  99. if (!m_filterString.isEmpty())
  100. {
  101. AZStd::vector<IVariable*> newVariables;
  102. for (IVariable* var : variables)
  103. {
  104. if (QString(var->GetName()).toLower().contains(m_filterString))
  105. {
  106. newVariables.emplace_back(var);
  107. }
  108. }
  109. variables.swap(newVariables);
  110. }
  111. //sorting if needed. sort first when grouping to make grouping easier
  112. if (m_sortProperties || m_groupProperties)
  113. {
  114. std::sort(variables.begin(), variables.end(), [](IVariable *var1, IVariable *var2) {return QString::compare(var1->GetName(), var2->GetName(), Qt::CaseInsensitive) <=0; } );
  115. }
  116. CPropertyContainer *parentContainer = m_rootContainer.get();
  117. ReflectedPropertyItem *parentItem = m_root;
  118. QChar currentGroupInitial;
  119. for (auto var : variables)
  120. {
  121. if (m_groupProperties)
  122. {
  123. //check to see if this item starts with same letter as last. If not, create a new group for it.
  124. const QChar groupInitial = var->GetName().toUpper().at(0);
  125. if (groupInitial != currentGroupInitial)
  126. {
  127. currentGroupInitial = groupInitial;
  128. //make new group be the parent for this item
  129. parentItem = new ReflectedPropertyItem(this, parentItem);
  130. QString groupName(groupInitial);
  131. parentContainer = new CPropertyContainer(AZStd::string(groupName.toUtf8().data()));
  132. parentContainer->SetAutoExpand(false);
  133. m_rootContainer->AddProperty(parentContainer);
  134. }
  135. }
  136. ReflectedPropertyItemPtr childItem = new ReflectedPropertyItem(this, parentItem);
  137. childItem->SetVariable(var);
  138. CReflectedVar *reflectedVar = childItem->GetReflectedVar();
  139. parentContainer->AddProperty(reflectedVar);
  140. }
  141. m_editor->QueueInvalidation(AzToolsFramework::Refresh_EntireTree);
  142. return parentItem;
  143. }
  144. //////////////////////////////////////////////////////////////////////////
  145. static void AddVariable(CVariableBase& varArray, CVariableBase& var, const char* varName, const char* humanVarName, const char* description, IVariable::OnSetCallback* func, void* pUserData, char dataType = IVariable::DT_SIMPLE)
  146. {
  147. if (varName)
  148. {
  149. var.SetName(varName);
  150. }
  151. if (humanVarName)
  152. {
  153. var.SetHumanName(humanVarName);
  154. }
  155. if (description)
  156. {
  157. var.SetDescription(description);
  158. }
  159. var.SetDataType(dataType);
  160. var.SetUserData(QVariant::fromValue<void *>(pUserData));
  161. if (func)
  162. {
  163. var.AddOnSetCallback(func);
  164. }
  165. varArray.AddVariable(&var);
  166. }
  167. void ReflectedPropertyControl::CreateItems(XmlNodeRef node)
  168. {
  169. CVarBlockPtr out;
  170. CreateItems(node, out, nullptr);
  171. }
  172. void ReflectedPropertyControl::CreateItems(XmlNodeRef node, CVarBlockPtr& outBlockPtr, IVariable::OnSetCallback* func, bool splitCamelCaseIntoWords)
  173. {
  174. SelectItem(nullptr);
  175. outBlockPtr = new CVarBlock;
  176. for (size_t i = 0, iGroupCount(node->getChildCount()); i < iGroupCount; ++i)
  177. {
  178. XmlNodeRef groupNode = node->getChild(static_cast<int>(i));
  179. if (groupNode->haveAttr("hidden"))
  180. {
  181. bool isGroupHidden = false;
  182. groupNode->getAttr("hidden", isGroupHidden);
  183. if (isGroupHidden)
  184. {
  185. // do not create visual editors for this group
  186. continue;
  187. }
  188. }
  189. CSmartVariableArray group;
  190. group->SetName(groupNode->getTag());
  191. group->SetHumanName(groupNode->getTag());
  192. group->SetDescription("");
  193. group->SetDataType(IVariable::DT_SIMPLE);
  194. outBlockPtr->AddVariable(&*group);
  195. for (int k = 0, iChildCount(groupNode->getChildCount()); k < iChildCount; ++k)
  196. {
  197. XmlNodeRef child = groupNode->getChild(k);
  198. const char* type;
  199. if (!child->getAttr("type", &type))
  200. {
  201. continue;
  202. }
  203. // read parameter description from the tip tag and from associated console variable
  204. QString strDescription;
  205. child->getAttr("tip", strDescription);
  206. QString strTipCVar;
  207. child->getAttr("TipCVar", strTipCVar);
  208. if (!strTipCVar.isEmpty())
  209. {
  210. strTipCVar.replace("*", child->getTag());
  211. if (ICVar* pCVar = gEnv->pConsole->GetCVar(strTipCVar.toUtf8().data()))
  212. {
  213. if (!strDescription.isEmpty())
  214. {
  215. strDescription += QString("\r\n");
  216. }
  217. strDescription = pCVar->GetHelp();
  218. #ifdef FEATURE_SVO_GI
  219. // Hide or unlock experimental items
  220. if ((pCVar->GetFlags() & VF_EXPERIMENTAL) && strstr(groupNode->getTag(), "Total_Illumination"))
  221. {
  222. AzToolsFramework::EditorSettingsAPIRequests::SettingOutcome outcome;
  223. AzToolsFramework::EditorSettingsAPIBus::BroadcastResult(outcome, &AzToolsFramework::EditorSettingsAPIBus::Handler::GetValue, "Settings\\ExperimentalFeatures|TotalIlluminationEnabled");
  224. AZStd::any outcomeValue = outcome.GetValue<AZStd::any>();
  225. if (outcomeValue.is<bool>())
  226. {
  227. bool featureEnabled = AZStd::any_cast<bool>(outcomeValue);
  228. if (!featureEnabled)
  229. {
  230. continue;
  231. }
  232. }
  233. }
  234. #endif
  235. }
  236. }
  237. QString humanReadableName;
  238. child->getAttr("human", humanReadableName);
  239. if (humanReadableName.isEmpty())
  240. {
  241. humanReadableName = child->getTag();
  242. if (splitCamelCaseIntoWords)
  243. {
  244. for (int index = 1; index < humanReadableName.length() - 1; index++)
  245. {
  246. // insert spaces between words
  247. if ((humanReadableName[index - 1].isLower() && humanReadableName[index].isUpper()) || (humanReadableName[index + 1].isLower() && humanReadableName[index - 1].isUpper() && humanReadableName[index].isUpper()))
  248. {
  249. humanReadableName.insert(index++, ' ');
  250. }
  251. // convert single upper cases letters to lower case
  252. if (humanReadableName[index].isUpper() && humanReadableName[index + 1].isLower())
  253. {
  254. humanReadableName[index] = humanReadableName[index].toLower();
  255. }
  256. }
  257. }
  258. }
  259. void* pUserData = reinterpret_cast<void*>((i << 16) | k);
  260. if (!azstricmp(type, "int"))
  261. {
  262. CSmartVariable<int> intVar;
  263. AddVariable(group, intVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
  264. int nValue(0);
  265. if (child->getAttr("value", nValue))
  266. {
  267. intVar->Set(nValue);
  268. }
  269. int nMin(0), nMax(0);
  270. if (child->getAttr("min", nMin) && child->getAttr("max", nMax))
  271. {
  272. intVar->SetLimits(static_cast<float>(nMin), static_cast<float>(nMax));
  273. }
  274. }
  275. else if (!azstricmp(type, "float"))
  276. {
  277. CSmartVariable<float> floatVar;
  278. AddVariable(group, floatVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
  279. float fValue(0.0f);
  280. if (child->getAttr("value", fValue))
  281. {
  282. floatVar->Set(fValue);
  283. }
  284. float fMin(0), fMax(0);
  285. if (child->getAttr("min", fMin) && child->getAttr("max", fMax))
  286. {
  287. floatVar->SetLimits(fMin, fMax);
  288. }
  289. }
  290. else if (!azstricmp(type, "vector"))
  291. {
  292. CSmartVariable<Vec3> vec3Var;
  293. AddVariable(group, vec3Var, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
  294. Vec3 vValue(0, 0, 0);
  295. if (child->getAttr("value", vValue))
  296. {
  297. vec3Var->Set(vValue);
  298. }
  299. }
  300. else if (!azstricmp(type, "bool"))
  301. {
  302. CSmartVariable<bool> bVar;
  303. AddVariable(group, bVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
  304. bool bValue(false);
  305. if (child->getAttr("value", bValue))
  306. {
  307. bVar->Set(bValue);
  308. }
  309. }
  310. else if (!azstricmp(type, "texture"))
  311. {
  312. CSmartVariable<QString> textureVar;
  313. AddVariable(group, textureVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData, IVariable::DT_TEXTURE);
  314. const char* textureName;
  315. if (child->getAttr("value", &textureName))
  316. {
  317. textureVar->Set(textureName);
  318. }
  319. }
  320. }
  321. }
  322. AddVarBlock(outBlockPtr);
  323. InvalidateCtrl();
  324. }
  325. void ReflectedPropertyControl::ReplaceVarBlock(IVariable *categoryItem, CVarBlock *varBlock)
  326. {
  327. assert(m_root);
  328. ReflectedPropertyItem *pCategoryItem = m_root->findItem(categoryItem);
  329. if (pCategoryItem)
  330. {
  331. pCategoryItem->ReplaceVarBlock(varBlock);
  332. m_editor->QueueInvalidation(AzToolsFramework::Refresh_EntireTree);
  333. }
  334. }
  335. void ReflectedPropertyControl::ReplaceRootVarBlock(CVarBlock* newVarBlock)
  336. {
  337. const AZStd::string category = m_rootContainer->m_varName;
  338. RemoveAllItems();
  339. AddVarBlock(newVarBlock, category.c_str());
  340. }
  341. void ReflectedPropertyControl::UpdateVarBlock(CVarBlock *pVarBlock)
  342. {
  343. UpdateVarBlock(m_root, pVarBlock, m_pVarBlock);
  344. m_editor->QueueInvalidation(AzToolsFramework::Refresh_AttributesAndValues);
  345. }
  346. void ReflectedPropertyControl::UpdateVarBlock(ReflectedPropertyItem* pPropertyItem, IVariableContainer *pSourceContainer, IVariableContainer *pTargetContainer)
  347. {
  348. for (int i = 0; i < pPropertyItem->GetChildCount(); ++i)
  349. {
  350. ReflectedPropertyItem *pChild = pPropertyItem->GetChild(i);
  351. if (pChild->GetType() != ePropertyInvalid)
  352. {
  353. const QString pPropertyVariableName = pChild->GetVariable()->GetName();
  354. IVariable* pTargetVariable = pTargetContainer->FindVariable(pPropertyVariableName.toUtf8().data());
  355. IVariable* pSourceVariable = pSourceContainer->FindVariable(pPropertyVariableName.toUtf8().data());
  356. if (pSourceVariable && pTargetVariable)
  357. {
  358. pTargetVariable->SetFlags(pSourceVariable->GetFlags());
  359. pTargetVariable->SetDisplayValue(pSourceVariable->GetDisplayValue());
  360. pTargetVariable->SetUserData(pSourceVariable->GetUserData());
  361. UpdateVarBlock(pChild, pSourceVariable, pTargetVariable);
  362. }
  363. }
  364. }
  365. }
  366. ReflectedPropertyItem* ReflectedPropertyControl::FindItemByVar(IVariable* pVar)
  367. {
  368. return m_root->findItem(pVar);
  369. }
  370. ReflectedPropertyItem* ReflectedPropertyControl::GetRootItem()
  371. {
  372. return m_root;
  373. }
  374. int ReflectedPropertyControl::GetContentHeight() const
  375. {
  376. return m_editor->GetContentHeight();
  377. }
  378. void ReflectedPropertyControl::AddCustomPopupMenuPopup(const QString& text, const AZStd::function<void(int)>& handler, const QStringList& items)
  379. {
  380. m_customPopupMenuPopups.push_back(SCustomPopupMenu(text, handler, items));
  381. }
  382. void ReflectedPropertyControl::AddCustomPopupMenuItem(const QString& text, const SCustomPopupItem::Callback handler)
  383. {
  384. m_customPopupMenuItems.push_back(SCustomPopupItem(text, handler));
  385. }
  386. //////////////////////////////////////////////////////////////////////////
  387. template<typename T>
  388. void ReflectedPropertyControl::RemoveCustomPopup(const QString& text, T& customPopup)
  389. {
  390. for (auto itr = customPopup.begin(); itr != customPopup.end(); ++itr)
  391. {
  392. if (text == itr->m_text)
  393. {
  394. customPopup.erase(itr);
  395. break;
  396. }
  397. }
  398. }
  399. //////////////////////////////////////////////////////////////////////////
  400. void ReflectedPropertyControl::RemoveCustomPopupMenuItem(const QString& text)
  401. {
  402. RemoveCustomPopup(text, m_customPopupMenuItems);
  403. }
  404. //////////////////////////////////////////////////////////////////////////
  405. void ReflectedPropertyControl::RemoveCustomPopupMenuPopup(const QString& text)
  406. {
  407. RemoveCustomPopup(text, m_customPopupMenuPopups);
  408. }
  409. void ReflectedPropertyControl::RestrictToItemsContaining(const QString &searchName)
  410. {
  411. m_filterString = searchName.toLower();
  412. RecreateAllItems();
  413. }
  414. void ReflectedPropertyControl::SetUpdateCallback(const ReflectedPropertyControl::UpdateVarCallback& callback)
  415. {
  416. m_updateVarFunc = callback;
  417. }
  418. void ReflectedPropertyControl::SetSavedStateKey(AZ::u32 key)
  419. {
  420. m_editor->SetSavedStateKey(key);
  421. }
  422. void ReflectedPropertyControl::RemoveAllItems()
  423. {
  424. m_editor->ClearInstances();
  425. m_rootContainer.reset();
  426. m_root.reset();
  427. }
  428. void ReflectedPropertyControl::ClearVarBlock()
  429. {
  430. RemoveAllItems();
  431. m_pVarBlock = nullptr;
  432. }
  433. void ReflectedPropertyControl::RecreateAllItems()
  434. {
  435. RemoveAllItems();
  436. AddVarBlock(m_pVarBlock);
  437. }
  438. void ReflectedPropertyControl::SetGroupProperties(bool group)
  439. {
  440. m_groupProperties = group;
  441. RecreateAllItems();
  442. }
  443. void ReflectedPropertyControl::SetSortProperties(bool sort)
  444. {
  445. m_sortProperties = sort;
  446. RecreateAllItems();
  447. }
  448. void ReflectedPropertyControl::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
  449. {
  450. if (!pNode)
  451. return;
  452. CReflectedVar *pReflectedVar = GetReflectedVarFromCallbackInstance(pNode);
  453. ReflectedPropertyItem *item = m_root->findItem(pReflectedVar);
  454. AZ_Assert(item, "No item found in property modification callback");
  455. item->OnReflectedVarChanged();
  456. OnItemChange(item);
  457. }
  458. void ReflectedPropertyControl::SetIsTwoColumnCtrlSection(bool isSection)
  459. {
  460. m_isTwoColumnSection = isSection;
  461. }
  462. void ReflectedPropertyControl::RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode* pNode, const QPoint& pos)
  463. {
  464. if (!pNode)
  465. return;
  466. CReflectedVar *pReflectedVar = GetReflectedVarFromCallbackInstance(pNode);
  467. ReflectedPropertyItem *pItem = m_root->findItem(pReflectedVar);
  468. AZ_Assert(pItem, "No item found in Context Menu callback");
  469. CClipboard clipboard(nullptr);
  470. // Popup Menu with Event selection.
  471. QMenu menu;
  472. unsigned int i = 0;
  473. const int ePPA_CustomItemBase = 10; // reserved from 10 to 99
  474. const int ePPA_CustomPopupBase = 100; // reserved from 100 to x*100+100 where x is size of m_customPopupMenuPopups
  475. menu.addAction(tr("Copy"), [&]() { OnCopy({ pItem }, false); });
  476. menu.addAction(tr("Copy Recursively"), [&]() { OnCopy({ pItem }, true); });
  477. if (m_isTwoColumnSection)
  478. {
  479. // For a two-column control, OnCopyAll will only copy all for this section
  480. // Emit a signal to the two-column control if we want to copy all sections
  481. menu.addAction(tr("Copy Section"), [&]() { OnCopyAll(); });
  482. menu.addAction(tr("Copy All"), [&]() { emit CopyAllSections(); });
  483. menu.addSeparator();
  484. menu.addAction(tr("Paste"), [&]() { emit PasteAllSections(); })->setEnabled(!clipboard.IsEmpty());
  485. }
  486. else
  487. {
  488. menu.addAction(tr("Copy All"), [&]() { OnCopyAll(); });
  489. menu.addSeparator();
  490. menu.addAction(tr("Paste"), [&]() { OnPaste(); })->setEnabled(!clipboard.IsEmpty());
  491. }
  492. if (!m_customPopupMenuItems.empty() || !m_customPopupMenuPopups.empty())
  493. {
  494. menu.addSeparator();
  495. }
  496. for (auto itr = m_customPopupMenuItems.cbegin(); itr != m_customPopupMenuItems.cend(); ++itr, ++i)
  497. {
  498. QAction *action = menu.addAction(itr->m_text);
  499. action->setData(ePPA_CustomItemBase + i);
  500. }
  501. for (unsigned int j = 0; j < m_customPopupMenuPopups.size(); ++j)
  502. {
  503. SCustomPopupMenu* pMenuInfo = &m_customPopupMenuPopups[j];
  504. QMenu* pSubMenu = menu.addMenu(pMenuInfo->m_text);
  505. for (UINT k = 0; k < static_cast<UINT>(pMenuInfo->m_subMenuText.size()); ++k)
  506. {
  507. const UINT uID = ePPA_CustomPopupBase + ePPA_CustomPopupBase * j + k;
  508. QAction *action = pSubMenu->addAction(pMenuInfo->m_subMenuText[k]);
  509. action->setData(uID);
  510. }
  511. }
  512. QAction *result = menu.exec(pos);
  513. if (!result)
  514. {
  515. return;
  516. }
  517. const int res = result->data().toInt();
  518. if (res >= ePPA_CustomItemBase && res < m_customPopupMenuItems.size() + ePPA_CustomItemBase)
  519. {
  520. m_customPopupMenuItems[res - ePPA_CustomItemBase].m_callback();
  521. }
  522. else if (res >= ePPA_CustomPopupBase && res < ePPA_CustomPopupBase + ePPA_CustomPopupBase * m_customPopupMenuPopups.size())
  523. {
  524. const int menuid = res / ePPA_CustomPopupBase - 1;
  525. const int option = res % ePPA_CustomPopupBase;
  526. m_customPopupMenuPopups[menuid].m_callback(option);
  527. }
  528. }
  529. void ReflectedPropertyControl::SetSelChangeCallback(SelChangeCallback callback)
  530. {
  531. m_selChangeFunc = callback;
  532. m_editor->SetSelectionEnabled(true);
  533. }
  534. void ReflectedPropertyControl::PropertySelectionChanged(AzToolsFramework::InstanceDataNode *pNode, bool selected)
  535. {
  536. if (!pNode)
  537. {
  538. return;
  539. }
  540. CReflectedVar *pReflectedVar = GetReflectedVarFromCallbackInstance(pNode);
  541. ReflectedPropertyItem *pItem = m_root->findItem(pReflectedVar);
  542. AZ_Assert(pItem, "No item found in selectoin change callback");
  543. if (m_selChangeFunc)
  544. {
  545. //keep same logic as MFC where pass null if it's deselection
  546. m_selChangeFunc(selected ? pItem->GetVariable() : nullptr);
  547. }
  548. }
  549. CReflectedVar * ReflectedPropertyControl::GetReflectedVarFromCallbackInstance(AzToolsFramework::InstanceDataNode *pNode)
  550. {
  551. if (!pNode)
  552. return nullptr;
  553. const AZ::SerializeContext::ClassData *classData = pNode->GetClassMetadata();
  554. if (classData->m_azRtti && classData->m_azRtti->IsTypeOf(CReflectedVar::TYPEINFO_Uuid()))
  555. return reinterpret_cast<CReflectedVar *>(pNode->GetInstance(0));
  556. else
  557. return GetReflectedVarFromCallbackInstance(pNode->GetParent());
  558. }
  559. AzToolsFramework::PropertyRowWidget* ReflectedPropertyControl::FindPropertyRowWidget(ReflectedPropertyItem* item)
  560. {
  561. if (!item)
  562. {
  563. return nullptr;
  564. }
  565. const AzToolsFramework::ReflectedPropertyEditor::WidgetList& widgets = m_editor->GetWidgets();
  566. for (const auto& instance : widgets)
  567. {
  568. if (instance.second->label() == item->GetPropertyName())
  569. {
  570. return instance.second;
  571. }
  572. }
  573. return nullptr;
  574. }
  575. void ReflectedPropertyControl::OnItemChange(ReflectedPropertyItem *item, bool deferCallbacks)
  576. {
  577. if (!item->IsModified() || !m_bSendCallbackOnNonModified)
  578. return;
  579. // variable updates/changes can trigger widgets being shown/hidden; allow a delay triggering the update
  580. // callback until after the current event queue is processed, so that we aren't changing other widgets
  581. // as a ton of them are still being created.
  582. Qt::ConnectionType connectionType = deferCallbacks ? Qt::QueuedConnection : Qt::DirectConnection;
  583. if (m_updateVarFunc && m_bEnableCallback)
  584. {
  585. QMetaObject::invokeMethod(this, "DoUpdateCallback", connectionType, Q_ARG(IVariable*, item->GetVariable()));
  586. }
  587. if (m_updateObjectFunc && m_bEnableCallback)
  588. {
  589. // KDAB: This callback has same signature as DoUpdateCallback. I think the only reason there are 2 is because some
  590. // EntityObject registers callback and some derived objects want to register their own callback. the normal UpdateCallback
  591. // can only be registered for item at a time so the original authors added a 2nd callback function, so we ported it this way.
  592. // This can probably get cleaned up to only on callback function with multiple receivers.
  593. QMetaObject::invokeMethod(this, "DoUpdateObjectCallback", connectionType, Q_ARG(IVariable*, item->GetVariable()));
  594. }
  595. }
  596. void ReflectedPropertyControl::DoUpdateCallback(IVariable *var)
  597. {
  598. // guard against element containing the IVariable being removed during a deferred callback
  599. const bool variableStillExists = FindVariable(var);
  600. AZ_Assert(variableStillExists, "This variable and the item containing it were destroyed during a deferred callback. Change to non-deferred callback.");
  601. if (!m_updateVarFunc || !variableStillExists)
  602. {
  603. return;
  604. }
  605. QScopedValueRollback<bool> rb(m_bEnableCallback, false);
  606. m_updateVarFunc(var);
  607. }
  608. void ReflectedPropertyControl::DoUpdateObjectCallback(IVariable *var)
  609. {
  610. // guard against element containing the IVariable being removed during a deferred callback
  611. const bool variableStillExists = FindVariable(var);
  612. AZ_Assert(variableStillExists, "This variable and the item containing it were destroyed during a deferred callback. Change to non-deferred callback.");
  613. if ( !m_updateVarFunc || !variableStillExists)
  614. {
  615. return;
  616. }
  617. QScopedValueRollback<bool> rb(m_bEnableCallback, false);
  618. m_updateObjectFunc(var);
  619. }
  620. void ReflectedPropertyControl::InvalidateCtrl(bool queued)
  621. {
  622. if (queued)
  623. {
  624. m_editor->QueueInvalidation(AzToolsFramework::Refresh_AttributesAndValues);
  625. }
  626. else
  627. {
  628. m_editor->InvalidateAttributesAndValues();
  629. }
  630. }
  631. void ReflectedPropertyControl::RebuildCtrl(bool queued)
  632. {
  633. if (queued)
  634. {
  635. m_editor->QueueInvalidation(AzToolsFramework::Refresh_EntireTree);
  636. }
  637. else
  638. {
  639. m_editor->InvalidateAll();
  640. }
  641. }
  642. bool ReflectedPropertyControl::CallUndoFunc(ReflectedPropertyItem *item)
  643. {
  644. if (!m_undoFunc)
  645. return false;
  646. m_undoFunc(item->GetVariable());
  647. return true;
  648. }
  649. void ReflectedPropertyControl::ClearSelection()
  650. {
  651. m_editor->SelectInstance(nullptr);
  652. }
  653. void ReflectedPropertyControl::SelectItem(ReflectedPropertyItem* item)
  654. {
  655. AzToolsFramework::PropertyRowWidget *widget = FindPropertyRowWidget(item);
  656. if (widget)
  657. {
  658. m_editor->SelectInstance(m_editor->GetNodeFromWidget(widget));
  659. }
  660. }
  661. ReflectedPropertyItem* ReflectedPropertyControl::GetSelectedItem()
  662. {
  663. AzToolsFramework::PropertyRowWidget *widget = m_editor->GetWidgetFromNode(m_editor->GetSelectedInstance());
  664. if (widget)
  665. {
  666. return m_root->findItem(widget->label());
  667. }
  668. return nullptr;
  669. }
  670. QVector<ReflectedPropertyItem*> ReflectedPropertyControl::GetSelectedItems()
  671. {
  672. auto item = GetSelectedItem();
  673. return item == nullptr ? QVector<ReflectedPropertyItem*>() : QVector<ReflectedPropertyItem*>{item};
  674. }
  675. void ReflectedPropertyControl::OnCopy(QVector<ReflectedPropertyItem*> itemsToCopy, bool bRecursively)
  676. {
  677. if (!itemsToCopy.isEmpty())
  678. {
  679. CClipboard clipboard(nullptr);
  680. auto rootNode = XmlHelpers::CreateXmlNode("PropertyCtrl");
  681. for (auto item : itemsToCopy)
  682. {
  683. CopyItem(rootNode, item, bRecursively);
  684. }
  685. clipboard.Put(rootNode);
  686. }
  687. }
  688. void ReflectedPropertyControl::OnCopyAll()
  689. {
  690. if (m_root)
  691. {
  692. CClipboard clipboard(nullptr);
  693. auto rootNode = XmlHelpers::CreateXmlNode("PropertyCtrl");
  694. OnCopyAll(rootNode);
  695. clipboard.Put(rootNode);
  696. }
  697. }
  698. void ReflectedPropertyControl::OnCopyAll(XmlNodeRef rootNode)
  699. {
  700. if (m_root)
  701. {
  702. for (int i = 0; i < m_root->GetChildCount(); i++)
  703. {
  704. CopyItem(rootNode, m_root->GetChild(i), true);
  705. }
  706. }
  707. }
  708. void ReflectedPropertyControl::OnPaste()
  709. {
  710. CClipboard clipboard(nullptr);
  711. CUndo undo("Paste Properties");
  712. XmlNodeRef rootNode = clipboard.Get();
  713. SetValuesFromNode(rootNode);
  714. }
  715. void ReflectedPropertyControl::SetValuesFromNode(XmlNodeRef rootNode)
  716. {
  717. if (!rootNode || !rootNode->isTag("PropertyCtrl"))
  718. {
  719. return;
  720. }
  721. for (int i = 0; i < rootNode->getChildCount(); i++)
  722. {
  723. XmlNodeRef node = rootNode->getChild(i);
  724. QString value;
  725. QString name;
  726. node->getAttr("Name", name);
  727. node->getAttr("Value", value);
  728. auto pItem = m_root->FindItemByFullName(name);
  729. if (pItem)
  730. {
  731. pItem->SetValue(value);
  732. // process callbacks immediately. In some cases, like the MaterialEditor the changing
  733. // the value of one item will change the properties available in this control
  734. // which needs to happen before we paste other values.
  735. OnItemChange(pItem, false);
  736. }
  737. }
  738. }
  739. void ReflectedPropertyControl::CopyItem(XmlNodeRef rootNode, ReflectedPropertyItem* pItem, bool bRecursively)
  740. {
  741. XmlNodeRef node = rootNode->newChild("PropertyItem");
  742. node->setAttr("Name", pItem->GetFullName().toLatin1().data());
  743. node->setAttr("Value", pItem->GetVariable()->GetDisplayValue().toLatin1().data());
  744. if (bRecursively)
  745. {
  746. for (int i = 0; i < pItem->GetChildCount(); i++)
  747. {
  748. CopyItem(rootNode, pItem->GetChild(i), bRecursively);
  749. }
  750. }
  751. }
  752. void ReflectedPropertyControl::ReloadValues()
  753. {
  754. if (m_root)
  755. m_root->ReloadValues();
  756. InvalidateCtrl();
  757. }
  758. void ReflectedPropertyControl::SetShowFilterWidget(bool showFilter)
  759. {
  760. m_filterWidget->setVisible(showFilter);
  761. }
  762. void ReflectedPropertyControl::SetUndoCallback(UndoCallback &callback)
  763. {
  764. m_undoFunc = callback;
  765. }
  766. void ReflectedPropertyControl::ClearUndoCallback()
  767. {
  768. m_undoFunc = nullptr;
  769. }
  770. bool ReflectedPropertyControl::FindVariable(IVariable *categoryItem) const
  771. {
  772. assert(m_root);
  773. if (!m_root)
  774. {
  775. return false;
  776. }
  777. ReflectedPropertyItem *pItem = m_root->findItem(categoryItem);
  778. return pItem != nullptr;
  779. }
  780. void ReflectedPropertyControl::EnableUpdateCallback(bool bEnable)
  781. {
  782. // Handle case where update callbacks were disabled and we're enabling them now
  783. // Need to force immediate invalidation of any queued invalidations made while callbacks were disabled
  784. // to make them fire now, while m_bEnableCallback is still false.
  785. if (bEnable && !m_bEnableCallback)
  786. {
  787. m_editor->ForceQueuedInvalidation();
  788. }
  789. m_bEnableCallback = bEnable;
  790. }
  791. void ReflectedPropertyControl::SetGrayed([[maybe_unused]] bool grayed)
  792. {
  793. //KDAB_PROPERTYCTRL_PORT_TODO
  794. //control should be grayed out but not disabled?
  795. }
  796. void ReflectedPropertyControl::SetReadOnly(bool readonly)
  797. {
  798. setEnabled(!readonly);
  799. }
  800. void ReflectedPropertyControl::SetMultiSelect([[maybe_unused]] bool multiselect)
  801. {
  802. //KDAB_PROPERTYCTRL_PORT_TODO
  803. }
  804. void ReflectedPropertyControl::EnableNotifyWithoutValueChange(bool bFlag)
  805. {
  806. m_bForceModified = bFlag;
  807. }
  808. void ReflectedPropertyControl::SetTitle(const QString &title)
  809. {
  810. m_titleLabel->setText(title);
  811. m_titleLabel->setHidden(title.isEmpty());
  812. }
  813. void ReflectedPropertyControl::ExpandAll()
  814. {
  815. m_editor->ExpandAll();
  816. }
  817. void ReflectedPropertyControl::CollapseAll()
  818. {
  819. m_editor->CollapseAll();
  820. }
  821. void ReflectedPropertyControl::Expand(ReflectedPropertyItem* item, bool expand)
  822. {
  823. item->Expand(expand);
  824. }
  825. void ReflectedPropertyControl::ExpandAllChildren(ReflectedPropertyItem* item, bool recursive)
  826. {
  827. item->ExpandAllChildren(recursive);
  828. }
  829. TwoColumnPropertyControl::TwoColumnPropertyControl(QWidget *parent /*= nullptr*/)
  830. : QWidget(parent)
  831. , m_twoColumns(true)
  832. {
  833. QHBoxLayout *mainlayout = new QHBoxLayout(this);
  834. m_leftContainer = new QWidget;
  835. auto leftLayout = new QVBoxLayout(m_leftContainer);
  836. leftLayout->setContentsMargins(0, 0, 0, 0);
  837. m_leftScrollArea = new QScrollArea;
  838. m_leftScrollArea->setMinimumWidth(minimumColumnWidth);
  839. m_leftScrollArea->setWidget(m_leftContainer);
  840. m_leftScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  841. m_leftScrollArea->setWidgetResizable(true);
  842. mainlayout->addWidget(m_leftScrollArea);
  843. m_rightContainer = new QWidget;
  844. auto rightLayout = new QVBoxLayout(m_rightContainer);
  845. rightLayout->setContentsMargins(0, 0, 0, 0);
  846. m_rightScrollArea = new QScrollArea;
  847. m_rightScrollArea->setMinimumWidth(minimumColumnWidth);
  848. m_rightScrollArea->setWidget(m_rightContainer);
  849. m_rightScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  850. m_rightScrollArea->setWidgetResizable(true);
  851. mainlayout->addWidget(m_rightScrollArea);
  852. }
  853. void TwoColumnPropertyControl::Setup([[maybe_unused]] bool showScrollbars /*= true*/, [[maybe_unused]] int labelWidth /*= 150*/)
  854. {
  855. }
  856. void TwoColumnPropertyControl::AddVarBlock(CVarBlock *varBlock, [[maybe_unused]] const char *szCategory /*= nullptr*/)
  857. {
  858. m_pVarBlock = varBlock;
  859. auto leftLayout = static_cast<QBoxLayout *>(m_leftContainer->layout());
  860. auto rightLayout = static_cast<QBoxLayout *>(m_rightContainer->layout());
  861. for (int i = 0; i < m_pVarBlock->GetNumVariables(); ++i)
  862. {
  863. CVarBlock *vb = new CVarBlock;
  864. PropertyCard *ctrl = new PropertyCard;
  865. m_varBlockList.append(vb);
  866. m_controlList.append(ctrl);
  867. IVariable *var = m_pVarBlock->GetVariable(i);
  868. vb->AddVariable(var);
  869. ctrl->AddVarBlock(vb);
  870. if (var->GetFlags() & IVariable::UI_ROLLUP2)
  871. {
  872. rightLayout->addWidget(ctrl);
  873. }
  874. else
  875. {
  876. leftLayout->addWidget(ctrl);
  877. }
  878. ctrl->GetControl()->SetIsTwoColumnCtrlSection(true);
  879. connect(ctrl->GetControl(), &ReflectedPropertyControl::CopyAllSections, this, &TwoColumnPropertyControl::OnCopyAll);
  880. connect(ctrl->GetControl(), &ReflectedPropertyControl::PasteAllSections, this, &TwoColumnPropertyControl::OnPaste);
  881. }
  882. leftLayout->addStretch(1);
  883. rightLayout->addStretch(1);
  884. }
  885. void TwoColumnPropertyControl::resizeEvent(QResizeEvent *event)
  886. {
  887. const bool twoColumns = event->size().width() >= minimumTwoColumnWidth;
  888. if (m_twoColumns != twoColumns)
  889. {
  890. ToggleTwoColumnLayout();
  891. }
  892. }
  893. void TwoColumnPropertyControl::ToggleTwoColumnLayout()
  894. {
  895. auto leftLayout = static_cast<QBoxLayout *>(m_leftContainer->layout());
  896. if (m_twoColumns)
  897. {
  898. // change layout to one column
  899. leftLayout->insertWidget(0, m_rightScrollArea->takeWidget());
  900. m_rightScrollArea->hide();
  901. }
  902. else
  903. {
  904. // change layout to two columns
  905. auto item = leftLayout->takeAt(0);
  906. m_rightScrollArea->setWidget(item->widget());
  907. delete item;
  908. m_rightScrollArea->show();
  909. }
  910. m_twoColumns = !m_twoColumns;
  911. }
  912. void TwoColumnPropertyControl::ReplaceVarBlock(IVariable *categoryItem, CVarBlock *varBlock)
  913. {
  914. for (auto ctrl : m_controlList)
  915. {
  916. ctrl->GetControl()->ReplaceVarBlock(categoryItem, varBlock);
  917. }
  918. }
  919. void TwoColumnPropertyControl::RemoveAllItems()
  920. {
  921. for (auto ctrl : m_controlList)
  922. {
  923. ctrl->GetControl()->RemoveAllItems();
  924. }
  925. }
  926. bool TwoColumnPropertyControl::FindVariable(IVariable *categoryItem) const
  927. {
  928. for (auto ctrl : m_controlList)
  929. {
  930. if (ctrl->GetControl()->FindVariable(categoryItem))
  931. {
  932. return true;
  933. }
  934. }
  935. return false;
  936. }
  937. void TwoColumnPropertyControl::InvalidateCtrl()
  938. {
  939. for (auto ctrl : m_controlList)
  940. {
  941. ctrl->GetControl()->InvalidateCtrl();
  942. }
  943. }
  944. void TwoColumnPropertyControl::RebuildCtrl()
  945. {
  946. for (auto ctrl : m_controlList)
  947. {
  948. ctrl->GetControl()->RebuildCtrl();
  949. }
  950. }
  951. void TwoColumnPropertyControl::SetStoreUndoByItems(bool bStoreUndoByItems)
  952. {
  953. for (auto ctrl : m_controlList)
  954. {
  955. ctrl->GetControl()->SetStoreUndoByItems(bStoreUndoByItems);
  956. }
  957. }
  958. void TwoColumnPropertyControl::SetUndoCallback(ReflectedPropertyControl::UndoCallback callback)
  959. {
  960. for (auto ctrl : m_controlList)
  961. {
  962. ctrl->GetControl()->SetUndoCallback(callback);
  963. }
  964. }
  965. void TwoColumnPropertyControl::ClearUndoCallback()
  966. {
  967. for (auto ctrl : m_controlList)
  968. {
  969. ctrl->GetControl()->ClearUndoCallback();
  970. }
  971. }
  972. void TwoColumnPropertyControl::EnableUpdateCallback(bool bEnable)
  973. {
  974. for (auto ctrl : m_controlList)
  975. {
  976. ctrl->GetControl()->EnableUpdateCallback(bEnable);
  977. }
  978. }
  979. void TwoColumnPropertyControl::SetUpdateCallback(ReflectedPropertyControl::UpdateVarCallback callback)
  980. {
  981. for (auto ctrl : m_controlList)
  982. {
  983. ctrl->GetControl()->SetUpdateCallback(callback);
  984. }
  985. }
  986. void TwoColumnPropertyControl::SetGrayed(bool grayed)
  987. {
  988. for (auto ctrl : m_controlList)
  989. {
  990. ctrl->GetControl()->SetGrayed(grayed);
  991. }
  992. }
  993. void TwoColumnPropertyControl::SetSavedStateKey(const QString &key)
  994. {
  995. for (int i = 0; i < m_controlList.count(); ++i)
  996. {
  997. m_controlList[i]->GetControl()->SetSavedStateKey(AZ::Crc32((key + QString::number(i)).toUtf8().data()));
  998. }
  999. }
  1000. void TwoColumnPropertyControl::ExpandAllChildren(ReflectedPropertyItem* item, bool recursive)
  1001. {
  1002. for (auto ctrl : m_controlList)
  1003. {
  1004. ctrl->GetControl()->ExpandAllChildren(item, recursive);
  1005. }
  1006. }
  1007. void TwoColumnPropertyControl::ExpandAllChildren(bool recursive)
  1008. {
  1009. for (auto ctrl : m_controlList)
  1010. {
  1011. ctrl->GetControl()->ExpandAllChildren(ctrl->GetControl()->GetRootItem(), recursive);
  1012. }
  1013. }
  1014. void TwoColumnPropertyControl::ReloadItems()
  1015. {
  1016. for (auto ctrl : m_controlList)
  1017. {
  1018. ctrl->GetControl()->ReloadValues();
  1019. }
  1020. }
  1021. void TwoColumnPropertyControl::OnCopyAll()
  1022. {
  1023. CClipboard clipboard(nullptr);
  1024. auto rootNode = XmlHelpers::CreateXmlNode("PropertyCtrl");
  1025. for (auto ctrl : m_controlList)
  1026. {
  1027. ctrl->GetControl()->OnCopyAll(rootNode);
  1028. }
  1029. clipboard.Put(rootNode);
  1030. }
  1031. void TwoColumnPropertyControl::OnPaste()
  1032. {
  1033. CClipboard clipboard(nullptr);
  1034. CUndo undo("Paste Properties");
  1035. XmlNodeRef rootNode = clipboard.Get();
  1036. for (auto ctrl : m_controlList)
  1037. {
  1038. ctrl->GetControl()->SetValuesFromNode(rootNode);
  1039. }
  1040. }
  1041. PropertyCard::PropertyCard([[maybe_unused]] QWidget* parent /*= nullptr*/)
  1042. {
  1043. // create header bar
  1044. m_header = new AzToolsFramework::ComponentEditorHeader(this);
  1045. m_header->SetExpandable(true);
  1046. // create property editor
  1047. m_propertyEditor = new ReflectedPropertyControl(this);
  1048. m_propertyEditor->Setup(false);
  1049. m_propertyEditor->GetEditor()->SetHideRootProperties(true);
  1050. m_propertyEditor->setProperty("ComponentDisabl", true); // used by stylesheet
  1051. QVBoxLayout *mainLayout = new QVBoxLayout(this);
  1052. mainLayout->setMargin(0);
  1053. mainLayout->setContentsMargins(0, 0, 0, 0);
  1054. mainLayout->addWidget(m_header);
  1055. mainLayout->addWidget(m_propertyEditor);
  1056. setLayout(mainLayout);
  1057. connect(m_header, &AzToolsFramework::ComponentEditorHeader::OnExpanderChanged, this, &PropertyCard::OnExpanderChanged);
  1058. connect(m_propertyEditor->GetEditor(), &AzToolsFramework::ReflectedPropertyEditor::OnExpansionContractionDone, this, &PropertyCard::OnExpansionContractionDone);
  1059. SetExpanded(true);
  1060. }
  1061. void PropertyCard::AddVarBlock(CVarBlock *varBlock)
  1062. {
  1063. if (varBlock->GetNumVariables() > 0)
  1064. {
  1065. m_header->SetTitle(varBlock->GetVariable(0)->GetName());
  1066. m_propertyEditor->AddVarBlock(varBlock);
  1067. }
  1068. }
  1069. ReflectedPropertyControl* PropertyCard::GetControl()
  1070. {
  1071. return m_propertyEditor;
  1072. }
  1073. void PropertyCard::SetExpanded(bool expanded)
  1074. {
  1075. m_header->SetExpanded(expanded);
  1076. m_propertyEditor->setVisible(expanded);
  1077. }
  1078. bool PropertyCard::IsExpanded() const
  1079. {
  1080. return m_header->IsExpanded();
  1081. }
  1082. void PropertyCard::OnExpanderChanged(bool expanded)
  1083. {
  1084. SetExpanded(expanded);
  1085. emit OnExpansionContractionDone();
  1086. }
  1087. #include <Controls/ReflectedPropertyControl/moc_ReflectedPropertyCtrl.cpp>