Merge branch 'development' of https://github.com/o3de/o3de into daimini/settings-registry-origin-tracking

This commit is contained in:
Danilo Aimini
2021-08-16 09:43:10 -07:00
26 changed files with 1643 additions and 315 deletions
-2
View File
@@ -10,8 +10,6 @@
#include "StringHelpers.h"
#include "Util.h"
#include <AzCore/std/string/string.h>
int StringHelpers::CompareIgnoreCase(const AZStd::string& str0, const AZStd::string& str1)
{
const size_t minLength = Util::getMin(str0.length(), str1.length());
+1 -1
View File
@@ -12,7 +12,7 @@
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <vector>
namespace StringHelpers
{
@@ -1882,7 +1882,10 @@ namespace AzQtComponents
return;
}
QApplication::setOverrideCursor(m_dragCursor);
if (!QApplication::overrideCursor())
{
QApplication::setOverrideCursor(m_dragCursor);
}
QPoint relativePressPos = pressPos;
@@ -19,6 +19,7 @@
#include <AzCore/Component/Component.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Component/EntityBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/Undo/UndoSystem.h>
@@ -110,6 +111,7 @@ namespace AzToolsFramework
, public EditorInspectorComponentNotificationBus::MultiHandler
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, public AZ::EntitySystemBus::Handler
, public AZ::TickBus::Handler
, private EditorWindowUIRequestBus::Handler
{
Q_OBJECT;
@@ -117,6 +119,23 @@ namespace AzToolsFramework
AZ_CLASS_ALLOCATOR(EntityPropertyEditor, AZ::SystemAllocator, 0)
enum class ReorderState
{
Inactive, // No row widget reordering operation is in progress.
DraggingComponent, // User is dragging a component editor.
DraggingRowWidget, // User is dragging a row widget around.
UsingMenu, // User has the context menu open and may hover over a move up/down operation.
MenuOperationInProgress, // User has selected a move/up down menu item.
WaitForRedraw, // Wait for rebuild of RPE.
HighlightMovedRow // User has moved a row, highlight the new position.
};
enum class DropArea
{
Above,
Below
};
EntityPropertyEditor(QWidget* pParent = NULL, Qt::WindowFlags flags = Qt::WindowFlags(), bool isLevelEntityEditor = false);
virtual ~EntityPropertyEditor();
@@ -151,6 +170,16 @@ namespace AzToolsFramework
bool IsLockedToSpecificEntities() const { return !m_overrideSelectedEntityIds.empty(); }
static bool AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components, const ComponentFilter& filter);
ReorderState GetReorderState() const;
ComponentEditor* GetEditorForCurrentReorderRowWidget() const;
PropertyRowWidget* GetReorderRowWidget() const;
PropertyRowWidget* GetReorderDropTarget() const;
DropArea GetReorderDropArea() const;
QPixmap GetReorderRowWidgetImage() const;
float GetMoveIndicatorAlpha() const;
PropertyRowWidget* GetRowToHighlight();
Q_SIGNALS:
void SelectedEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name);
@@ -211,6 +240,9 @@ namespace AzToolsFramework
void GetSelectedEntities(EntityIdList& selectedEntityIds) override;
void SetNewComponentId(AZ::ComponentId componentId) override;
// TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// EditorWindowRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
@@ -253,6 +285,10 @@ namespace AzToolsFramework
void ContextMenuActionPullFieldData(AZ::Component* parentComponent, InstanceDataNode* fieldNode);
void ContextMenuActionSetDataFlag(InstanceDataNode* node, AZ::DataPatch::Flag flag, bool additive);
void GenerateRowWidgetIndexMapToChildIndex(PropertyRowWidget* parent, int destIndex);
void ContextMenuActionMoveItemUp(ComponentEditor* componentEditor, PropertyRowWidget* rowWidget);
void ContextMenuActionMoveItemDown(ComponentEditor* componentEditor, PropertyRowWidget* rowWidget);
/// Given an InstanceDataNode, calculate a DataPatch address relative to the entity.
/// @return true if successful.
bool GetEntityDataPatchAddress(const InstanceDataNode* componentFieldNode, AZ::DataPatch::AddressType& dataPatchAddressOut, AZ::EntityId* entityIdOut = nullptr) const;
@@ -341,8 +377,6 @@ namespace AzToolsFramework
QAction* m_actionToMoveComponentsBottom = nullptr;
QAction* m_resetToSliceAction = nullptr;
bool m_isShowingContextMenu = false;
void CreateActions();
void UpdateActions();
@@ -390,6 +424,10 @@ namespace AzToolsFramework
void ResetToSlice();
bool DoesOwnFocus() const;
AZ::u32 GetHeightOfRowAndVisibleChildren(const PropertyRowWidget* row) const;
QRect GetWidgetAndVisibleChildrenGlobalRect(const PropertyRowWidget* widget) const;
PropertyRowWidget* GetRowWidgetAtSameLevelAfter(PropertyRowWidget* widget) const;
PropertyRowWidget* GetRowWidgetAtSameLevelBefore(PropertyRowWidget* widget) const;
QRect GetWidgetGlobalRect(const QWidget* widget) const;
bool DoesIntersectWidget(const QRect& globalRect, const QWidget* widget) const;
bool DoesIntersectSelectedComponentEditor(const QRect& globalRect) const;
@@ -445,6 +483,8 @@ namespace AzToolsFramework
bool HandleSelectionEvents(QObject* object, QEvent* event);
bool m_selectionEventAccepted;
bool HandleMenuEvent(QObject* object, QEvent* event);
// drag and drop events
QRect GetInflatedRectFromPoint(const QPoint& point, int radius) const;
bool GetComponentsAtDropEventPosition(QDropEvent* event, AZ::Entity::ComponentArrayType& targetComponents);
@@ -458,8 +498,12 @@ namespace AzToolsFramework
ComponentEditor* GetReorderDropTarget(const QRect& globalRect) const;
bool ResetDrag(QMouseEvent* event);
bool FindAllowedRowWidgetReorderDropTarget(const QPoint& globalPos);
bool UpdateRowWidgetDrag(const QPoint& localPos, Qt::MouseButtons mouseButtons, const QMimeData* mimeData);
PropertyRowWidget* FindPropertyRowWidgetAt(QPoint globalPos);
bool UpdateDrag(const QPoint& localPos, Qt::MouseButtons mouseButtons, const QMimeData* mimeData);
bool StartDrag(QMouseEvent* event);
void EndRowWidgetReorder();
bool HandleDrop(QDropEvent* event);
bool HandleDropForComponentTypes(QDropEvent* event);
bool HandleDropForComponentAssets(QDropEvent* event);
@@ -468,6 +512,8 @@ namespace AzToolsFramework
bool CanDropForComponentTypes(const QMimeData* mimeData) const;
bool CanDropForComponentAssets(const QMimeData* mimeData) const;
bool CanDropForAssetBrowserEntries(const QMimeData* mimeData) const;
void SetRowWidgetHighlighted(PropertyRowWidget* rowWidget);
AZStd::vector<AZ::s32> ExtractComponentEditorIndicesFromMimeData(const QMimeData* mimeData) const;
ComponentEditorVector GetComponentEditorsFromIndices(const AZStd::vector<AZ::s32>& indices) const;
ComponentEditor* GetComponentEditorsFromIndex(const AZ::s32 index) const;
@@ -559,6 +605,8 @@ namespace AzToolsFramework
QIcon m_emptyIcon;
QIcon m_clearIcon;
QIcon m_dragIcon;
QCursor m_dragCursor;
QStandardItem* m_comboItems[StatusItems];
EntityIdSet m_overrideSelectedEntityIds;
@@ -566,6 +614,19 @@ namespace AzToolsFramework
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
bool m_prefabsAreEnabled = false;
// Reordering row widgets within the RPE.
static constexpr float MoveFadeSeconds = 0.5f;
ReorderState m_currentReorderState = ReorderState::Inactive;
ComponentEditor* m_reorderRowWidgetEditor = nullptr;
InstanceDataNode* m_nodeToMove = nullptr;
PropertyRowWidget* m_reorderRowWidget = nullptr;
PropertyRowWidget* m_reorderDropTarget = nullptr;
DropArea m_reorderDropArea = DropArea::Above;
QPixmap m_reorderRowImage;
float m_moveFadeSecondsRemaining;
AZStd::vector<int> m_indexMapOfMovedRow;
// When m_initiatingPropertyChangeNotification is set to true, it means this EntityPropertyEditor is
// broadcasting a change to all listeners about a property change for a given entity. This is needed
// so that we don't update the values twice for this inspector
@@ -573,6 +634,9 @@ namespace AzToolsFramework
void ConnectToEntityBuses(const AZ::EntityId& entityId);
void DisconnectFromEntityBuses(const AZ::EntityId& entityId);
void BeginMoveRowWidgetFade();
void HighlightMovedRowWidget();
//! Stores a component id to be focused on next time the UI updates.
AZStd::optional<AZ::ComponentId> m_newComponentId;
@@ -594,6 +658,8 @@ namespace AzToolsFramework
bool SelectedEntitiesAreFromSameSourceSliceEntity() const;
void DragStopped();
AZ::Entity* GetSelectedEntityById(AZ::EntityId& entityId) const;
};
@@ -368,10 +368,15 @@ namespace AzToolsFramework
delete m_containerAddButton;
}
this->unsetCursor();
if ((m_parentRow) && (m_parentRow->IsContainerEditable()))
{
if (!m_elementRemoveButton)
{
QIcon icon = QIcon(QStringLiteral(":/Cursors/Grab_release.svg"));
this->setCursor(QCursor(icon.pixmap(16), 5, 2));
static QIcon s_iconRemove(QStringLiteral(":/stylesheet/img/UI20/delete-16.svg"));
m_elementRemoveButton = new QToolButton(this);
m_elementRemoveButton->setAutoRaise(true);
@@ -570,7 +575,12 @@ namespace AzToolsFramework
AZ_Assert(m_selectionEnabled, "Property is not selectable");
m_isSelected = selected;
m_nameLabel->setProperty("selected", selected);
}
}
bool PropertyRowWidget::GetSelected()
{
return m_isSelected;
}
void PropertyRowWidget::SetSelectionEnabled(bool selectionEnabled)
{
@@ -1395,6 +1405,21 @@ namespace AzToolsFramework
return !m_childrenRows.empty();
}
AZ::u32 PropertyRowWidget::GetChildRowCount() const
{
return static_cast<AZ::u32>(m_childrenRows.size());
}
PropertyRowWidget* PropertyRowWidget::GetChildRowByIndex(AZ::u32 index) const
{
if (index >= m_childrenRows.size())
{
return nullptr;
}
return m_childrenRows[index];
}
bool PropertyRowWidget::ShouldPreValidatePropertyChange() const
{
return (m_changeValidators.size() > 0);
@@ -1722,6 +1747,162 @@ namespace AzToolsFramework
return m_parentRow->CanChildrenBeReordered();
}
int PropertyRowWidget::GetIndexInParent() const
{
if (!GetParentRow())
{
return -1;
}
for (AZ::u32 index = 0; index < GetParentRow()->GetChildRowCount(); index++)
{
if (GetParentRow()->GetChildrenRows()[index] == this)
{
return index;
}
}
return -1;
}
bool PropertyRowWidget::CanMoveUp() const
{
if (!CanBeReordered())
{
return false;
}
return this != m_parentRow->GetChildRowByIndex(0);
}
bool PropertyRowWidget::CanMoveDown() const
{
if (!CanBeReordered())
{
return false;
}
AZ::u32 numChildrenOfParent = m_parentRow->GetChildRowCount();
return this != m_parentRow->GetChildRowByIndex(numChildrenOfParent - 1);
}
int PropertyRowWidget::GetContainingEditorFrameWidth()
{
QWidget* parent = parentWidget();
// Find the first ancestor that can be cast to a QFrame, this will be the RPE.
while (!qobject_cast<QFrame*>(parent))
{
parent = parent->parentWidget();
}
if (!parent)
{
return 0;
}
// The parent of the RPE is the size we want.
parent = parent->parentWidget();
return parent->rect().width();
}
int PropertyRowWidget::GetHeightOfRowAndVisibleChildren()
{
int height = rect().height();
if (!GetChildRowCount() || !IsExpanded())
{
return height;
}
for (auto childRow : GetChildrenRows())
{
height += childRow->GetHeightOfRowAndVisibleChildren();
}
return height;
}
int PropertyRowWidget::DrawDragImageAndVisibleChildrenInto(QPainter& painter, int xpos, int ypos)
{
// Render our image into the given painter.
int ystart = ypos;
render(&painter, QPoint(xpos, ypos));
if (!GetChildRowCount() || !IsExpanded())
{
return rect().height();
}
ypos += rect().height();
// Recursively draw any children.
for (auto childRow : GetChildrenRows())
{
ypos += childRow->DrawDragImageAndVisibleChildrenInto(painter, xpos, ypos);
}
return ypos - ystart;
}
QPixmap PropertyRowWidget::createDragImage(
const QColor backgroundColor, const QColor borderColor, const float alpha, DragImageType imageType)
{
// Make the drag box as wide as the containing editor minus a gap each side for the border.
static constexpr int ParentEditorBorderSize = 2;
int width = GetContainingEditorFrameWidth() - ParentEditorBorderSize * 2;
int height = 0;
if (imageType == DragImageType::IncludeVisibleChildren)
{
height = GetHeightOfRowAndVisibleChildren();
}
else
{
height = rect().height();
}
const auto dpr = devicePixelRatioF();
QPixmap dragImage(width * dpr, height * dpr);
dragImage.setDevicePixelRatio(dpr);
dragImage.fill(Qt::transparent);
QRect imageRect = QRect(0, 0, width, height);
QPainter dragPainter(&dragImage);
dragPainter.setCompositionMode(QPainter::CompositionMode_Source);
dragPainter.fillRect(imageRect, Qt::transparent);
dragPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
dragPainter.setOpacity(alpha);
dragPainter.fillRect(imageRect, backgroundColor);
dragPainter.setOpacity(1.0f);
int marginWidth = (imageRect.width() - rect().width()) / 2 + ParentEditorBorderSize - 1;
if (imageType == DragImageType::IncludeVisibleChildren)
{
DrawDragImageAndVisibleChildrenInto(dragPainter, marginWidth, 0);
}
else
{
render(&dragPainter, QPoint(marginWidth, 0));
}
QPen pen;
pen.setColor(QColor(borderColor));
pen.setWidth(1);
dragPainter.setPen(pen);
dragPainter.drawRect(0, 0, imageRect.width() - 1, imageRect.height() - 1);
dragPainter.end();
return dragImage;
}
}
#include "UI/PropertyEditor/moc_PropertyRowWidget.cpp"
@@ -45,6 +45,13 @@ namespace AzToolsFramework
Q_PROPERTY(bool appendDefaultLabelToName READ GetAppendDefaultLabelToName WRITE AppendDefaultLabelToName)
public:
AZ_CLASS_ALLOCATOR(PropertyRowWidget, AZ::SystemAllocator, 0)
enum class DragImageType
{
SingleRow,
IncludeVisibleChildren
};
PropertyRowWidget(QWidget* pParent);
virtual ~PropertyRowWidget();
@@ -86,6 +93,9 @@ namespace AzToolsFramework
bool GetAppendDefaultLabelToName();
void AppendDefaultLabelToName(bool doAppend);
AZ::u32 GetChildRowCount() const;
PropertyRowWidget* GetChildRowByIndex(AZ::u32 index) const;
AZStd::vector<PropertyRowWidget*>& GetChildrenRows() { return m_childrenRows; }
bool HasChildRows() const;
@@ -124,6 +134,7 @@ namespace AzToolsFramework
void SetSelectionEnabled(bool selectionEnabled);
void SetSelected(bool selected);
bool GetSelected();
bool eventFilter(QObject *watched, QEvent *event) override;
void paintEvent(QPaintEvent*) override;
@@ -152,9 +163,18 @@ namespace AzToolsFramework
bool CanChildrenBeReordered() const;
bool CanBeReordered() const;
int GetIndexInParent() const;
bool CanMoveUp() const;
bool CanMoveDown() const;
int GetContainingEditorFrameWidth();
QPixmap createDragImage(const QColor backgroundColor, const QColor borderColor, const float alpha, DragImageType imageType);
protected:
int CalculateLabelWidth() const;
int GetHeightOfRowAndVisibleChildren();
int DrawDragImageAndVisibleChildrenInto(QPainter& painter, int xpos, int ypos);
bool IsHidden(InstanceDataNode* node) const;
struct ChangeNotification;
@@ -216,6 +236,7 @@ namespace AzToolsFramework
bool m_isMultiSizeContainer = false;
bool m_isFixedSizeOrSmartPtrContainer = false;
bool m_custom = false;
bool m_canChildrenBeReordered = false;
bool m_isSelected = false;
bool m_selectionEnabled = false;
@@ -19,6 +19,7 @@
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QScrollArea>
#include <QtWidgets/QApplication>
#include <QPainter>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QTextFormat::d': class 'QSharedDataPointer<QTextFormatPrivate>' needs to have dll-interface to be used by clients of class 'QTextFormat'
#include <QtWidgets/QInputDialog>
AZ_POP_DISABLE_WARNING
@@ -1343,7 +1344,7 @@ namespace AzToolsFramework
// calculate the index/offset of the instance data node in the container
// (useful for notifying which element in a vector was modified/removed)
static size_t CalculateElementIndexInContainer(
static int CalculateElementIndexInContainer(
InstanceDataNode* node, void* parentInstanceNode,
AZ::SerializeContext::IDataContainer* container, AZStd::vector<void*>& nodeInstancesOut)
{
@@ -1358,7 +1359,7 @@ namespace AzToolsFramework
}
}
size_t elementIndex = 0;
int elementIndex = 0;
void* elementPtr = nodeInstancesOut.empty() ? nullptr : nodeInstancesOut.front();
// find the index of the element we are about to remove
@@ -1429,7 +1430,7 @@ namespace AzToolsFramework
// if the element being modified exists in a container, calculate
// the index to be passed through to PropertyNotify
const auto calculateElementIndex = [](InstanceDataNode* node) -> size_t {
const auto calculateElementIndex = [](InstanceDataNode* node) -> int {
if (InstanceDataNode* parent = node->GetParent())
{
if (AZ::SerializeContext::IDataContainer* container = parent->GetClassMetadata()->m_container)
@@ -1656,6 +1657,221 @@ namespace AzToolsFramework
AzToolsFramework::Refresh_EntireTree);
}
InstanceDataNode* ReflectedPropertyEditor::FindContainerNodeForNode(InstanceDataNode* node) const
{
// Locate the owning container. There may be a level of indirection due to wrappers, such as DynamicSerializableField.
InstanceDataNode* pContainerNode = node->GetParent();
if (!pContainerNode)
{
return nullptr;
}
while (pContainerNode && !pContainerNode->GetClassMetadata()->m_container)
{
pContainerNode = pContainerNode->GetParent();
node = node->GetParent();
}
// Check for pContainerNode again, can happen if a node is deleted during operation.
if (!pContainerNode)
{
return nullptr;
}
if (IsParentAssociativeContainer(pContainerNode) && IsPairContainer(pContainerNode))
{
// Go up one more level to the associative container, we'll remove the pair from that container
pContainerNode = pContainerNode->GetParent();
node = node->GetParent();
}
AZ_Assert(
pContainerNode, "Failed to locate parent container for element \"%s\" of type %s.",
node->GetElementMetadata() ? node->GetElementMetadata()->m_name : node->GetClassMetadata()->m_name,
node->GetClassMetadata()->m_typeId.ToString<AZStd::string>().c_str());
return pContainerNode;
}
InstanceDataNode* ReflectedPropertyEditor::GetNodeAtIndex(int index)
{
if (index >= m_impl->m_widgetsInDisplayOrder.size())
{
return nullptr;
}
return GetNodeFromWidget(m_impl->m_widgetsInDisplayOrder[index]);
}
QSet<PropertyRowWidget*> ReflectedPropertyEditor::GetTopLevelWidgets()
{
return m_impl->getTopLevelWidgets();
}
void ReflectedPropertyEditor::ChangeNodeIndex(InstanceDataNode* containerNode, InstanceDataNode* node, int fromIndex, int toIndex)
{
auto container = containerNode->GetElementMetadata()
? containerNode->GetElementMetadata()->m_genericClassInfo->GetClassData()->m_container
: nullptr;
if (fromIndex == toIndex)
{
return;
}
if (!container || container->GetAssociativeContainerInterface())
{
return;
}
AZ::Uuid typeId = node->GetClassMetadata()->m_typeId;
if (m_impl->m_ptrNotify)
{
m_impl->m_ptrNotify->BeforePropertyModified(containerNode);
}
const AZ::SerializeContext::ClassElement* containerClassElement = container->GetElement(container->GetDefaultElementNameCrc());
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
// Backup the item we're moving.
void* srcElement = nullptr;
void* destElement = nullptr;
int destIndex = -1;
int srcIndex = fromIndex;
srcElement = container->GetElementByIndex(containerNode->GetInstance(0), containerClassElement, srcIndex);
void* tmpBuffer = serializeContext->CloneObject(srcElement, typeId);
// Shuffle all intervening items up (or down).
int indexOffset = (toIndex < fromIndex) ? -1 : 1;
while (destIndex != toIndex - indexOffset)
{
destIndex = srcIndex;
srcIndex += indexOffset;
destElement = srcElement;
srcElement = container->GetElementByIndex(containerNode->GetInstance(0), containerClassElement, srcIndex);
serializeContext->CloneObjectInplace(destElement, srcElement, typeId);
}
// Now replace the final element with the one backed up previously.
destElement = srcElement;
serializeContext->CloneObjectInplace(destElement, tmpBuffer, typeId);
if (m_impl->m_ptrNotify)
{
m_impl->m_ptrNotify->AfterPropertyModified(containerNode);
m_impl->m_ptrNotify->SealUndoStack();
}
// Need to refresh any pinned inspectors as well to keep the container state in sync
QueueInvalidation(Refresh_Values);
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values);
}
void ReflectedPropertyEditor::MoveNodeToIndex(InstanceDataNode* node, int index)
{
InstanceDataNode* pContainerNode = FindContainerNodeForNode(node);
if (!pContainerNode)
{
return;
}
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
AZStd::vector<void*> nodeInstancesOut;
const int elementIndex = CalculateElementIndexInContainer(node, pContainerNode->GetInstance(0), container, nodeInstancesOut);
ChangeNodeIndex(pContainerNode, node, elementIndex, index);
}
void ReflectedPropertyEditor::MoveNodeBefore(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore)
{
InstanceDataNode* pContainerNode = FindContainerNodeForNode(nodeToMove);
InstanceDataNode* pContainerNodeTarget = FindContainerNodeForNode(nodeToMoveBefore);
if (nodeToMove == nodeToMoveBefore)
{
return;
}
// Can only move nodes within the same parent.
if (pContainerNode != pContainerNodeTarget)
{
return;
}
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
AZStd::vector<void*> nodeInstancesOut;
int elementIndex = CalculateElementIndexInContainer(nodeToMove, pContainerNode->GetInstance(0), container, nodeInstancesOut);
nodeInstancesOut.clear();
int elementIndexTarget =
CalculateElementIndexInContainer(nodeToMoveBefore, pContainerNode->GetInstance(0), container, nodeInstancesOut);
if (elementIndex < elementIndexTarget)
{
elementIndexTarget -= 1;
}
ChangeNodeIndex(pContainerNode, nodeToMove, elementIndex, elementIndexTarget);
}
void ReflectedPropertyEditor::MoveNodeAfter(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore)
{
InstanceDataNode* pContainerNode = FindContainerNodeForNode(nodeToMove);
InstanceDataNode* pContainerNodeTarget = FindContainerNodeForNode(nodeToMoveBefore);
if (nodeToMove == nodeToMoveBefore)
{
return;
}
// Can only move nodes within the same parent.
if (pContainerNode != pContainerNodeTarget)
{
return;
}
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
AZStd::vector<void*> nodeInstancesOut;
int elementIndex = CalculateElementIndexInContainer(nodeToMove, pContainerNode->GetInstance(0), container, nodeInstancesOut);
nodeInstancesOut.clear();
int elementIndexTarget =
CalculateElementIndexInContainer(nodeToMoveBefore, pContainerNode->GetInstance(0), container, nodeInstancesOut);
if (elementIndex > elementIndexTarget)
{
elementIndexTarget += 1;
}
ChangeNodeIndex(pContainerNode, nodeToMove, elementIndex, elementIndexTarget);
}
int ReflectedPropertyEditor::GetNodeIndexInContainer(InstanceDataNode* node)
{
InstanceDataNode* pContainerNode = FindContainerNodeForNode(node);
AZ::SerializeContext::IDataContainer* container = pContainerNode->GetClassMetadata()->m_container;
AZStd::vector<void*> nodeInstancesOut;
int elementIndex = CalculateElementIndexInContainer(node, pContainerNode->GetInstance(0), container, nodeInstancesOut);
return elementIndex;
}
void ReflectedPropertyEditor::OnPropertyRowRequestContainerRemoveItem(PropertyRowWidget* widget, InstanceDataNode* node)
{
// Locate the owning container. There may be a level of indirection due to wrappers, such as DynamicSerializableField.
@@ -1690,7 +1906,7 @@ namespace AzToolsFramework
// the index of the element being removed
AZStd::vector<void*> nodeInstancesOut;
const size_t elementIndex = CalculateElementIndexInContainer(
const int elementIndex = CalculateElementIndexInContainer(
node, pContainerNode->GetInstance(0), container, nodeInstancesOut);
// pass the context as the last parameter to actually delete the related data.
@@ -155,9 +155,19 @@ namespace AzToolsFramework
using VisibilityCallback = AZStd::function<void(InstanceDataNode* node, NodeDisplayVisibility& visibility, bool& checkChildVisibility)>;
void SetVisibilityCallback(VisibilityCallback callback);
void MoveNodeToIndex(InstanceDataNode* node, int index);
void MoveNodeBefore(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore);
void MoveNodeAfter(InstanceDataNode* nodeToMove, InstanceDataNode* nodeToMoveBefore);
int GetNodeIndexInContainer(InstanceDataNode* node);
InstanceDataNode* GetNodeAtIndex(int index);
QSet<PropertyRowWidget*> GetTopLevelWidgets();
signals:
void OnExpansionContractionDone();
private:
InstanceDataNode* FindContainerNodeForNode(InstanceDataNode* node) const;
void ChangeNodeIndex(InstanceDataNode* containerNode, InstanceDataNode* node, int oldIndex, int newIndex);
class Impl;
std::unique_ptr<Impl> m_impl;
+14 -43
View File
@@ -2873,7 +2873,7 @@ void CXConsole::Paste()
//////////////////////////////////////////////////////////////////////////
int CXConsole::GetNumVars()
{
return (int)m_mapVariables.size();
return static_cast<int>(m_mapVariables.size());
}
//////////////////////////////////////////////////////////////////////////
@@ -3132,7 +3132,6 @@ char* CXConsole::GetCheatVarAt(uint32 nOffset)
//////////////////////////////////////////////////////////////////////////
size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, const char* szPrefix)
{
size_t i = 0;
size_t iPrefixLen = szPrefix ? strlen(szPrefix) : 0;
// variables
@@ -3140,11 +3139,6 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
ConsoleVariablesMap::const_iterator it, end = m_mapVariables.end();
for (it = m_mapVariables.begin(); it != end; ++it)
{
if (i >= pszArray.size())
{
break;
}
if (szPrefix)
{
if (_strnicmp(it->first, szPrefix, iPrefixLen) != 0)
@@ -3158,9 +3152,7 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
continue;
}
pszArray[i] = it->first;
i++;
pszArray.push_back(it->first);
}
}
@@ -3169,11 +3161,6 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
ConsoleCommandsMap::iterator it, end = m_mapCommands.end();
for (it = m_mapCommands.begin(); it != end; ++it)
{
if (i >= pszArray.size())
{
break;
}
if (szPrefix)
{
if (_strnicmp(it->first.c_str(), szPrefix, iPrefixLen) != 0)
@@ -3187,25 +3174,18 @@ size_t CXConsole::GetSortedVars(AZStd::vector<AZStd::string_view>& pszArray, con
continue;
}
pszArray[i] = it->first.c_str();
i++;
pszArray.push_back(it->first.c_str());
}
}
if (i != 0)
{
std::sort(pszArray.begin(), pszArray.end());
}
return i;
std::sort(pszArray.begin(), pszArray.end());
return pszArray.size();
}
//////////////////////////////////////////////////////////////////////////
void CXConsole::FindVar(const char* substr)
{
AZStd::vector<AZStd::string_view> cmds;
cmds.resize(GetNumVars() + m_mapCommands.size());
size_t cmdCount = GetSortedVars(cmds);
for (size_t i = 0; i < cmdCount; i++)
@@ -3231,10 +3211,9 @@ const char* CXConsole::AutoComplete(const char* substr)
// following code can be optimized
AZStd::vector<AZStd::string_view> cmds;
cmds.resize(GetNumVars() + m_mapCommands.size());
size_t cmdCount = GetSortedVars(cmds);
size_t substrLen = strlen(substr);
size_t substrLen = substr ? strlen(substr) : 0;
// If substring is empty return first command.
if (substrLen == 0 && cmdCount > 0)
@@ -3246,7 +3225,7 @@ const char* CXConsole::AutoComplete(const char* substr)
for (size_t i = 0; i < cmdCount; i++)
{
const char* szCmd = cmds[i].data();
size_t cmdlen = strlen(szCmd);
size_t cmdlen = cmds[i].size();
if (cmdlen >= substrLen && memcmp(szCmd, substr, substrLen) == 0)
{
if (substrLen == cmdlen)
@@ -3267,7 +3246,7 @@ const char* CXConsole::AutoComplete(const char* substr)
{
const char* szCmd = cmds[i].data();
size_t cmdlen = strlen(szCmd);
size_t cmdlen = cmds[i].size();
if (cmdlen >= substrLen && azstrnicmp(szCmd, substr, substrLen) == 0)
{
if (substrLen == cmdlen)
@@ -3301,27 +3280,19 @@ void CXConsole::SetInputLine(const char* szLine)
const char* CXConsole::AutoCompletePrev(const char* substr)
{
AZStd::vector<AZStd::string_view> cmds;
cmds.resize(GetNumVars() + m_mapCommands.size());
size_t cmdCount = GetSortedVars(cmds);
GetSortedVars(cmds);
// If substring is empty return last command.
if (strlen(substr) == 0 && cmds.size() > 0)
if (strlen(substr) == 0 && !cmds.empty())
{
return cmds[cmdCount - 1].data();
return cmds.back().data();
}
for (unsigned int i = 0; i < cmdCount; i++)
for (const AZStd::string_view& cmd : cmds)
{
if (azstricmp(substr, cmds[i].data()) == 0)
if (azstricmp(substr, cmd.data()) == 0)
{
if (i > 0)
{
return cmds[i - 1].data();
}
else
{
return cmds[0].data();
}
return cmd.data();
}
}
return AutoComplete(substr);
@@ -27,7 +27,7 @@ namespace TestImpact
"relative_paths",
"artifact_dir",
"enumeration_cache_dir",
"test_impact_data_files",
"test_impact_data_file",
"temp",
"active",
"target_sources",
@@ -72,7 +72,7 @@ namespace TestImpact
RelativePaths,
ArtifactDir,
EnumerationCacheDir,
TestImpactDataFiles,
TestImpactDataFile,
TempWorkspace,
ActiveWorkspace,
TargetSources,
@@ -138,31 +138,18 @@ namespace TestImpact
tempWorkspaceConfig.m_artifactDirectory =
GetAbsPathFromRelPath(
tempWorkspaceConfig.m_root, tempWorkspace[Config::Keys[Config::RelativePaths]][Config::Keys[Config::ArtifactDir]].GetString());
tempWorkspaceConfig.m_enumerationCacheDirectory = GetAbsPathFromRelPath(
tempWorkspaceConfig.m_root,
tempWorkspace[Config::Keys[Config::RelativePaths]][Config::Keys[Config::EnumerationCacheDir]].GetString());
return tempWorkspaceConfig;
}
AZStd::array<RepoPath, 3> ParseTestImpactAnalysisDataFiles(const RepoPath& root, const rapidjson::Value& sparTiaFile)
{
AZStd::array<RepoPath, 3> sparTiaFiles;
sparTiaFiles[static_cast<size_t>(SuiteType::Main)] =
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Main).c_str()].GetString());
sparTiaFiles[static_cast<size_t>(SuiteType::Periodic)] =
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Periodic).c_str()].GetString());
sparTiaFiles[static_cast<size_t>(SuiteType::Sandbox)] =
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Sandbox).c_str()].GetString());
return sparTiaFiles;
}
WorkspaceConfig::Active ParseActiveWorkspaceConfig(const rapidjson::Value& activeWorkspace)
{
WorkspaceConfig::Active activeWorkspaceConfig;
const auto& relativePaths = activeWorkspace[Config::Keys[Config::RelativePaths]];
activeWorkspaceConfig.m_root = activeWorkspace[Config::Keys[Config::Root]].GetString();
activeWorkspaceConfig.m_enumerationCacheDirectory
= GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::EnumerationCacheDir]].GetString());
activeWorkspaceConfig.m_sparTiaFiles =
ParseTestImpactAnalysisDataFiles(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::TestImpactDataFiles]]);
activeWorkspaceConfig.m_sparTiaFile = relativePaths[Config::Keys[Config::TestImpactDataFile]].GetString();
return activeWorkspaceConfig;
}
@@ -530,7 +530,7 @@ namespace TestImpact
size_t GetTotalNumTimedOutTestRuns() const override;
size_t GetTotalNumUnexecutedTestRuns() const override;
//! Returns the report for the discarded test runs.
// ImpactAnalysisSequenceReport overrides ...
const TestRunSelection GetDiscardedTestRuns() const;
//! Returns the report for the discarded test runs.
@@ -37,14 +37,14 @@ namespace TestImpact
{
RepoPath m_root; //!< Path to the temporary workspace (cleaned prior to use).
RepoPath m_artifactDirectory; //!< Path to read and write runtime artifacts to and from.
RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache.
};
//! Active persistent data workspace configuration.
struct Active
{
RepoPath m_root; //!< Path to the persistent workspace tracked by the repository.
RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache.
AZStd::array<RepoPath, 3> m_sparTiaFiles; //!< Paths to the test impact analysis data files for each test suite.
RepoPath m_sparTiaFile; //!< Paths to the test impact analysis data file.
};
Temp m_temp;
@@ -275,7 +275,7 @@ namespace TestImpact
m_testEngine = AZStd::make_unique<TestEngine>(
m_config.m_repo.m_root,
m_config.m_target.m_outputDirectory,
m_config.m_workspace.m_active.m_enumerationCacheDirectory,
m_config.m_workspace.m_temp.m_enumerationCacheDirectory,
m_config.m_workspace.m_temp.m_artifactDirectory,
m_config.m_testEngine.m_testRunner.m_binary,
m_config.m_testEngine.m_instrumentation.m_binary,
@@ -289,7 +289,8 @@ namespace TestImpact
}
else
{
m_sparTiaFile = m_config.m_workspace.m_active.m_sparTiaFiles[static_cast<size_t>(m_suiteFilter)].String();
m_sparTiaFile =
m_config.m_workspace.m_active.m_root / RepoPath(SuiteTypeAsString(m_suiteFilter)) / m_config.m_workspace.m_active.m_sparTiaFile;
}
// Populate the dynamic dependency map with the existing source coverage data (if any)
@@ -10,6 +10,7 @@
#include <AzCore/Math/MathIntrinsics.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/typetraits/aligned_storage.h>
#include <AzCore/base.h>
#include <stdint.h>
+17 -2
View File
@@ -46,11 +46,26 @@ namespace PhysX
JointComponent::LeadFollowerInfo leadFollowerInfo;
ObtainLeadFollowerInfo(leadFollowerInfo);
if (!leadFollowerInfo.m_followerActor)
if (leadFollowerInfo.m_followerActor == nullptr ||
leadFollowerInfo.m_followerBody == nullptr)
{
return;
}
// if there is no lead body, this will be a constraint of the follower's global position, so use invalid body handle.
AzPhysics::SimulatedBodyHandle parentHandle = AzPhysics::InvalidSimulatedBodyHandle;
if (leadFollowerInfo.m_leadBody != nullptr)
{
parentHandle = leadFollowerInfo.m_leadBody->m_bodyHandle;
}
else
{
AZ_TracePrintf(
"PhysX", "Entity [%s] Ball Joint component missing lead entity. This joint will be a global constraint on the follower's global position.",
GetEntity()->GetName().c_str());
}
BallJointConfiguration configuration;
configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation();
configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation();
@@ -65,7 +80,7 @@ namespace PhysX
m_jointHandle = sceneInterface->AddJoint(
leadFollowerInfo.m_followerBody->m_sceneOwner,
&configuration,
leadFollowerInfo.m_leadBody->m_bodyHandle,
parentHandle,
leadFollowerInfo.m_followerBody->m_bodyHandle);
m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner;
}
+16 -2
View File
@@ -54,11 +54,25 @@ namespace PhysX
JointComponent::LeadFollowerInfo leadFollowerInfo;
ObtainLeadFollowerInfo(leadFollowerInfo);
if (!leadFollowerInfo.m_followerActor)
if (leadFollowerInfo.m_followerActor == nullptr ||
leadFollowerInfo.m_followerBody == nullptr)
{
return;
}
// if there is no lead body, this will be a constraint of the follower's global position, so use invalid body handle.
AzPhysics::SimulatedBodyHandle parentHandle = AzPhysics::InvalidSimulatedBodyHandle;
if (leadFollowerInfo.m_leadBody != nullptr)
{
parentHandle = leadFollowerInfo.m_leadBody->m_bodyHandle;
}
else
{
AZ_TracePrintf("PhysX",
"Entity [%s] Fixed Joint component missing lead entity. This joint will be a global constraint on the follower's global position.",
GetEntity()->GetName().c_str());
}
FixedJointConfiguration configuration;
configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation();
configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation();
@@ -72,7 +86,7 @@ namespace PhysX
m_jointHandle = sceneInterface->AddJoint(
leadFollowerInfo.m_followerBody->m_sceneOwner,
&configuration,
leadFollowerInfo.m_leadBody->m_bodyHandle,
parentHandle,
leadFollowerInfo.m_followerBody->m_bodyHandle);
m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner;
}
+16 -2
View File
@@ -48,12 +48,24 @@ namespace PhysX
JointComponent::LeadFollowerInfo leadFollowerInfo;
ObtainLeadFollowerInfo(leadFollowerInfo);
if (leadFollowerInfo.m_followerActor == nullptr ||
leadFollowerInfo.m_leadBody == nullptr ||
leadFollowerInfo.m_followerBody == nullptr)
{
return;
}
// if there is no lead body, this will be a constraint of the follower's global position, so use invalid body handle.
AzPhysics::SimulatedBodyHandle parentHandle = AzPhysics::InvalidSimulatedBodyHandle;
if (leadFollowerInfo.m_leadBody != nullptr)
{
parentHandle = leadFollowerInfo.m_leadBody->m_bodyHandle;
}
else
{
AZ_TracePrintf(
"PhysX", "Entity [%s] Hinge Joint component missing lead entity. This joint will be a global constraint on the follower's global position.",
GetEntity()->GetName().c_str());
}
HingeJointConfiguration configuration;
configuration.m_parentLocalPosition = leadFollowerInfo.m_leadLocal.GetTranslation();
configuration.m_parentLocalRotation = leadFollowerInfo.m_leadLocal.GetRotation();
@@ -66,7 +78,9 @@ namespace PhysX
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
m_jointHandle = sceneInterface->AddJoint(
leadFollowerInfo.m_followerBody->m_sceneOwner, &configuration, leadFollowerInfo.m_leadBody->m_bodyHandle,
leadFollowerInfo.m_followerBody->m_sceneOwner,
&configuration,
parentHandle,
leadFollowerInfo.m_followerBody->m_bodyHandle);
m_jointSceneOwner = leadFollowerInfo.m_followerBody->m_sceneOwner;
}
@@ -190,8 +190,9 @@ namespace PhysX {
{
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
if (!actorData.parentActor || !actorData.childActor)
if (actorData.parentActor == nullptr && actorData.childActor == nullptr)
{
AZ_Warning("PhysX Joint", false, "CreateJoint failed - at least one body must be a PxRigidActor.");
return nullptr;
}
@@ -239,7 +240,8 @@ namespace PhysX {
{
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
if (!actorData.parentActor || !actorData.childActor)
//only check the child actor, as a null parent actor means this joint is a global constraint.
if (!actorData.childActor)
{
return nullptr;
}
@@ -252,7 +254,8 @@ namespace PhysX {
{
PHYSX_SCENE_READ_LOCK(actorData.childActor->getScene());
joint = physx::PxFixedJointCreate(PxGetPhysics(),
joint = physx::PxFixedJointCreate(
PxGetPhysics(),
actorData.parentActor, PxMathConvert(parentLocalTM),
actorData.childActor, PxMathConvert(childLocalTM));
}
@@ -272,7 +275,8 @@ namespace PhysX {
{
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
if (!actorData.parentActor || !actorData.childActor)
// only check the child actor, as a null parent actor means this joint is a global constraint.
if (!actorData.childActor)
{
return nullptr;
}
@@ -306,7 +310,8 @@ namespace PhysX {
{
PxJointActorData actorData = GetJointPxActors(sceneHandle, parentBodyHandle, childBodyHandle);
if (!actorData.parentActor || !actorData.childActor)
// only check the child actor, as a null parent actor means this joint is a global constraint.
if (!actorData.childActor)
{
return nullptr;
}
+62 -4
View File
@@ -123,7 +123,7 @@ namespace PhysX
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_TRUE(followerEndPosition.GetX() > followerPosition.GetX());
EXPECT_GT(followerEndPosition.GetX(), followerPosition.GetX());
}
TEST_F(PhysXJointsTest, Joint_HingeJoint_FollowerSwingsAroundLead)
@@ -164,8 +164,8 @@ namespace PhysX
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_TRUE(followerEndPosition.GetX() > followerPosition.GetX());
EXPECT_TRUE(abs(followerEndPosition.GetZ()) > FLT_EPSILON);
EXPECT_GT(followerEndPosition.GetX(), followerPosition.GetX());
EXPECT_GT(abs(followerEndPosition.GetZ()), FLT_EPSILON);
}
TEST_F(PhysXJointsTest, Joint_BallJoint_FollowerSwingsUpAboutLead)
@@ -206,7 +206,65 @@ namespace PhysX
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_TRUE(followerEndPosition.GetZ() > followerPosition.GetZ());
EXPECT_GT(followerEndPosition.GetZ(), followerPosition.GetZ());
}
TEST_F(PhysXJointsTest, Joint_BallJoint_GlobalConstraint)
{
// Place an entity in the world with a rigid body, physx collider, and a ball joint components.
// Do not set a lead entity on the ball joint component.
// Set entity's initial velocity to 10 in the X and Y directions on the rigid body component.
// The entity should swing up on the global constraint.
const AZ::Vector3 followerPosition(0.0f, 0.0f, -1.0f);
const AZ::Vector3 followerInitialLinearVelocity(10.0f, 10.0f, 0.0f);
const AZ::Vector3 jointLocalPosition(0.0f, 0.0f, 2.0f);
const AZ::Quaternion jointLocalRotation = AZ::Quaternion::CreateRotationY(90.0f);
const AZ::Transform jointLocalTransform = AZ::Transform::CreateFromQuaternionAndTranslation(jointLocalRotation, jointLocalPosition);
//we want a global constraint, so leave the lead entity unset.
auto jointConfig = AZStd::make_shared<JointComponentConfiguration>();
jointConfig->m_localTransformFromFollower = jointLocalTransform;
auto jointLimits = AZStd::make_shared<JointLimitProperties>();
jointLimits->m_isLimited = false;
auto followerEntity = AddBodyColliderEntity<BallJointComponent>(
m_testSceneHandle, followerPosition, followerInitialLinearVelocity, jointConfig, nullptr, jointLimits);
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_GT(followerEndPosition.GetZ(), followerPosition.GetZ());
}
TEST_F(PhysXJointsTest, Joint_HingeJoint_GlobalConstraint)
{
// Place an entity in the world with a rigid body, physx collider, and a hinge joint components.
// Do not set a lead entity on the hinge joint component.
// Set entity's initial velocity to 10 in the X and Y directions on the rigid body component.
// The entity should swing up on the global constraint.
const AZ::Vector3 followerPosition(0.0f, 0.0f, -1.0f);
const AZ::Vector3 followerInitialLinearVelocity(10.0f, 10.0f, 0.0f);
const AZ::Vector3 jointLocalPosition(0.0f, 0.0f, 2.0f);
const AZ::Quaternion jointLocalRotation = AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 180.0f, 90.0f));
const AZ::Transform jointLocalTransform = AZ::Transform::CreateFromQuaternionAndTranslation(jointLocalRotation, jointLocalPosition);
// do not set the lead entity as that makes this a global constraint
auto jointConfig = AZStd::make_shared<JointComponentConfiguration>();
jointConfig->m_localTransformFromFollower = jointLocalTransform;
auto jointLimits = AZStd::make_shared<JointLimitProperties>();
jointLimits->m_isLimited = false;
auto followerEntity = AddBodyColliderEntity<HingeJointComponent>(
m_testSceneHandle, followerPosition, followerInitialLinearVelocity, jointConfig, nullptr, jointLimits);
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_GT(followerEndPosition.GetZ(), followerPosition.GetZ());
}
// for some reason TYPED_TEST_CASE with the fixture is not working on Android + Linux
@@ -15,19 +15,14 @@
"temp": {
"root": "${temp_dir}",
"relative_paths": {
"artifact_dir": "RuntimeArtifact"
"artifact_dir": "RuntimeArtifact",
"enumeration_cache_dir": "EnumerationCache"
}
},
"active": {
"root": "${active_dir}",
"relative_paths": {
"test_impact_data_files": {
"main": "TestImpactData.main.spartia",
"periodic": "TestImpactData.periodic.spartia",
"sandbox": "TestImpactData.sandbox.spartia"
},
"enumeration_cache_dir": "EnumerationCache",
"last_build_target_list_file": "LastRunBuildTargets.json"
"test_impact_data_file": "TestImpactData.spartia"
}
},
"historic": {
+26 -14
View File
@@ -372,9 +372,12 @@ CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_FORMAT_STR = """
}}
compile{config}Sources.dependsOn copyNativeArtifacts{config}
"""
CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_DEPENDENCY_FORMAT_STR = """
copyNativeArtifacts{config}.mustRunAfter {{
tasks.findAll {{ task->task.name.contains('externalNativeBuild{config}') }}
tasks.findAll {{ task->task.name.contains('syncLYLayoutMode{config}') }}
}}
"""
@@ -383,7 +386,13 @@ CUSTOM_APPLY_ASSET_LAYOUT_TASK_FORMAT_STR = """
workingDir '{working_dir}'
commandLine '{python_full_path}', 'layout_tool.py', '--project-path', '{project_path}', '-p', 'Android', '-a', '{asset_type}', '-m', '{asset_mode}', '--create-layout-root', '-l', '{asset_layout_folder}'
}}
compile{config}Sources.dependsOn syncLYLayoutMode{config}
syncLYLayoutMode{config}.mustRunAfter {{
tasks.findAll {{ task->task.name.contains('externalNativeBuild{config}') }}
}}
"""
@@ -832,25 +841,28 @@ class AndroidProjectGenerator(object):
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
file_includes='Test.Assets/**/*.*')
else:
# Copy over settings registry files from the Registry folder with build output directory
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] = \
CUSTOM_APPLY_ASSET_LAYOUT_TASK_FORMAT_STR.format(working_dir=common.normalize_path_for_settings(self.engine_root / 'cmake/Tools'),
python_full_path=common.normalize_path_for_settings(self.engine_root / 'python' / PYTHON_SCRIPT),
asset_type=self.asset_type,
project_path=self.project_path.as_posix(),
asset_mode=self.asset_mode if native_config != 'Release' else 'PAK',
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
config=native_config)
# Copy over settings registry files from the Registry folder with build output directory
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] += \
CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_FORMAT_STR.format(config=native_config,
config_lower=native_config_lower,
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
file_includes='**/Registry/*.setreg')
if self.include_assets_in_apk:
if not self.is_test_project:
if self.include_assets_in_apk:
# This is a dependency of the layout sync only if we are including assets in the APK
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] += \
CUSTOM_APPLY_ASSET_LAYOUT_TASK_FORMAT_STR.format(working_dir=common.normalize_path_for_settings(self.engine_root / 'cmake/Tools'),
python_full_path=common.normalize_path_for_settings(self.engine_root / 'python' / PYTHON_SCRIPT),
asset_type=self.asset_type,
project_path=self.project_path.as_posix(),
asset_mode=self.asset_mode if native_config != 'Release' else 'PAK',
asset_layout_folder=(self.build_dir / 'app/src/main/assets').resolve().as_posix(),
config=native_config)
else:
gradle_build_env[f'CUSTOM_APPLY_ASSET_LAYOUT_{native_config_upper}_TASK'] = ''
CUSTOM_GRADLE_COPY_NATIVE_CONFIG_BUILD_ARTIFACTS_DEPENDENCY_FORMAT_STR.format(config=native_config)
if self.signing_config:
gradle_build_env[f'SIGNING_{native_config_upper}_CONFIG'] = f'signingConfig signingConfigs.{native_config_lower}' if self.signing_config else ''
else:
+1 -2
View File
@@ -230,7 +230,7 @@ class TestImpact:
# Flag for corner case where:
# 1. TIAF was already run previously for this commit.
# 2. There was no last commit hash when TIAF last ran on this commit (due to no coverage data existing get for this branch)
# 2. There was no last commit hash when TIAF last ran on this commit (due to no coverage data existing yet for this branch)
# 3. TIAF has not been run on any other commits between the run for this commit and the last run for this commit.
# The above results in TIAF being stuck in a state of generating an empty change list (and thus doing no work until another
# commit comes in) which is problematic if the commit needs to be re-run for whatever reason so in these conditions we revert
@@ -323,7 +323,6 @@ class TestImpact:
logger.info(f"Args: {unpacked_args}")
runtime_result = subprocess.run([str(self._tiaf_bin)] + args)
report = None
# If the sequence completed (with or without failures) we will update the historical meta-data
if runtime_result.returncode == 0 or runtime_result.returncode == 7:
logger.info("Test impact analysis runtime returned successfully.")
@@ -17,11 +17,11 @@ logger = get_logger(__file__)
class PersistentStorage(ABC):
WORKSPACE_KEY = "workspace"
LAST_RUNS_KEY = "last_runs"
HISTORIC_SEQUENCES_KEY = "historic_sequences"
ACTIVE_KEY = "active"
ROOT_KEY = "root"
RELATIVE_PATHS_KEY = "relative_paths"
TEST_IMPACT_DATA_FILES_KEY = "test_impact_data_files"
TEST_IMPACT_DATA_FILE_KEY = "test_impact_data_file"
LAST_COMMIT_HASH_KEY = "last_commit_hash"
COVERAGE_DATA_KEY = "coverage_data"
@@ -35,19 +35,21 @@ class PersistentStorage(ABC):
"""
# Work on the assumption that there is no historic meta-data (a valid state to be in, should none exist)
self._suite = suite
self._last_commit_hash = None
self._has_historic_data = False
self._has_previous_last_commit_hash = False
self._this_commit_hash = commit
self._this_commit_hash_last_commit_hash = None
self._historic_data = None
logger.info(f"Attempting to access persistent storage for the commit {self._this_commit_hash}")
logger.info(f"Attempting to access persistent storage for the commit '{self._this_commit_hash}' for suite '{self._suite}'")
try:
# The runtime expects the coverage data to be in the location specified in the config file (unless overridden with
# the --datafile command line argument, which the TIAF scripts do not do)
self._active_workspace = pathlib.Path(config[self.WORKSPACE_KEY][self.ACTIVE_KEY][self.ROOT_KEY])
unpacked_coverage_data_file = config[self.WORKSPACE_KEY][self.ACTIVE_KEY][self.RELATIVE_PATHS_KEY][self.TEST_IMPACT_DATA_FILES_KEY][suite]
self._active_workspace = self._active_workspace.joinpath(pathlib.Path(self._suite))
unpacked_coverage_data_file = config[self.WORKSPACE_KEY][self.ACTIVE_KEY][self.RELATIVE_PATHS_KEY][self.TEST_IMPACT_DATA_FILE_KEY]
except KeyError as e:
raise SystemError(f"The config does not contain the key {str(e)}.")
@@ -70,25 +72,27 @@ class PersistentStorage(ABC):
self._last_commit_hash = self._historic_data[self.LAST_COMMIT_HASH_KEY]
logger.info(f"Last commit hash '{self._last_commit_hash}' found.")
if self.LAST_RUNS_KEY in self._historic_data:
# Last commit hash for the sequence that was run for this commit previously (if any)
if self._this_commit_hash in self._historic_data[self.LAST_RUNS_KEY]:
# Last commit hash for the sequence that was run for this commit previously (if any)
if self.HISTORIC_SEQUENCES_KEY in self._historic_data:
if self._this_commit_hash in self._historic_data[self.HISTORIC_SEQUENCES_KEY]:
# 'None' is a valid value for the previously used last commit hash if there was no coverage data at that time
self._this_commit_hash_last_commit_hash = self._historic_data[self.LAST_RUNS_KEY][self._this_commit_hash]
self._this_commit_hash_last_commit_hash = self._historic_data[self.HISTORIC_SEQUENCES_KEY][self._this_commit_hash]
self._has_previous_last_commit_hash = self._this_commit_hash_last_commit_hash is not None
if self._has_previous_last_commit_hash:
logger.info(f"Last commit hash '{self._this_commit_hash_last_commit_hash}' was used previously for this commit.")
else:
logger.info(f"Prior sequence data found for this commit but it is empty (there was no coverage data vailable at that time).")
logger.info(f"Prior sequence data found for this commit but it is empty (there was no coverage data available at that time).")
else:
logger.info(f"No prior sequence data found for commit '{self._this_commit_hash}', this is the first sequence for this commit.")
else:
logger.info(f"No prior sequence data found for any commits.")
# Create the active workspace directory where the coverage data file will be placed and unpack the coverage data so
# it is accessible by the runtime
# Create the active workspace directory for the unpacked historic data files so they are accessible by the runtime
self._active_workspace.mkdir(exist_ok=True)
# Coverage file
logger.info(f"Writing coverage data to '{self._unpacked_coverage_data_file}'.")
with open(self._unpacked_coverage_data_file, "w", newline='\n') as coverage_data:
coverage_data.write(self._historic_data[self.COVERAGE_DATA_KEY])
@@ -117,9 +121,9 @@ class PersistentStorage(ABC):
self._historic_data[self.LAST_COMMIT_HASH_KEY] = self._this_commit_hash
# Last commit hash for this commit
if not self.LAST_RUNS_KEY in self._historic_data:
self._historic_data[self.LAST_RUNS_KEY] = {}
self._historic_data[self.LAST_RUNS_KEY][self._this_commit_hash] = self._last_commit_hash
if not self.HISTORIC_SEQUENCES_KEY in self._historic_data:
self._historic_data[self.HISTORIC_SEQUENCES_KEY] = {}
self._historic_data[self.HISTORIC_SEQUENCES_KEY][self._this_commit_hash] = self._last_commit_hash
# Coverage data for this branch
with open(self._unpacked_coverage_data_file, "r") as coverage_data:
@@ -32,10 +32,12 @@ class PersistentStorageLocal(PersistentStorage):
try:
# Attempt to obtain the local persistent data location specified in the runtime config file
self._historic_workspace = pathlib.Path(config[self.WORKSPACE_KEY][self.HISTORIC_KEY][self.ROOT_KEY])
self._historic_workspace = self._historic_workspace.joinpath(pathlib.Path(self._suite))
historic_data_file = pathlib.Path(config[self.WORKSPACE_KEY][self.HISTORIC_KEY][self.RELATIVE_PATHS_KEY][self.DATA_KEY])
# Attempt to unpack the local historic data file
self._historic_data_file = self._historic_workspace.joinpath(historic_data_file)
logger.info(f"Attempting to retrieve historic data at location '{self._historic_data_file}'...")
if self._historic_data_file.is_file():
with open(self._historic_data_file, "r") as historic_data_raw:
historic_data_json = historic_data_raw.read()
@@ -43,9 +43,9 @@ class PersistentStorageS3(PersistentStorage):
# historic_data.json.zip is the file containing the coverage and meta-data of the last TIAF sequence run
historic_data_file = f"historic_data.{object_extension}"
# The location of the data is in the form <root_dir>/<branch>/<config> so the build config of each branch gets its own historic data
self._historic_data_dir = f'{root_dir}/{branch}/{config[self.META_KEY][self.BUILD_CONFIG_KEY]}'
self._historic_data_key = f'{self._historic_data_dir}/{historic_data_file}'
# The location of the data is in the form <root_dir>/<branch>/<config>/<suite> so the build config of each branch gets its own historic data
self._historic_data_dir = f"{root_dir}/{branch}/{config[self.META_KEY][self.BUILD_CONFIG_KEY]}/{self._suite}"
self._historic_data_key = f"{self._historic_data_dir}/{historic_data_file}"
logger.info(f"Attempting to retrieve historic data for branch '{branch}' at location '{self._historic_data_key}' on bucket '{s3_bucket}'...")
self._s3 = boto3.resource("s3")