1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282 |
- /*
- * 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 "ReflectedPropertyCtrl.h"
- // Qt
- #include <QScopedValueRollback>
- #include <QHBoxLayout>
- #include <QVBoxLayout>
- #include <QLabel>
- #include <QLineEdit>
- #include <QMenu>
- #include <QScrollArea>
- // AzToolsFramework
- #include <AzToolsFramework/UI/PropertyEditor/ComponentEditorHeader.hxx>
- #include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
- #include <AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx>
- #include <AzToolsFramework/UI/SearchWidget/SearchCriteriaWidget.hxx>
- #include <AzToolsFramework/Editor/EditorSettingsAPIBus.h>
- //AzCore
- #include <AzCore/Component/ComponentApplicationBus.h>
- // Editor
- #include "Clipboard.h"
- ReflectedPropertyControl::ReflectedPropertyControl(QWidget *parent /*= nullptr*/, Qt::WindowFlags windowFlags /*= Qt::WindowFlags()*/)
- : QWidget(parent, windowFlags)
- , AzToolsFramework::IPropertyEditorNotify()
- , m_filterLineEdit(nullptr)
- , m_filterWidget(nullptr)
- , m_titleLabel(nullptr)
- , m_bEnableCallback(true)
- , m_updateVarFunc(nullptr)
- , m_updateObjectFunc(nullptr)
- , m_selChangeFunc(nullptr)
- , m_undoFunc(nullptr)
- , m_bStoreUndoByItems(true)
- , m_bForceModified(false)
- , m_groupProperties(false)
- , m_sortProperties(false)
- , m_bSendCallbackOnNonModified(true)
- , m_initialized(false)
- , m_isTwoColumnSection(false)
- {
- AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
- AZ_Assert(m_serializeContext, "Serialization context not available");
- qRegisterMetaType<IVariable*>("IVariablePtr");
- m_editor = new AzToolsFramework::ReflectedPropertyEditor(nullptr);
- m_editor->SetAutoResizeLabels(true);
- m_titleLabel = new QLabel;
- m_titleLabel->hide();
- m_filterWidget = new QWidget;
- QLabel *label = new QLabel(tr("Search"));
- m_filterLineEdit = new QLineEdit;
- QHBoxLayout *filterlayout = new QHBoxLayout(m_filterWidget);
- filterlayout->addWidget(label);
- filterlayout->addWidget(m_filterLineEdit);
- connect(m_filterLineEdit, &QLineEdit::textChanged, this, &ReflectedPropertyControl::RestrictToItemsContaining);
- QVBoxLayout *mainLayout = new QVBoxLayout(this);
- mainLayout->setContentsMargins(0, 0, 0, 0);
- mainLayout->addWidget(m_titleLabel, 0, Qt::AlignHCenter);
- mainLayout->addWidget(m_filterWidget);
- mainLayout->addWidget(m_editor, 1);
- SetShowFilterWidget(false);
- setMinimumSize(330, 0);
- }
- void ReflectedPropertyControl::Setup(bool showScrollbars /*= true*/, int labelWidth /*= 150*/)
- {
- if (!m_initialized)
- {
- m_editor->Setup(m_serializeContext, this, showScrollbars, labelWidth);
- m_initialized = true;
- }
- }
- QSize ReflectedPropertyControl::sizeHint() const
- {
- return m_editor->sizeHint();
- }
- ReflectedPropertyItem* ReflectedPropertyControl::AddVarBlock(CVarBlock *varBlock, const char *szCategory /*= nullptr*/)
- {
- AZ_Assert(m_initialized, "ReflectedPropertyControl not initialized. Setup must be called first.");
- if (!varBlock)
- return nullptr;
- m_pVarBlock = varBlock;
- if (!m_root)
- {
- m_root = new ReflectedPropertyItem(this, nullptr);
- m_rootContainer.reset(new CPropertyContainer(szCategory ? AZStd::string(szCategory) : AZStd::string()));
- m_rootContainer->SetAutoExpand(true);
- m_editor->AddInstance(m_rootContainer.get());
- }
- AZStd::vector<IVariable*> variables(varBlock->GetNumVariables());
- //Copy variables into vector
- int n = 0;
- std::generate(variables.begin(), variables.end(), [&n, varBlock]{return varBlock->GetVariable(n++); });
- //filter list based on search string
- if (!m_filterString.isEmpty())
- {
- AZStd::vector<IVariable*> newVariables;
- for (IVariable* var : variables)
- {
- if (QString(var->GetName()).toLower().contains(m_filterString))
- {
- newVariables.emplace_back(var);
- }
- }
- variables.swap(newVariables);
- }
- //sorting if needed. sort first when grouping to make grouping easier
- if (m_sortProperties || m_groupProperties)
- {
- std::sort(variables.begin(), variables.end(), [](IVariable *var1, IVariable *var2) {return QString::compare(var1->GetName(), var2->GetName(), Qt::CaseInsensitive) <=0; } );
- }
- CPropertyContainer *parentContainer = m_rootContainer.get();
- ReflectedPropertyItem *parentItem = m_root;
- QChar currentGroupInitial;
- for (auto var : variables)
- {
- if (m_groupProperties)
- {
- //check to see if this item starts with same letter as last. If not, create a new group for it.
- const QChar groupInitial = var->GetName().toUpper().at(0);
- if (groupInitial != currentGroupInitial)
- {
- currentGroupInitial = groupInitial;
- //make new group be the parent for this item
- parentItem = new ReflectedPropertyItem(this, parentItem);
- QString groupName(groupInitial);
- parentContainer = new CPropertyContainer(AZStd::string(groupName.toUtf8().data()));
- parentContainer->SetAutoExpand(false);
- m_rootContainer->AddProperty(parentContainer);
- }
- }
- ReflectedPropertyItemPtr childItem = new ReflectedPropertyItem(this, parentItem);
- childItem->SetVariable(var);
- CReflectedVar *reflectedVar = childItem->GetReflectedVar();
- parentContainer->AddProperty(reflectedVar);
- }
- m_editor->QueueInvalidation(AzToolsFramework::Refresh_EntireTree);
- return parentItem;
- }
- //////////////////////////////////////////////////////////////////////////
- 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)
- {
- if (varName)
- {
- var.SetName(varName);
- }
- if (humanVarName)
- {
- var.SetHumanName(humanVarName);
- }
- if (description)
- {
- var.SetDescription(description);
- }
- var.SetDataType(dataType);
- var.SetUserData(QVariant::fromValue<void *>(pUserData));
- if (func)
- {
- var.AddOnSetCallback(func);
- }
- varArray.AddVariable(&var);
- }
- void ReflectedPropertyControl::CreateItems(XmlNodeRef node)
- {
- CVarBlockPtr out;
- CreateItems(node, out, nullptr);
- }
- void ReflectedPropertyControl::CreateItems(XmlNodeRef node, CVarBlockPtr& outBlockPtr, IVariable::OnSetCallback* func, bool splitCamelCaseIntoWords)
- {
- SelectItem(nullptr);
- outBlockPtr = new CVarBlock;
- for (size_t i = 0, iGroupCount(node->getChildCount()); i < iGroupCount; ++i)
- {
- XmlNodeRef groupNode = node->getChild(static_cast<int>(i));
- if (groupNode->haveAttr("hidden"))
- {
- bool isGroupHidden = false;
- groupNode->getAttr("hidden", isGroupHidden);
- if (isGroupHidden)
- {
- // do not create visual editors for this group
- continue;
- }
- }
- CSmartVariableArray group;
- group->SetName(groupNode->getTag());
- group->SetHumanName(groupNode->getTag());
- group->SetDescription("");
- group->SetDataType(IVariable::DT_SIMPLE);
- outBlockPtr->AddVariable(&*group);
- for (int k = 0, iChildCount(groupNode->getChildCount()); k < iChildCount; ++k)
- {
- XmlNodeRef child = groupNode->getChild(k);
- const char* type;
- if (!child->getAttr("type", &type))
- {
- continue;
- }
- // read parameter description from the tip tag and from associated console variable
- QString strDescription;
- child->getAttr("tip", strDescription);
- QString strTipCVar;
- child->getAttr("TipCVar", strTipCVar);
- if (!strTipCVar.isEmpty())
- {
- strTipCVar.replace("*", child->getTag());
- if (ICVar* pCVar = gEnv->pConsole->GetCVar(strTipCVar.toUtf8().data()))
- {
- if (!strDescription.isEmpty())
- {
- strDescription += QString("\r\n");
- }
- strDescription = pCVar->GetHelp();
- #ifdef FEATURE_SVO_GI
- // Hide or unlock experimental items
- if ((pCVar->GetFlags() & VF_EXPERIMENTAL) && strstr(groupNode->getTag(), "Total_Illumination"))
- {
- AzToolsFramework::EditorSettingsAPIRequests::SettingOutcome outcome;
- AzToolsFramework::EditorSettingsAPIBus::BroadcastResult(outcome, &AzToolsFramework::EditorSettingsAPIBus::Handler::GetValue, "Settings\\ExperimentalFeatures|TotalIlluminationEnabled");
- AZStd::any outcomeValue = outcome.GetValue<AZStd::any>();
- if (outcomeValue.is<bool>())
- {
- bool featureEnabled = AZStd::any_cast<bool>(outcomeValue);
- if (!featureEnabled)
- {
- continue;
- }
- }
- }
- #endif
- }
- }
- QString humanReadableName;
- child->getAttr("human", humanReadableName);
- if (humanReadableName.isEmpty())
- {
- humanReadableName = child->getTag();
- if (splitCamelCaseIntoWords)
- {
- for (int index = 1; index < humanReadableName.length() - 1; index++)
- {
- // insert spaces between words
- if ((humanReadableName[index - 1].isLower() && humanReadableName[index].isUpper()) || (humanReadableName[index + 1].isLower() && humanReadableName[index - 1].isUpper() && humanReadableName[index].isUpper()))
- {
- humanReadableName.insert(index++, ' ');
- }
- // convert single upper cases letters to lower case
- if (humanReadableName[index].isUpper() && humanReadableName[index + 1].isLower())
- {
- humanReadableName[index] = humanReadableName[index].toLower();
- }
- }
- }
- }
- void* pUserData = reinterpret_cast<void*>((i << 16) | k);
- if (!azstricmp(type, "int"))
- {
- CSmartVariable<int> intVar;
- AddVariable(group, intVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
- int nValue(0);
- if (child->getAttr("value", nValue))
- {
- intVar->Set(nValue);
- }
- int nMin(0), nMax(0);
- if (child->getAttr("min", nMin) && child->getAttr("max", nMax))
- {
- intVar->SetLimits(static_cast<float>(nMin), static_cast<float>(nMax));
- }
- }
- else if (!azstricmp(type, "float"))
- {
- CSmartVariable<float> floatVar;
- AddVariable(group, floatVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
- float fValue(0.0f);
- if (child->getAttr("value", fValue))
- {
- floatVar->Set(fValue);
- }
- float fMin(0), fMax(0);
- if (child->getAttr("min", fMin) && child->getAttr("max", fMax))
- {
- floatVar->SetLimits(fMin, fMax);
- }
- }
- else if (!azstricmp(type, "vector"))
- {
- CSmartVariable<Vec3> vec3Var;
- AddVariable(group, vec3Var, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
- Vec3 vValue(0, 0, 0);
- if (child->getAttr("value", vValue))
- {
- vec3Var->Set(vValue);
- }
- }
- else if (!azstricmp(type, "bool"))
- {
- CSmartVariable<bool> bVar;
- AddVariable(group, bVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData);
- bool bValue(false);
- if (child->getAttr("value", bValue))
- {
- bVar->Set(bValue);
- }
- }
- else if (!azstricmp(type, "texture"))
- {
- CSmartVariable<QString> textureVar;
- AddVariable(group, textureVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData, IVariable::DT_TEXTURE);
- const char* textureName;
- if (child->getAttr("value", &textureName))
- {
- textureVar->Set(textureName);
- }
- }
- }
- }
- AddVarBlock(outBlockPtr);
- InvalidateCtrl();
- }
- void ReflectedPropertyControl::ReplaceVarBlock(IVariable *categoryItem, CVarBlock *varBlock)
- {
- assert(m_root);
- ReflectedPropertyItem *pCategoryItem = m_root->findItem(categoryItem);
- if (pCategoryItem)
- {
- pCategoryItem->ReplaceVarBlock(varBlock);
- m_editor->QueueInvalidation(AzToolsFramework::Refresh_EntireTree);
- }
- }
- void ReflectedPropertyControl::ReplaceRootVarBlock(CVarBlock* newVarBlock)
- {
- const AZStd::string category = m_rootContainer->m_varName;
- RemoveAllItems();
- AddVarBlock(newVarBlock, category.c_str());
- }
- void ReflectedPropertyControl::UpdateVarBlock(CVarBlock *pVarBlock)
- {
- UpdateVarBlock(m_root, pVarBlock, m_pVarBlock);
- m_editor->QueueInvalidation(AzToolsFramework::Refresh_AttributesAndValues);
- }
- void ReflectedPropertyControl::UpdateVarBlock(ReflectedPropertyItem* pPropertyItem, IVariableContainer *pSourceContainer, IVariableContainer *pTargetContainer)
- {
- for (int i = 0; i < pPropertyItem->GetChildCount(); ++i)
- {
- ReflectedPropertyItem *pChild = pPropertyItem->GetChild(i);
- if (pChild->GetType() != ePropertyInvalid)
- {
- const QString pPropertyVariableName = pChild->GetVariable()->GetName();
- IVariable* pTargetVariable = pTargetContainer->FindVariable(pPropertyVariableName.toUtf8().data());
- IVariable* pSourceVariable = pSourceContainer->FindVariable(pPropertyVariableName.toUtf8().data());
- if (pSourceVariable && pTargetVariable)
- {
- pTargetVariable->SetFlags(pSourceVariable->GetFlags());
- pTargetVariable->SetDisplayValue(pSourceVariable->GetDisplayValue());
- pTargetVariable->SetUserData(pSourceVariable->GetUserData());
- UpdateVarBlock(pChild, pSourceVariable, pTargetVariable);
- }
- }
- }
- }
- ReflectedPropertyItem* ReflectedPropertyControl::FindItemByVar(IVariable* pVar)
- {
- return m_root->findItem(pVar);
- }
- ReflectedPropertyItem* ReflectedPropertyControl::GetRootItem()
- {
- return m_root;
- }
- int ReflectedPropertyControl::GetContentHeight() const
- {
- return m_editor->GetContentHeight();
- }
- void ReflectedPropertyControl::AddCustomPopupMenuPopup(const QString& text, const AZStd::function<void(int)>& handler, const QStringList& items)
- {
- m_customPopupMenuPopups.push_back(SCustomPopupMenu(text, handler, items));
- }
- void ReflectedPropertyControl::AddCustomPopupMenuItem(const QString& text, const SCustomPopupItem::Callback handler)
- {
- m_customPopupMenuItems.push_back(SCustomPopupItem(text, handler));
- }
- //////////////////////////////////////////////////////////////////////////
- template<typename T>
- void ReflectedPropertyControl::RemoveCustomPopup(const QString& text, T& customPopup)
- {
- for (auto itr = customPopup.begin(); itr != customPopup.end(); ++itr)
- {
- if (text == itr->m_text)
- {
- customPopup.erase(itr);
- break;
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void ReflectedPropertyControl::RemoveCustomPopupMenuItem(const QString& text)
- {
- RemoveCustomPopup(text, m_customPopupMenuItems);
- }
- //////////////////////////////////////////////////////////////////////////
- void ReflectedPropertyControl::RemoveCustomPopupMenuPopup(const QString& text)
- {
- RemoveCustomPopup(text, m_customPopupMenuPopups);
- }
- void ReflectedPropertyControl::RestrictToItemsContaining(const QString &searchName)
- {
- m_filterString = searchName.toLower();
- RecreateAllItems();
- }
- void ReflectedPropertyControl::SetUpdateCallback(const ReflectedPropertyControl::UpdateVarCallback& callback)
- {
- m_updateVarFunc = callback;
- }
- void ReflectedPropertyControl::SetSavedStateKey(AZ::u32 key)
- {
- m_editor->SetSavedStateKey(key);
- }
- void ReflectedPropertyControl::RemoveAllItems()
- {
- m_editor->ClearInstances();
- m_rootContainer.reset();
- m_root.reset();
- }
- void ReflectedPropertyControl::ClearVarBlock()
- {
- RemoveAllItems();
- m_pVarBlock = nullptr;
- }
- void ReflectedPropertyControl::RecreateAllItems()
- {
- RemoveAllItems();
- AddVarBlock(m_pVarBlock);
- }
- void ReflectedPropertyControl::SetGroupProperties(bool group)
- {
- m_groupProperties = group;
- RecreateAllItems();
- }
- void ReflectedPropertyControl::SetSortProperties(bool sort)
- {
- m_sortProperties = sort;
- RecreateAllItems();
- }
- void ReflectedPropertyControl::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
- {
- if (!pNode)
- return;
- CReflectedVar *pReflectedVar = GetReflectedVarFromCallbackInstance(pNode);
- ReflectedPropertyItem *item = m_root->findItem(pReflectedVar);
- AZ_Assert(item, "No item found in property modification callback");
- item->OnReflectedVarChanged();
- OnItemChange(item);
- }
- void ReflectedPropertyControl::SetIsTwoColumnCtrlSection(bool isSection)
- {
- m_isTwoColumnSection = isSection;
- }
- void ReflectedPropertyControl::RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode* pNode, const QPoint& pos)
- {
- if (!pNode)
- return;
- CReflectedVar *pReflectedVar = GetReflectedVarFromCallbackInstance(pNode);
- ReflectedPropertyItem *pItem = m_root->findItem(pReflectedVar);
- AZ_Assert(pItem, "No item found in Context Menu callback");
- CClipboard clipboard(nullptr);
- // Popup Menu with Event selection.
- QMenu menu;
- unsigned int i = 0;
- const int ePPA_CustomItemBase = 10; // reserved from 10 to 99
- const int ePPA_CustomPopupBase = 100; // reserved from 100 to x*100+100 where x is size of m_customPopupMenuPopups
- menu.addAction(tr("Copy"), [&]() { OnCopy({ pItem }, false); });
- menu.addAction(tr("Copy Recursively"), [&]() { OnCopy({ pItem }, true); });
- if (m_isTwoColumnSection)
- {
- // For a two-column control, OnCopyAll will only copy all for this section
- // Emit a signal to the two-column control if we want to copy all sections
- menu.addAction(tr("Copy Section"), [&]() { OnCopyAll(); });
- menu.addAction(tr("Copy All"), [&]() { emit CopyAllSections(); });
- menu.addSeparator();
- menu.addAction(tr("Paste"), [&]() { emit PasteAllSections(); })->setEnabled(!clipboard.IsEmpty());
- }
- else
- {
- menu.addAction(tr("Copy All"), [&]() { OnCopyAll(); });
- menu.addSeparator();
- menu.addAction(tr("Paste"), [&]() { OnPaste(); })->setEnabled(!clipboard.IsEmpty());
- }
- if (!m_customPopupMenuItems.empty() || !m_customPopupMenuPopups.empty())
- {
- menu.addSeparator();
- }
- for (auto itr = m_customPopupMenuItems.cbegin(); itr != m_customPopupMenuItems.cend(); ++itr, ++i)
- {
- QAction *action = menu.addAction(itr->m_text);
- action->setData(ePPA_CustomItemBase + i);
- }
- for (unsigned int j = 0; j < m_customPopupMenuPopups.size(); ++j)
- {
- SCustomPopupMenu* pMenuInfo = &m_customPopupMenuPopups[j];
- QMenu* pSubMenu = menu.addMenu(pMenuInfo->m_text);
- for (UINT k = 0; k < static_cast<UINT>(pMenuInfo->m_subMenuText.size()); ++k)
- {
- const UINT uID = ePPA_CustomPopupBase + ePPA_CustomPopupBase * j + k;
- QAction *action = pSubMenu->addAction(pMenuInfo->m_subMenuText[k]);
- action->setData(uID);
- }
- }
- QAction *result = menu.exec(pos);
- if (!result)
- {
- return;
- }
- const int res = result->data().toInt();
- if (res >= ePPA_CustomItemBase && res < m_customPopupMenuItems.size() + ePPA_CustomItemBase)
- {
- m_customPopupMenuItems[res - ePPA_CustomItemBase].m_callback();
- }
- else if (res >= ePPA_CustomPopupBase && res < ePPA_CustomPopupBase + ePPA_CustomPopupBase * m_customPopupMenuPopups.size())
- {
- const int menuid = res / ePPA_CustomPopupBase - 1;
- const int option = res % ePPA_CustomPopupBase;
- m_customPopupMenuPopups[menuid].m_callback(option);
- }
- }
- void ReflectedPropertyControl::SetSelChangeCallback(SelChangeCallback callback)
- {
- m_selChangeFunc = callback;
- m_editor->SetSelectionEnabled(true);
- }
- void ReflectedPropertyControl::PropertySelectionChanged(AzToolsFramework::InstanceDataNode *pNode, bool selected)
- {
- if (!pNode)
- {
- return;
- }
- CReflectedVar *pReflectedVar = GetReflectedVarFromCallbackInstance(pNode);
- ReflectedPropertyItem *pItem = m_root->findItem(pReflectedVar);
- AZ_Assert(pItem, "No item found in selectoin change callback");
- if (m_selChangeFunc)
- {
- //keep same logic as MFC where pass null if it's deselection
- m_selChangeFunc(selected ? pItem->GetVariable() : nullptr);
- }
- }
- CReflectedVar * ReflectedPropertyControl::GetReflectedVarFromCallbackInstance(AzToolsFramework::InstanceDataNode *pNode)
- {
- if (!pNode)
- return nullptr;
- const AZ::SerializeContext::ClassData *classData = pNode->GetClassMetadata();
- if (classData->m_azRtti && classData->m_azRtti->IsTypeOf(CReflectedVar::TYPEINFO_Uuid()))
- return reinterpret_cast<CReflectedVar *>(pNode->GetInstance(0));
- else
- return GetReflectedVarFromCallbackInstance(pNode->GetParent());
- }
- AzToolsFramework::PropertyRowWidget* ReflectedPropertyControl::FindPropertyRowWidget(ReflectedPropertyItem* item)
- {
- if (!item)
- {
- return nullptr;
- }
- const AzToolsFramework::ReflectedPropertyEditor::WidgetList& widgets = m_editor->GetWidgets();
- for (const auto& instance : widgets)
- {
- if (instance.second->label() == item->GetPropertyName())
- {
- return instance.second;
- }
- }
- return nullptr;
- }
- void ReflectedPropertyControl::OnItemChange(ReflectedPropertyItem *item, bool deferCallbacks)
- {
- if (!item->IsModified() || !m_bSendCallbackOnNonModified)
- return;
- // variable updates/changes can trigger widgets being shown/hidden; allow a delay triggering the update
- // callback until after the current event queue is processed, so that we aren't changing other widgets
- // as a ton of them are still being created.
- Qt::ConnectionType connectionType = deferCallbacks ? Qt::QueuedConnection : Qt::DirectConnection;
- if (m_updateVarFunc && m_bEnableCallback)
- {
- QMetaObject::invokeMethod(this, "DoUpdateCallback", connectionType, Q_ARG(IVariable*, item->GetVariable()));
- }
- if (m_updateObjectFunc && m_bEnableCallback)
- {
- // KDAB: This callback has same signature as DoUpdateCallback. I think the only reason there are 2 is because some
- // EntityObject registers callback and some derived objects want to register their own callback. the normal UpdateCallback
- // can only be registered for item at a time so the original authors added a 2nd callback function, so we ported it this way.
- // This can probably get cleaned up to only on callback function with multiple receivers.
- QMetaObject::invokeMethod(this, "DoUpdateObjectCallback", connectionType, Q_ARG(IVariable*, item->GetVariable()));
- }
- }
- void ReflectedPropertyControl::DoUpdateCallback(IVariable *var)
- {
- // guard against element containing the IVariable being removed during a deferred callback
- const bool variableStillExists = FindVariable(var);
- AZ_Assert(variableStillExists, "This variable and the item containing it were destroyed during a deferred callback. Change to non-deferred callback.");
- if (!m_updateVarFunc || !variableStillExists)
- {
- return;
- }
- QScopedValueRollback<bool> rb(m_bEnableCallback, false);
- m_updateVarFunc(var);
- }
- void ReflectedPropertyControl::DoUpdateObjectCallback(IVariable *var)
- {
- // guard against element containing the IVariable being removed during a deferred callback
- const bool variableStillExists = FindVariable(var);
- AZ_Assert(variableStillExists, "This variable and the item containing it were destroyed during a deferred callback. Change to non-deferred callback.");
- if ( !m_updateVarFunc || !variableStillExists)
- {
- return;
- }
- QScopedValueRollback<bool> rb(m_bEnableCallback, false);
- m_updateObjectFunc(var);
- }
- void ReflectedPropertyControl::InvalidateCtrl(bool queued)
- {
- if (queued)
- {
- m_editor->QueueInvalidation(AzToolsFramework::Refresh_AttributesAndValues);
- }
- else
- {
- m_editor->InvalidateAttributesAndValues();
- }
- }
- void ReflectedPropertyControl::RebuildCtrl(bool queued)
- {
- if (queued)
- {
- m_editor->QueueInvalidation(AzToolsFramework::Refresh_EntireTree);
- }
- else
- {
- m_editor->InvalidateAll();
- }
- }
- bool ReflectedPropertyControl::CallUndoFunc(ReflectedPropertyItem *item)
- {
- if (!m_undoFunc)
- return false;
- m_undoFunc(item->GetVariable());
- return true;
- }
- void ReflectedPropertyControl::ClearSelection()
- {
- m_editor->SelectInstance(nullptr);
- }
- void ReflectedPropertyControl::SelectItem(ReflectedPropertyItem* item)
- {
- AzToolsFramework::PropertyRowWidget *widget = FindPropertyRowWidget(item);
- if (widget)
- {
- m_editor->SelectInstance(m_editor->GetNodeFromWidget(widget));
- }
- }
- ReflectedPropertyItem* ReflectedPropertyControl::GetSelectedItem()
- {
- AzToolsFramework::PropertyRowWidget *widget = m_editor->GetWidgetFromNode(m_editor->GetSelectedInstance());
- if (widget)
- {
- return m_root->findItem(widget->label());
- }
- return nullptr;
- }
- QVector<ReflectedPropertyItem*> ReflectedPropertyControl::GetSelectedItems()
- {
- auto item = GetSelectedItem();
- return item == nullptr ? QVector<ReflectedPropertyItem*>() : QVector<ReflectedPropertyItem*>{item};
- }
- void ReflectedPropertyControl::OnCopy(QVector<ReflectedPropertyItem*> itemsToCopy, bool bRecursively)
- {
- if (!itemsToCopy.isEmpty())
- {
- CClipboard clipboard(nullptr);
- auto rootNode = XmlHelpers::CreateXmlNode("PropertyCtrl");
- for (auto item : itemsToCopy)
- {
- CopyItem(rootNode, item, bRecursively);
- }
- clipboard.Put(rootNode);
- }
- }
- void ReflectedPropertyControl::OnCopyAll()
- {
- if (m_root)
- {
- CClipboard clipboard(nullptr);
- auto rootNode = XmlHelpers::CreateXmlNode("PropertyCtrl");
- OnCopyAll(rootNode);
- clipboard.Put(rootNode);
- }
- }
- void ReflectedPropertyControl::OnCopyAll(XmlNodeRef rootNode)
- {
- if (m_root)
- {
- for (int i = 0; i < m_root->GetChildCount(); i++)
- {
- CopyItem(rootNode, m_root->GetChild(i), true);
- }
- }
- }
- void ReflectedPropertyControl::OnPaste()
- {
- CClipboard clipboard(nullptr);
- CUndo undo("Paste Properties");
- XmlNodeRef rootNode = clipboard.Get();
- SetValuesFromNode(rootNode);
- }
- void ReflectedPropertyControl::SetValuesFromNode(XmlNodeRef rootNode)
- {
- if (!rootNode || !rootNode->isTag("PropertyCtrl"))
- {
- return;
- }
- for (int i = 0; i < rootNode->getChildCount(); i++)
- {
- XmlNodeRef node = rootNode->getChild(i);
- QString value;
- QString name;
- node->getAttr("Name", name);
- node->getAttr("Value", value);
- auto pItem = m_root->FindItemByFullName(name);
- if (pItem)
- {
- pItem->SetValue(value);
- // process callbacks immediately. In some cases, like the MaterialEditor the changing
- // the value of one item will change the properties available in this control
- // which needs to happen before we paste other values.
- OnItemChange(pItem, false);
- }
- }
- }
- void ReflectedPropertyControl::CopyItem(XmlNodeRef rootNode, ReflectedPropertyItem* pItem, bool bRecursively)
- {
- XmlNodeRef node = rootNode->newChild("PropertyItem");
- node->setAttr("Name", pItem->GetFullName().toLatin1().data());
- node->setAttr("Value", pItem->GetVariable()->GetDisplayValue().toLatin1().data());
- if (bRecursively)
- {
- for (int i = 0; i < pItem->GetChildCount(); i++)
- {
- CopyItem(rootNode, pItem->GetChild(i), bRecursively);
- }
- }
- }
- void ReflectedPropertyControl::ReloadValues()
- {
- if (m_root)
- m_root->ReloadValues();
- InvalidateCtrl();
- }
- void ReflectedPropertyControl::SetShowFilterWidget(bool showFilter)
- {
- m_filterWidget->setVisible(showFilter);
- }
- void ReflectedPropertyControl::SetUndoCallback(UndoCallback &callback)
- {
- m_undoFunc = callback;
- }
- void ReflectedPropertyControl::ClearUndoCallback()
- {
- m_undoFunc = nullptr;
- }
- bool ReflectedPropertyControl::FindVariable(IVariable *categoryItem) const
- {
- assert(m_root);
- if (!m_root)
- {
- return false;
- }
- ReflectedPropertyItem *pItem = m_root->findItem(categoryItem);
- return pItem != nullptr;
- }
- void ReflectedPropertyControl::EnableUpdateCallback(bool bEnable)
- {
- // Handle case where update callbacks were disabled and we're enabling them now
- // Need to force immediate invalidation of any queued invalidations made while callbacks were disabled
- // to make them fire now, while m_bEnableCallback is still false.
- if (bEnable && !m_bEnableCallback)
- {
- m_editor->ForceQueuedInvalidation();
- }
- m_bEnableCallback = bEnable;
- }
- void ReflectedPropertyControl::SetGrayed([[maybe_unused]] bool grayed)
- {
- //KDAB_PROPERTYCTRL_PORT_TODO
- //control should be grayed out but not disabled?
- }
- void ReflectedPropertyControl::SetReadOnly(bool readonly)
- {
- setEnabled(!readonly);
- }
- void ReflectedPropertyControl::SetMultiSelect([[maybe_unused]] bool multiselect)
- {
- //KDAB_PROPERTYCTRL_PORT_TODO
- }
- void ReflectedPropertyControl::EnableNotifyWithoutValueChange(bool bFlag)
- {
- m_bForceModified = bFlag;
- }
- void ReflectedPropertyControl::SetTitle(const QString &title)
- {
- m_titleLabel->setText(title);
- m_titleLabel->setHidden(title.isEmpty());
- }
- void ReflectedPropertyControl::ExpandAll()
- {
- m_editor->ExpandAll();
- }
- void ReflectedPropertyControl::CollapseAll()
- {
- m_editor->CollapseAll();
- }
- void ReflectedPropertyControl::Expand(ReflectedPropertyItem* item, bool expand)
- {
- item->Expand(expand);
- }
- void ReflectedPropertyControl::ExpandAllChildren(ReflectedPropertyItem* item, bool recursive)
- {
- item->ExpandAllChildren(recursive);
- }
- TwoColumnPropertyControl::TwoColumnPropertyControl(QWidget *parent /*= nullptr*/)
- : QWidget(parent)
- , m_twoColumns(true)
- {
- QHBoxLayout *mainlayout = new QHBoxLayout(this);
- m_leftContainer = new QWidget;
- auto leftLayout = new QVBoxLayout(m_leftContainer);
- leftLayout->setContentsMargins(0, 0, 0, 0);
- m_leftScrollArea = new QScrollArea;
- m_leftScrollArea->setMinimumWidth(minimumColumnWidth);
- m_leftScrollArea->setWidget(m_leftContainer);
- m_leftScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- m_leftScrollArea->setWidgetResizable(true);
- mainlayout->addWidget(m_leftScrollArea);
- m_rightContainer = new QWidget;
- auto rightLayout = new QVBoxLayout(m_rightContainer);
- rightLayout->setContentsMargins(0, 0, 0, 0);
- m_rightScrollArea = new QScrollArea;
- m_rightScrollArea->setMinimumWidth(minimumColumnWidth);
- m_rightScrollArea->setWidget(m_rightContainer);
- m_rightScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- m_rightScrollArea->setWidgetResizable(true);
- mainlayout->addWidget(m_rightScrollArea);
- }
- void TwoColumnPropertyControl::Setup([[maybe_unused]] bool showScrollbars /*= true*/, [[maybe_unused]] int labelWidth /*= 150*/)
- {
- }
- void TwoColumnPropertyControl::AddVarBlock(CVarBlock *varBlock, [[maybe_unused]] const char *szCategory /*= nullptr*/)
- {
- m_pVarBlock = varBlock;
- auto leftLayout = static_cast<QBoxLayout *>(m_leftContainer->layout());
- auto rightLayout = static_cast<QBoxLayout *>(m_rightContainer->layout());
- for (int i = 0; i < m_pVarBlock->GetNumVariables(); ++i)
- {
- CVarBlock *vb = new CVarBlock;
- PropertyCard *ctrl = new PropertyCard;
- m_varBlockList.append(vb);
- m_controlList.append(ctrl);
- IVariable *var = m_pVarBlock->GetVariable(i);
- vb->AddVariable(var);
- ctrl->AddVarBlock(vb);
- if (var->GetFlags() & IVariable::UI_ROLLUP2)
- {
- rightLayout->addWidget(ctrl);
- }
- else
- {
- leftLayout->addWidget(ctrl);
- }
- ctrl->GetControl()->SetIsTwoColumnCtrlSection(true);
- connect(ctrl->GetControl(), &ReflectedPropertyControl::CopyAllSections, this, &TwoColumnPropertyControl::OnCopyAll);
- connect(ctrl->GetControl(), &ReflectedPropertyControl::PasteAllSections, this, &TwoColumnPropertyControl::OnPaste);
- }
- leftLayout->addStretch(1);
- rightLayout->addStretch(1);
- }
- void TwoColumnPropertyControl::resizeEvent(QResizeEvent *event)
- {
- const bool twoColumns = event->size().width() >= minimumTwoColumnWidth;
- if (m_twoColumns != twoColumns)
- {
- ToggleTwoColumnLayout();
- }
- }
- void TwoColumnPropertyControl::ToggleTwoColumnLayout()
- {
- auto leftLayout = static_cast<QBoxLayout *>(m_leftContainer->layout());
- if (m_twoColumns)
- {
- // change layout to one column
- leftLayout->insertWidget(0, m_rightScrollArea->takeWidget());
- m_rightScrollArea->hide();
- }
- else
- {
- // change layout to two columns
- auto item = leftLayout->takeAt(0);
- m_rightScrollArea->setWidget(item->widget());
- delete item;
- m_rightScrollArea->show();
- }
- m_twoColumns = !m_twoColumns;
- }
- void TwoColumnPropertyControl::ReplaceVarBlock(IVariable *categoryItem, CVarBlock *varBlock)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->ReplaceVarBlock(categoryItem, varBlock);
- }
- }
- void TwoColumnPropertyControl::RemoveAllItems()
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->RemoveAllItems();
- }
- }
- bool TwoColumnPropertyControl::FindVariable(IVariable *categoryItem) const
- {
- for (auto ctrl : m_controlList)
- {
- if (ctrl->GetControl()->FindVariable(categoryItem))
- {
- return true;
- }
- }
- return false;
- }
- void TwoColumnPropertyControl::InvalidateCtrl()
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->InvalidateCtrl();
- }
- }
- void TwoColumnPropertyControl::RebuildCtrl()
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->RebuildCtrl();
- }
- }
- void TwoColumnPropertyControl::SetStoreUndoByItems(bool bStoreUndoByItems)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->SetStoreUndoByItems(bStoreUndoByItems);
- }
- }
- void TwoColumnPropertyControl::SetUndoCallback(ReflectedPropertyControl::UndoCallback callback)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->SetUndoCallback(callback);
- }
- }
- void TwoColumnPropertyControl::ClearUndoCallback()
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->ClearUndoCallback();
- }
- }
- void TwoColumnPropertyControl::EnableUpdateCallback(bool bEnable)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->EnableUpdateCallback(bEnable);
- }
- }
- void TwoColumnPropertyControl::SetUpdateCallback(ReflectedPropertyControl::UpdateVarCallback callback)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->SetUpdateCallback(callback);
- }
- }
- void TwoColumnPropertyControl::SetGrayed(bool grayed)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->SetGrayed(grayed);
- }
- }
- void TwoColumnPropertyControl::SetSavedStateKey(const QString &key)
- {
- for (int i = 0; i < m_controlList.count(); ++i)
- {
- m_controlList[i]->GetControl()->SetSavedStateKey(AZ::Crc32((key + QString::number(i)).toUtf8().data()));
- }
- }
- void TwoColumnPropertyControl::ExpandAllChildren(ReflectedPropertyItem* item, bool recursive)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->ExpandAllChildren(item, recursive);
- }
- }
- void TwoColumnPropertyControl::ExpandAllChildren(bool recursive)
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->ExpandAllChildren(ctrl->GetControl()->GetRootItem(), recursive);
- }
- }
- void TwoColumnPropertyControl::ReloadItems()
- {
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->ReloadValues();
- }
- }
- void TwoColumnPropertyControl::OnCopyAll()
- {
- CClipboard clipboard(nullptr);
- auto rootNode = XmlHelpers::CreateXmlNode("PropertyCtrl");
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->OnCopyAll(rootNode);
- }
- clipboard.Put(rootNode);
- }
- void TwoColumnPropertyControl::OnPaste()
- {
- CClipboard clipboard(nullptr);
- CUndo undo("Paste Properties");
- XmlNodeRef rootNode = clipboard.Get();
- for (auto ctrl : m_controlList)
- {
- ctrl->GetControl()->SetValuesFromNode(rootNode);
- }
- }
- PropertyCard::PropertyCard([[maybe_unused]] QWidget* parent /*= nullptr*/)
- {
- // create header bar
- m_header = new AzToolsFramework::ComponentEditorHeader(this);
- m_header->SetExpandable(true);
- // create property editor
- m_propertyEditor = new ReflectedPropertyControl(this);
- m_propertyEditor->Setup(false);
- m_propertyEditor->GetEditor()->SetHideRootProperties(true);
- m_propertyEditor->setProperty("ComponentDisabl", true); // used by stylesheet
- QVBoxLayout *mainLayout = new QVBoxLayout(this);
- mainLayout->setMargin(0);
- mainLayout->setContentsMargins(0, 0, 0, 0);
- mainLayout->addWidget(m_header);
- mainLayout->addWidget(m_propertyEditor);
- setLayout(mainLayout);
- connect(m_header, &AzToolsFramework::ComponentEditorHeader::OnExpanderChanged, this, &PropertyCard::OnExpanderChanged);
- connect(m_propertyEditor->GetEditor(), &AzToolsFramework::ReflectedPropertyEditor::OnExpansionContractionDone, this, &PropertyCard::OnExpansionContractionDone);
- SetExpanded(true);
- }
- void PropertyCard::AddVarBlock(CVarBlock *varBlock)
- {
- if (varBlock->GetNumVariables() > 0)
- {
- m_header->SetTitle(varBlock->GetVariable(0)->GetName());
- m_propertyEditor->AddVarBlock(varBlock);
- }
- }
- ReflectedPropertyControl* PropertyCard::GetControl()
- {
- return m_propertyEditor;
- }
- void PropertyCard::SetExpanded(bool expanded)
- {
- m_header->SetExpanded(expanded);
- m_propertyEditor->setVisible(expanded);
- }
- bool PropertyCard::IsExpanded() const
- {
- return m_header->IsExpanded();
- }
- void PropertyCard::OnExpanderChanged(bool expanded)
- {
- SetExpanded(expanded);
- emit OnExpansionContractionDone();
- }
- #include <Controls/ReflectedPropertyControl/moc_ReflectedPropertyCtrl.cpp>
|