diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl index 90b9ba5afd..454d998321 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl @@ -53,6 +53,9 @@ namespace AZ //! RemoveableByUser : A bool which determines if the component can be removed by the user. //! Setting this to false prevents the user from removing this component. Default behavior is removeable by user. const static AZ::Crc32 RemoveableByUser = AZ_CRC("RemoveableByUser", 0x32c7fd50); + //! A bool which determines if the component can be dragged to change where it appears in the entity sort order. + //! Setting this to false prevents the user from dragging the component. Default behaviour is draggable by user. + const static AZ::Crc32 DraggableByUser = AZ_CRC_CE("DraggableByUser"); const static AZ::Crc32 AppearsInAddComponentMenu = AZ_CRC("AppearsInAddComponentMenu", 0x53790e31); const static AZ::Crc32 ForceAutoExpand = AZ_CRC("ForceAutoExpand", 0x1a5c79d2); // Ignores expansion state set by user, enforces expansion. const static AZ::Crc32 AutoExpand = AZ_CRC("AutoExpand", 0x306ff5c0); // Expands automatically unless user changes expansion state. diff --git a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp index 095d986fa1..57f14ddb38 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp @@ -36,6 +36,8 @@ namespace AzFramework void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); + incompatible.push_back(AZ_CRC_CE("DebugDrawObbService")); incompatible.push_back(AZ_CRC_CE("DebugDrawService")); incompatible.push_back(AZ_CRC_CE("EMotionFXActorService")); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h index 35b2e485b5..1959183fa0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h @@ -31,6 +31,10 @@ namespace AzToolsFramework //! Allows a component to get the list of selected entities //! \param selectedEntityIds the return vector holding the entities required virtual void GetSelectedEntities(EntityIdList& selectedEntityIds) = 0; + + //! Explicitly sets a component as having been the most recently added. + //! This means that the next time the UI refreshes, that component will be ensured to be visible. + virtual void SetNewComponentId(AZ::ComponentId componentId) = 0; }; using EntityPropertyEditorRequestBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp index 5e928a382a..d28f23c847 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp @@ -39,9 +39,8 @@ namespace AzToolsFramework editContext->Class("Non-uniform Scale", "Non-uniform scale for this entity only (does not propagate through hierarchy)") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Category, "Non-uniform Scale") - ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::RemoveableByUser, true) + ->Attribute(AZ::Edit::Attributes::DraggableByUser, false) ->DataElement( AZ::Edit::UIHandlers::Default, &EditorNonUniformScaleComponent::m_scale, "Non-uniform Scale", "Non-uniform scale for this entity only (does not propagate through hierarchy)") @@ -61,6 +60,8 @@ namespace AzToolsFramework void EditorNonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); + incompatible.push_back(AZ_CRC_CE("DebugDrawObbService")); incompatible.push_back(AZ_CRC_CE("DebugDrawService")); incompatible.push_back(AZ_CRC_CE("EMotionFXActorService")); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp index 7d8b802622..a025c02c32 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp @@ -26,12 +26,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -39,8 +41,6 @@ #include -#pragma optimize("", off) - namespace AzToolsFramework { namespace Components @@ -1232,33 +1232,28 @@ namespace AzToolsFramework AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(outcome, &AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, entityList, componentsToAdd); - auto nonUniformScaleComponent = GetEntity()->FindComponent(); + AZStd::vector pendingComponents; + AzToolsFramework::EditorPendingCompositionRequestBus::Event(GetEntityId(), + &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents); - if (!outcome.IsSuccess() || nonUniformScaleComponent == nullptr) + AZ::ComponentId nonUniformScaleComponentId = AZ::InvalidComponentId; + for (const auto pendingComponent : pendingComponents) { - AZ_Warning("Transform component", false, "Failed to add non-uniform scale component."); - return AZ::Edit::PropertyRefreshLevels::None; + if (pendingComponent->RTTI_IsTypeOf(AzToolsFramework::Components::EditorNonUniformScaleComponent::RTTI_Type())) + { + nonUniformScaleComponentId = pendingComponent->GetId(); + } } - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); - - ComponentOrderArray componentOrderArray; - EditorInspectorComponentRequestBus::EventResult(componentOrderArray, GetEntityId(), - &EditorInspectorComponentRequests::GetComponentOrderArray); - - // find the id for the non-uniform scale component and move it immediately after the transform component in the sort order - auto nonUniformScaleComponentIter = AZStd::find(componentOrderArray.begin(), componentOrderArray.end(), nonUniformScaleComponent->GetId()); - auto transformComponentIter = AZStd::find(componentOrderArray.begin(), componentOrderArray.end(), GetId()); - if (nonUniformScaleComponentIter != componentOrderArray.end() && transformComponentIter != componentOrderArray.end()) + if (!outcome.IsSuccess() || nonUniformScaleComponentId == AZ::InvalidComponentId) { - componentOrderArray.erase(nonUniformScaleComponentIter); - transformComponentIter = AZStd::find(componentOrderArray.begin(), componentOrderArray.end(), GetId()); - componentOrderArray.insert(++transformComponentIter, nonUniformScaleComponent->GetId()); - EditorInspectorComponentRequestBus::Event(GetEntityId(), - &EditorInspectorComponentRequests::SetComponentOrderArray, componentOrderArray); + AZ_Warning("Transform component", false, "Failed to add non-uniform scale component."); + return AZ::Edit::PropertyRefreshLevels::None; } + AzToolsFramework::EntityPropertyEditorRequestBus::Broadcast( + &AzToolsFramework::EntityPropertyEditorRequests::SetNewComponentId, nonUniformScaleComponentId); + return AZ::Edit::PropertyRefreshLevels::EntireTree; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index 181f5b9a9d..0fdd55f24e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -63,6 +63,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include #include @@ -494,6 +495,11 @@ namespace AzToolsFramework } } + void EntityPropertyEditor::SetNewComponentId(AZ::ComponentId componentId) + { + m_newComponentId = componentId; + } + void EntityPropertyEditor::SetOverrideEntityIds(const AzToolsFramework::EntityIdSet& entities) { m_overrideSelectedEntityIds = entities; @@ -1052,6 +1058,19 @@ namespace AzToolsFramework return false; } + // If component 1 is a non-uniform scale component, it is sorted earlier (it should appear immediately after transform) + if (component1.m_component->RTTI_IsTypeOf(AzToolsFramework::Components::EditorNonUniformScaleComponent::RTTI_Type())) + { + return true; + } + + // If component 2 is a non-uniform scale component, component 1 is never sorted earlier + // (transform will already dominate in the check above) + if (component2.m_component->RTTI_IsTypeOf(AzToolsFramework::Components::EditorNonUniformScaleComponent::RTTI_Type())) + { + return false; + } + if (!IsComponentRemovable(component1.m_component) && IsComponentRemovable(component2.m_component)) { return true; @@ -1128,10 +1147,7 @@ namespace AzToolsFramework { if (auto attributeData = azdynamic_cast*>(attribute)) { - if (!attributeData->Get(nullptr)) - { - return false; - } + return attributeData->Get(nullptr); } } } @@ -1166,6 +1182,34 @@ namespace AzToolsFramework return true; } + bool EntityPropertyEditor::IsComponentDraggable(const AZ::Component* component) + { + auto componentClassData = component ? GetComponentClassData(component) : nullptr; + if (componentClassData && componentClassData->m_editData) + { + if (auto editorDataElement = componentClassData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData)) + { + if (auto attribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::DraggableByUser)) + { + if (auto attributeData = azdynamic_cast*>(attribute)) + { + if (!attributeData->Get(nullptr)) + { + return false; + } + } + } + } + } + + return true; + } + + bool EntityPropertyEditor::AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const + { + return AZStd::all_of(components.begin(), components.end(), [](AZ::Component* component) {return IsComponentDraggable(component); }); + } + bool EntityPropertyEditor::AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const { return AreComponentsCopyable(components, m_componentFilter); @@ -3367,7 +3411,9 @@ namespace AzToolsFramework sourceComponents.size() == m_selectedEntityIds.size() && targetComponents.size() == m_selectedEntityIds.size() && AreComponentsRemovable(sourceComponents) && - AreComponentsRemovable(targetComponents); + AreComponentsRemovable(targetComponents) && + AreComponentsDraggable(sourceComponents) && + AreComponentsDraggable(targetComponents); } bool EntityPropertyEditor::IsMoveComponentsUpAllowed() const @@ -3681,14 +3727,38 @@ namespace AzToolsFramework void EntityPropertyEditor::ScrollToNewComponent() { - //force new components to be visible, assuming they are added to the end of the list and layout - auto componentEditor = GetComponentEditorsFromIndex(m_componentEditorsUsed - 1); + // force new components to be visible + // if no component has been explicitly set at the most recently added, + // assume new components are added to the end of the list and layout + AZ::s32 newComponentIndex = m_componentEditorsUsed - 1; + + // if there is a component id explicitly set as the most recently added, try to find it and make sure it is visible + if (m_newComponentId.has_value()) + { + AZ::ComponentId newComponentId = m_newComponentId.value(); + for (AZ::s32 componentIndex = 0; componentIndex < m_componentEditorsUsed; ++componentIndex) + { + if (m_componentEditors[componentIndex]) + { + for (const auto component : m_componentEditors[componentIndex]->GetComponents()) + { + if (component->GetId() == newComponentId) + { + newComponentIndex = componentIndex; + } + } + } + } + } + + auto componentEditor = GetComponentEditorsFromIndex(newComponentIndex); if (componentEditor) { m_gui->m_componentList->ensureWidgetVisible(componentEditor); } m_shouldScrollToNewComponents = false; m_shouldScrollToNewComponentsQueued = false; + m_newComponentId.reset(); } void EntityPropertyEditor::QueueScrollToNewComponent() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx index 677dc98277..2fb1c7e03a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx @@ -211,6 +211,7 @@ namespace AzToolsFramework // EntityPropertEditorRequestBus void GetSelectedAndPinnedEntities(EntityIdList& selectedEntityIds) override; void GetSelectedEntities(EntityIdList& selectedEntityIds) override; + void SetNewComponentId(AZ::ComponentId componentId) override; bool IsEntitySelected(const AZ::EntityId& id) const; bool IsSingleEntitySelected(const AZ::EntityId& id) const; @@ -237,6 +238,8 @@ namespace AzToolsFramework static bool DoesComponentPassFilter(const AZ::Component* component, const ComponentFilter& filter); static bool IsComponentRemovable(const AZ::Component* component); bool AreComponentsRemovable(const AZ::Entity::ComponentArrayType& components) const; + static bool IsComponentDraggable(const AZ::Component* component); + bool AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const; bool AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const; void AddMenuOptionsForComponents(QMenu& menu, const QPoint& position); @@ -568,6 +571,9 @@ namespace AzToolsFramework void ConnectToEntityBuses(const AZ::EntityId& entityId); void DisconnectFromEntityBuses(const AZ::EntityId& entityId); + //! Stores a component id to be focused on next time the UI updates. + AZStd::optional m_newComponentId; + private slots: void OnPropertyRefreshRequired(); // refresh is needed for a property. void UpdateContents();