merge from development

Signed-off-by: greerdv <greerdv@amazon.com>
This commit is contained in:
greerdv
2022-02-10 17:23:25 +00:00
429 changed files with 11108 additions and 5963 deletions
@@ -73,7 +73,7 @@ namespace AzToolsFramework
settings.m_keepDefaults = true;
}
if ((flags & StoreFlags::StoreLinkIds) != StoreFlags::None)
if ((flags & StoreFlags::StripLinkIds) != StoreFlags::StripLinkIds)
{
settings.m_metadata.Create<LinkIdMetadata>();
}
@@ -52,7 +52,7 @@ namespace AzToolsFramework
//! We do not save linkIds to file. However when loading a level we want to temporarily save
//! linkIds to instance dom so any nested prefabs will have linkIds correctly set.
StoreLinkIds = 1 << 1
StripLinkIds = 1 << 1
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(StoreFlags);
@@ -292,7 +292,7 @@ namespace AzToolsFramework::Prefab
return false;
}
const InstanceOptionalConstReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
if (!instance.has_value())
{
return false;
@@ -308,7 +308,7 @@ namespace AzToolsFramework::Prefab
return false;
}
InstanceOptionalConstReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
while (instance.has_value())
{
if (instance->get().GetAbsoluteInstanceAliasPath() == m_rootAliasFocusPath)
@@ -331,7 +331,7 @@ namespace AzToolsFramework
}
PrefabDom storedPrefabDom(&loadedTemplateDom->get().GetAllocator());
if (!PrefabDomUtils::StoreInstanceInPrefabDom(loadedPrefabInstance, storedPrefabDom, PrefabDomUtils::StoreFlags::StoreLinkIds))
if (!PrefabDomUtils::StoreInstanceInPrefabDom(loadedPrefabInstance, storedPrefabDom))
{
return false;
}
@@ -359,7 +359,7 @@ namespace AzToolsFramework
PrefabDom storedPrefabDom(&savingTemplateDom->get().GetAllocator());
if (!PrefabDomUtils::StoreInstanceInPrefabDom(savingPrefabInstance, storedPrefabDom,
PrefabDomUtils::StoreFlags::StripDefaultValues))
PrefabDomUtils::StoreFlags::StripDefaultValues | PrefabDomUtils::StoreFlags::StripLinkIds))
{
return false;
}
@@ -197,7 +197,7 @@ namespace AzToolsFramework
// Update the template of the instance since the entities are modified since the template creation.
Prefab::PrefabDom serializedInstance;
if (Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(instanceToCreate->get(), serializedInstance))
if (m_instanceToTemplateInterface->GenerateDomForInstance(serializedInstance, instanceToCreate->get()))
{
m_prefabSystemComponentInterface->UpdatePrefabTemplate(instanceToCreate->get().GetTemplateId(), serializedInstance);
}
@@ -163,17 +163,21 @@ namespace AzToolsFramework
void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, InstanceOptionalConstReference instanceToExclude)
{
auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId);
if (templateIdToLinkIdsIterator != m_templateToLinkIdsMap.end())
TemplateReference findTemplateResult = FindTemplate(templateId);
if (findTemplateResult.has_value())
{
// We need to initialize a queue here because once all linked instances of a template are updated,
// we will find all the linkIds corresponding to the updated template and add them to this queue again.
AZStd::queue<LinkIds> linkIdsToUpdateQueue;
linkIdsToUpdateQueue.push(LinkIds(templateIdToLinkIdsIterator->second.begin(),
templateIdToLinkIdsIterator->second.end()));
UpdateLinkedInstances(linkIdsToUpdateQueue);
auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId);
if (templateIdToLinkIdsIterator != m_templateToLinkIdsMap.end())
{
// We need to initialize a queue here because once all linked instances of a template are updated,
// we will find all the linkIds corresponding to the updated template and add them to this queue again.
AZStd::queue<LinkIds> linkIdsToUpdateQueue;
linkIdsToUpdateQueue.push(
LinkIds(templateIdToLinkIdsIterator->second.begin(), templateIdToLinkIdsIterator->second.end()));
UpdateLinkedInstances(linkIdsToUpdateQueue);
}
UpdatePrefabInstances(templateId, instanceToExclude);
}
UpdatePrefabInstances(templateId, instanceToExclude);
}
void PrefabSystemComponent::UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom)
@@ -256,7 +256,7 @@ namespace AzToolsFramework
return;
}
PrefabDomReference sourceDom = sourceTemplate->get().GetPrefabDom();
PrefabDom& sourceDom = sourceTemplate->get().GetPrefabDom();
//use instance pointer to reach position
PrefabDomValueReference instanceDomRef = link->get().GetLinkedInstanceDom();
@@ -274,16 +274,14 @@ namespace AzToolsFramework
(result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::PartialSkip),
"Some of the patches are not successfully applied.");
//remove the link id placed into the instance
auto linkIdIter = instanceDom.FindMember(PrefabDomUtils::LinkIdName);
if (linkIdIter != instanceDom.MemberEnd())
{
instanceDom.RemoveMember(PrefabDomUtils::LinkIdName);
}
// Remove the link ids if present in the doms. We don't want any overrides to be created on top of linkIds because
// linkIds are not persistent and will be created dynamically when prefabs are loaded into the editor.
instanceDom.RemoveMember(PrefabDomUtils::LinkIdName);
sourceDom.RemoveMember(PrefabDomUtils::LinkIdName);
//we use this to diff our copy against the vanilla template (source template)
PrefabDom patchLink;
m_instanceToTemplateInterface->GeneratePatch(patchLink, sourceDom->get(), instanceDom);
m_instanceToTemplateInterface->GeneratePatch(patchLink, sourceDom, instanceDom);
// Create a copy of patchLink by providing the allocator of m_linkDomNext so that the patch doesn't become invalid when
// the patch goes out of scope in this function.
@@ -235,10 +235,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
if (!asset->GetId().IsValid())
{
AZ_Error(
"Prefab", false,
"Invalid asset found referenced in scene while entering game mode. The asset was stored in an instance of %s.",
classData->m_name);
// Invalid asset found referenced in scene while entering game mode.
return false;
}
@@ -29,7 +29,7 @@
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/ToolsComponents/TransformComponentBus.h>
#include <AzToolsFramework/ToolsComponents/TransformComponentSerializer.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
@@ -963,18 +963,22 @@ namespace AzToolsFramework
if (!m_parentEntityId.IsValid())
{
// If Prefabs are enabled, reroute the invalid id to the level root
// If Prefabs are enabled, reroute the invalid id to the focused prefab container entity id
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabSystemEnabled)
{
auto prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
auto prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get();
if (prefabPublicInterface)
if (prefabFocusPublicInterface)
{
m_parentEntityId = prefabPublicInterface->GetLevelInstanceContainerEntityId();
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(
editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
m_parentEntityId = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
refreshLevel = AZ::Edit::PropertyRefreshLevels::ValuesOnly;
}
}
@@ -7,6 +7,7 @@
*/
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
#include <AzCore/Interface/Interface.h>
@@ -117,9 +118,16 @@ namespace AzToolsFramework
{
}
bool EditorEntityUiHandlerBase::OnEntityDoubleClick([[maybe_unused]] AZ::EntityId entityId) const
bool EditorEntityUiHandlerBase::OnOutlinerItemDoubleClick([[maybe_unused]] const QModelIndex& index) const
{
return false;
}
AZ::EntityId EditorEntityUiHandlerBase::GetEntityIdFromIndex(const QModelIndex& index)
{
QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName);
return AZ::EntityId(firstColumnIndex.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
}
} // namespace AzToolsFramework
@@ -21,7 +21,6 @@ class QTreeView;
namespace AzToolsFramework
{
//! Defines a handler that can customize entity UI appearance and behavior in the Entity Outliner.
//! This class is meant to be abstract, entities do not have a handler by default.
class EditorEntityUiHandlerBase
{
protected:
@@ -33,7 +32,7 @@ namespace AzToolsFramework
public:
EditorEntityUiHandlerId GetHandlerId();
// # Entity Outliner
// # Entity Outliner Item
//! Returns the item info string that is appended to the item name in the Outliner.
virtual QString GenerateItemInfoString(AZ::EntityId entityId) const;
@@ -41,10 +40,12 @@ namespace AzToolsFramework
virtual QString GenerateItemTooltip(AZ::EntityId entityId) const;
//! Returns the item icon pixmap to display in the Outliner.
virtual QIcon GenerateItemIcon(AZ::EntityId entityId) const;
//! Returns whether the element's lock and visibility state should be accessible in the Outliner
virtual bool CanToggleLockVisibility(AZ::EntityId entityId) const;
//! Returns whether the element's name should be editable
virtual bool CanRename(AZ::EntityId entityId) const;
//! Returns whether the element's lock and visibility state should be accessible in the Outliner
virtual bool CanToggleLockVisibility(AZ::EntityId entityId) const;
// Qt-specific painting functions
//! Paints the background of the item in the Outliner.
virtual void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
@@ -54,24 +55,27 @@ namespace AzToolsFramework
//! Paints the background of the descendant branches of the item in the Outliner.
virtual void PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
const QModelIndex& index, const QModelIndex& descendantIndex) const;
//! Paints visual elements on the foreground of the item in the Outliner.
virtual void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
//! Paints visual elements on the foreground of the descendants of the item in the Outliner.
virtual void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const;
// Outliner-specific interactions
//! Triggered when the entity is clicked in the Outliner.
//! @return True if the click has been handled and should not be propagated, false otherwise.
virtual bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const;
//! Triggered when the entity is double-clicked in the Outliner.
//! @return True if the double-click has been handled and should not be propagated, false otherwise.
virtual bool OnOutlinerItemDoubleClick(const QModelIndex& index) const;
//! Triggered when an entity's children are expanded in the Outliner.
virtual void OnOutlinerItemExpand(const QModelIndex& index) const;
//! Triggered when an entity's children are collapsed in the Outliner.
virtual void OnOutlinerItemCollapse(const QModelIndex& index) const;
//! Triggered when the entity is double clicked in the Outliner or in the Viewport.
//! @return True if the double click has been handled and should not be propagated, false otherwise.
virtual bool OnEntityDoubleClick(AZ::EntityId entityId) const;
protected:
static AZ::EntityId GetEntityIdFromIndex(const QModelIndex& index);
private:
EditorEntityUiHandlerId m_handlerId = 0;
@@ -945,7 +945,7 @@ namespace AzToolsFramework
{
if (AZ::EntityId entityId = GetEntityIdFromIndex(index); auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId))
{
entityUiHandler->OnEntityDoubleClick(entityId);
entityUiHandler->OnOutlinerItemDoubleClick(index);
}
}
@@ -33,7 +33,7 @@ namespace AzToolsFramework
}
}
QIcon LevelRootUiHandler::GenerateItemIcon(AZ::EntityId /*entityId*/) const
QIcon LevelRootUiHandler::GenerateItemIcon([[maybe_unused]] AZ::EntityId entityId) const
{
return QIcon(m_levelRootIconPath);
}
@@ -62,17 +62,18 @@ namespace AzToolsFramework
return infoString;
}
bool LevelRootUiHandler::CanToggleLockVisibility(AZ::EntityId /*entityId*/) const
bool LevelRootUiHandler::CanToggleLockVisibility([[maybe_unused]] AZ::EntityId entityId) const
{
return false;
}
bool LevelRootUiHandler::CanRename(AZ::EntityId /*entityId*/) const
bool LevelRootUiHandler::CanRename([[maybe_unused]] AZ::EntityId entityId) const
{
return false;
}
void LevelRootUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
void LevelRootUiHandler::PaintItemBackground(
QPainter* painter, const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& index) const
{
if (!painter)
{
@@ -94,8 +95,10 @@ namespace AzToolsFramework
painter->restore();
}
bool LevelRootUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const
bool LevelRootUiHandler::OnOutlinerItemDoubleClick(const QModelIndex& index) const
{
AZ::EntityId entityId = GetEntityIdFromIndex(index);
if (auto prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get();
!prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
@@ -33,7 +33,7 @@ namespace AzToolsFramework
bool CanToggleLockVisibility(AZ::EntityId entityId) const override;
bool CanRename(AZ::EntityId entityId) const override;
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
bool OnOutlinerItemDoubleClick(const QModelIndex& index) const override;
private:
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
@@ -99,7 +99,7 @@ namespace AzToolsFramework
return;
}
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
AZ::EntityId entityId = GetEntityIdFromIndex(index);
const bool isFirstColumn = index.column() == EntityOutlinerListModel::ColumnName;
const bool isLastColumn = index.column() == EntityOutlinerListModel::ColumnLockToggle;
QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName);
@@ -183,7 +183,7 @@ namespace AzToolsFramework
return;
}
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
AZ::EntityId entityId = GetEntityIdFromIndex(index);
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2) - 1;
@@ -283,7 +283,7 @@ namespace AzToolsFramework
void PrefabUiHandler::PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& index) const
{
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
AZ::EntityId entityId = GetEntityIdFromIndex(index);
const QPoint offset = QPoint(-18, 3);
QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName);
const int iconSize = 16;
@@ -385,7 +385,7 @@ namespace AzToolsFramework
bool PrefabUiHandler::OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
AZ::EntityId entityId = GetEntityIdFromIndex(index);
const QPoint offset = QPoint(-18, 3);
if (m_prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(entityId))
@@ -411,7 +411,7 @@ namespace AzToolsFramework
void PrefabUiHandler::OnOutlinerItemCollapse(const QModelIndex& index) const
{
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
AZ::EntityId entityId = GetEntityIdFromIndex(index);
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
@@ -420,8 +420,10 @@ namespace AzToolsFramework
}
}
bool PrefabUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const
bool PrefabUiHandler::OnOutlinerItemDoubleClick(const QModelIndex& index) const
{
AZ::EntityId entityId = GetEntityIdFromIndex(index);
if (!m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
// Focus on this prefab
@@ -43,8 +43,8 @@ namespace AzToolsFramework
const QModelIndex& index,
const QModelIndex& descendantIndex) const override;
bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
bool OnOutlinerItemDoubleClick(const QModelIndex& index) const override;
void OnOutlinerItemCollapse(const QModelIndex& index) const override;
bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
protected:
Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
@@ -102,7 +102,7 @@ namespace AzToolsFramework
m_editButton->setAutoRaise(true);
m_editButton->setIcon(QIcon(":/stylesheet/img/UI20/open-in-internal-app.svg"));
m_editButton->setToolTip("Edit asset");
m_editButton->setVisible(false);
SetEditButtonVisible(false);
connect(m_editButton, &QToolButton::clicked, this, &PropertyAssetCtrl::OnEditButtonClicked);
@@ -961,12 +961,16 @@ namespace AzToolsFramework
AzFramework::StringFunc::Path::GetFileName(assetPath.c_str(), m_defaultAssetHint);
}
m_browseEdit->setPlaceholderText((m_defaultAssetHint + m_DefaultSuffix).c_str());
UpdateEditButton();
}
void PropertyAssetCtrl::UpdateAssetDisplay()
{
UpdateThumbnail();
UpdateEditButton();
if (m_currentAssetType == AZ::Data::s_invalidAssetType)
{
return;
@@ -1109,7 +1113,9 @@ namespace AzToolsFramework
void PropertyAssetCtrl::SetEditButtonVisible(bool visible)
{
m_editButton->setVisible(visible);
m_showEditButton = visible;
m_editButton->setVisible(m_showEditButton);
UpdateEditButton();
}
void PropertyAssetCtrl::SetEditButtonIcon(const QIcon& icon)
@@ -1205,6 +1211,15 @@ namespace AzToolsFramework
m_thumbnail->ClearThumbnail();
}
void PropertyAssetCtrl::UpdateEditButton()
{
// if Edit button is in use (shown), enable/disable it depending on the current asset id.
if (m_showEditButton && m_disableEditButtonWhenNoAssetSelected)
{
m_editButton->setEnabled(GetCurrentAssetID().IsValid());
}
}
void PropertyAssetCtrl::SetClearButtonEnabled(bool enable)
{
m_browseEdit->setClearButtonEnabled(enable);
@@ -1236,6 +1251,17 @@ namespace AzToolsFramework
return m_hideProductFilesInAssetPicker;
}
void PropertyAssetCtrl::SetDisableEditButtonWhenNoAssetSelected(bool disableEditButtonWhenNoAssetSelected)
{
m_disableEditButtonWhenNoAssetSelected = disableEditButtonWhenNoAssetSelected;
UpdateEditButton();
}
bool PropertyAssetCtrl::GetDisableEditButtonWhenNoAssetSelected() const
{
return m_disableEditButtonWhenNoAssetSelected;
}
void PropertyAssetCtrl::SetShowThumbnail(bool enable)
{
m_showThumbnail = enable;
@@ -1349,6 +1375,12 @@ namespace AzToolsFramework
GUI->SetEditButtonTooltip(tr(buttonTooltip.c_str()));
}
}
else if (attrib == AZ_CRC_CE("DisableEditButtonWhenNoAssetSelected"))
{
bool disableEditButtonWhenNoAssetSelected = false;
attrValue->Read<bool>(disableEditButtonWhenNoAssetSelected);
GUI->SetDisableEditButtonWhenNoAssetSelected(disableEditButtonWhenNoAssetSelected);
}
else if (attrib == AZ::Edit::Attributes::DefaultAsset)
{
AZ::Data::AssetId assetId;
@@ -1597,6 +1629,12 @@ namespace AzToolsFramework
GUI->SetEditButtonTooltip(tr(buttonTooltip.c_str()));
}
}
else if (attrib == AZ_CRC_CE("DisableEditButtonWhenNoAssetSelected"))
{
bool disableEditButtonWhenNoAssetSelected = false;
attrValue->Read<bool>(disableEditButtonWhenNoAssetSelected);
GUI->SetDisableEditButtonWhenNoAssetSelected(disableEditButtonWhenNoAssetSelected);
}
}
void SimpleAssetPropertyHandlerDefault::WriteGUIValuesIntoProperty(size_t index, PropertyAssetCtrl* GUI, property_t& instance, InstanceDataNode* node)
@@ -159,6 +159,10 @@ namespace AzToolsFramework
//! By default the asset picker shows both on an AZ::Asset<> property. You can hide product assets with this flag.
bool m_hideProductFilesInAssetPicker = false;
//! True to disable the edit button when there is no asset currently selected.
bool m_disableEditButtonWhenNoAssetSelected = false;
bool m_showEditButton = false;
bool m_showThumbnail = false;
bool m_showThumbnailDropDownButton = false;
EditCallbackType* m_thumbnailCallback = nullptr;
@@ -220,6 +224,9 @@ namespace AzToolsFramework
void SetHideProductFilesInAssetPicker(bool hide);
bool GetHideProductFilesInAssetPicker() const;
void SetDisableEditButtonWhenNoAssetSelected(bool disableEditButtonWhenNoAssetSelected);
bool GetDisableEditButtonWhenNoAssetSelected() const;
// Enable and configure a thumbnail widget that displays an asset preview and dropdown arrow for a dropdown menu
void SetShowThumbnail(bool enable);
bool GetShowThumbnail() const;
@@ -250,6 +257,7 @@ namespace AzToolsFramework
private:
void UpdateThumbnail();
void UpdateEditButton();
};
class AssetPropertyHandlerDefault
@@ -154,6 +154,16 @@ namespace AzToolsFramework
(void)debugName;
}
// provides an option to specify reading parent element attributes.
// This allows parent elements to override attributes of their children if needed.
virtual void ConsumeParentAttribute(WidgetType* widget, AZ::u32 attrib, PropertyAttributeReader* attrValue, const char* debugName)
{
(void)widget;
(void)attrib;
(void)attrValue;
(void)debugName;
}
// override GetFirstInTabOrder, GetLastInTabOrder in your base class to define which widget gets focus first when pressing tab,
// and also what widget is last.
// for example, if your widget is a compound widget and contains, say, 5 buttons
@@ -40,7 +40,14 @@ namespace AzToolsFramework
}
void* classInstance = parent->FirstInstance(); // pointer to the owner class so we can read member variables and functions
auto consumeAttributes = [&](const auto& attributes, const char* name)
void* parentClassInstance = nullptr;
if (InstanceDataNode* parentInstanceDataNode = parent->GetParent())
{
parentClassInstance = parentInstanceDataNode->FirstInstance();
}
auto consumeAttributes = [this, classInstance, wid](const auto& attributes, const char* name)
{
for (size_t i = 0; i < attributes.size(); ++i)
{
@@ -50,25 +57,43 @@ namespace AzToolsFramework
}
};
auto consumeParentAttributes = [this, parentClassInstance, wid](const auto& attributes, const char* name)
{
if (parentClassInstance)
{
for (size_t i = 0; i < attributes.size(); ++i)
{
const auto& attrPair = attributes[i];
PropertyAttributeReader reader(parentClassInstance, &*attrPair.second);
ConsumeParentAttribute(wid, attrPair.first, &reader, name);
}
}
};
const AZ::SerializeContext::ClassElement* element = dataNode->GetElementMetadata();
if (element)
{
consumeAttributes(element->m_attributes, element->m_name);
const AZ::Edit::ElementData* elementEdit = dataNode->GetElementEditMetadata();
if (elementEdit)
if (const AZ::Edit::ElementData* elementEdit = dataNode->GetElementEditMetadata();
elementEdit != nullptr)
{
consumeAttributes(elementEdit->m_attributes, elementEdit->m_name);
}
}
if (dataNode->GetClassMetadata())
{
const AZ::Edit::ClassData* classEditData = dataNode->GetClassMetadata()->m_editData;
if (classEditData)
const AZ::SerializeContext::ClassElement* parentElement = parent != dataNode ?
dataNode->GetElementMetadata() :
nullptr;
if (parentElement != nullptr)
{
for (auto it = classEditData->m_elements.begin(); it != classEditData->m_elements.end(); ++it)
// Reuse the current instance element name for the debug name
consumeParentAttributes(parentElement->m_attributes, element->m_name);
if (const AZ::Edit::ElementData* elementEdit = parent->GetElementEditMetadata();
elementEdit != nullptr)
{
consumeAttributes(it->m_attributes, it->m_name);
consumeParentAttributes(elementEdit->m_attributes, elementEdit->m_name);
}
}
}
@@ -66,6 +66,12 @@ namespace AzToolsFramework
class GenericEnumPropertyComboBoxHandler
: public GenericComboBoxHandler<ValueType>
{
virtual void ConsumeParentAttribute(GenericComboBoxCtrlBase* GUI, AZ::u32 attrib, PropertyAttributeReader* attrValue, const char* debugName) override
{
// Simply re-route to ConsumeAttribute since no special logic is needed.
ConsumeAttribute(GUI, attrib, attrValue, debugName);
}
virtual void ConsumeAttribute(GenericComboBoxCtrlBase* GUI, AZ::u32 attrib, PropertyAttributeReader* attrValue, const char* debugName) override
{
(void)debugName;
@@ -15,7 +15,7 @@ namespace UnitTest
// When no containers are in the way, the function will just return the entityId of the entity that was clicked.
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -29,7 +29,7 @@ namespace UnitTest
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); // Containers are closed by default
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -47,7 +47,7 @@ namespace UnitTest
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -65,7 +65,7 @@ namespace UnitTest
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -85,7 +85,7 @@ namespace UnitTest
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -8,6 +8,7 @@
#include <Tests/FocusMode/EditorFocusModeFixture.h>
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <Tests/BoundsTestComponent.h>
@@ -93,10 +94,13 @@ namespace UnitTest
entity->CreateComponent<UnitTest::BoundsTestComponent>();
entity->Activate();
// Move the CarEntity so it's out of the way.
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, WorldCarEntityPosition);
// Move the City so that it is in view
AZ::TransformBus::Event(m_entityMap[CityEntityName], &AZ::TransformBus::Events::SetWorldTranslation, s_worldCityEntityPosition);
// Setup the camera so the Car entity is in view.
// Move the CarEntity so that it's not overlapping with the rest
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, s_worldCarEntityPosition);
// Setup the camera so the entities is in view.
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
@@ -113,4 +117,5 @@ namespace UnitTest
return entity->GetId();
}
} // namespace UnitTest
@@ -8,7 +8,6 @@
#pragma once
#include <AzCore/Component/TransformBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
@@ -38,9 +37,6 @@ namespace UnitTest
AzToolsFramework::EntityIdList GetSelectedEntities();
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzFramework::CameraState m_cameraState;
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
inline static const char* CityEntityName = "City";
inline static const char* StreetEntityName = "Street";
@@ -49,7 +45,11 @@ namespace UnitTest
inline static const char* Passenger1EntityName = "Passenger1";
inline static const char* Passenger2EntityName = "Passenger2";
inline static AZ::Vector3 WorldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
AzFramework::CameraState m_cameraState;
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
inline static AZ::Vector3 s_worldCityEntityPosition = AZ::Vector3(5.0f, 10.0f, 0.0f);
inline static AZ::Vector3 s_worldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
};
} // namespace UnitTest
@@ -45,5 +45,20 @@ namespace UnitTest
// Click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
}
void BoxSelectOnViewport()
{
// Calculate the position in screen space of where to begin and end the box select action
const auto beginningPositionWorldBoxSelect = AzFramework::WorldToScreen(AZ::Vector3(-10.0f, 15.0f, 5.0f), m_cameraState);
const auto endingPositionWorldBoxSelect = AzFramework::WorldToScreen(AZ::Vector3(10.0f, 15.0f, -5.0f), m_cameraState);
// Perform a box select in the viewport
m_actionDispatcher->SetStickySelect(true)
->CameraState(m_cameraState)
->MousePosition(beginningPositionWorldBoxSelect)
->MouseLButtonDown()
->MousePosition(endingPositionWorldBoxSelect)
->MouseLButtonUp();
}
};
} // namespace UnitTest
@@ -13,7 +13,7 @@ namespace UnitTest
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnLevel)
{
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -27,7 +27,7 @@ namespace UnitTest
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -41,7 +41,7 @@ namespace UnitTest
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -55,7 +55,7 @@ namespace UnitTest
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -68,10 +68,71 @@ namespace UnitTest
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionBoxSelectWithFocusOnLevel)
{
// Do a box select that includes all entities in the fixture
BoxSelectOnViewport();
// Entities are selected
using ::testing::UnorderedElementsAre;
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_THAT(selectedEntitiesAfter,
UnorderedElementsAre(
m_entityMap[CityEntityName],
m_entityMap[StreetEntityName],
m_entityMap[CarEntityName],
m_entityMap[Passenger1EntityName],
m_entityMap[SportsCarEntityName],
m_entityMap[Passenger2EntityName]
)
);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionBoxSelectWithFocusOnChild)
{
// Set the focus on the Passenger1 Entity (child of the entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
// Do a box select that includes all entities in the fixture
BoxSelectOnViewport();
// Entities are selected
using ::testing::UnorderedElementsAre;
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_THAT(selectedEntitiesAfter,
UnorderedElementsAre(
m_entityMap[StreetEntityName],
m_entityMap[CarEntityName],
m_entityMap[Passenger1EntityName],
m_entityMap[SportsCarEntityName],
m_entityMap[Passenger2EntityName]
)
);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionBoxSelectWithFocusOnLeaf)
{
// Set the focus on the Passenger1 Entity (child of the entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
// Do a box select that includes all entities in the fixture
BoxSelectOnViewport();
// Entities are selected
using ::testing::UnorderedElementsAre;
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_THAT(selectedEntitiesAfter,
UnorderedElementsAre(
m_entityMap[Passenger1EntityName]
)
);
}
} // namespace UnitTest