diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index 2a565830bd..af53b1426e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -39,7 +39,7 @@ namespace AzToolsFramework virtual AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) = 0; //! Returns a list of the ids of all the entities that are descendants of the focus root. - virtual EntityIdList GetFocusedEntities(AzFramework::EntityContextId entityContextId) = 0; + virtual const EntityIdList& GetFocusedEntities(AzFramework::EntityContextId entityContextId) = 0; //! Returns whether the entity id provided is part of the focused sub-tree. virtual bool IsInFocusSubTree(AZ::EntityId entityId) const = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index 3266daedb3..cb78f58cdf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -42,6 +42,8 @@ namespace AzToolsFramework AZ::Interface::Register(this); EditorEntityInfoNotificationBus::Handler::BusConnect(); Prefab::PrefabPublicNotificationBus::Handler::BusConnect(); + + RefreshFocusedEntityIdList(); } void FocusModeSystemComponent::Deactivate() @@ -74,29 +76,28 @@ namespace AzToolsFramework void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId) { - if (m_focusRoot == entityId) - { - return; - } - - if (auto tracker = AZ::Interface::Get()) - { - if (!m_focusRoot.IsValid() && entityId.IsValid()) - { - tracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus); - } - else if (m_focusRoot.IsValid() && !entityId.IsValid()) - { - tracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus); - } - } - AZ::EntityId previousFocusEntityId = m_focusRoot; m_focusRoot = entityId; RefreshFocusedEntityIdList(); - FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot); + // Only trigger notifications if the focus root has changed. + if (m_focusRoot != previousFocusEntityId) + { + if (auto tracker = AZ::Interface::Get()) + { + if (!previousFocusEntityId.IsValid() && m_focusRoot.IsValid()) + { + tracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus); + } + else if (previousFocusEntityId.IsValid() && !m_focusRoot.IsValid()) + { + tracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus); + } + } + + FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot); + } } void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId) @@ -109,13 +110,14 @@ namespace AzToolsFramework return m_focusRoot; } - EntityIdList FocusModeSystemComponent::GetFocusedEntities([[maybe_unused]] AzFramework::EntityContextId entityContextId) + const EntityIdList& FocusModeSystemComponent::GetFocusedEntities([[maybe_unused]] AzFramework::EntityContextId entityContextId) { return m_focusedEntityIdList; } bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) const { + // If the focus is on the root, all entities are in the focus subtree. if (m_focusRoot == AZ::EntityId()) { return true; @@ -126,9 +128,30 @@ namespace AzToolsFramework void FocusModeSystemComponent::OnEntityInfoUpdatedAddChildEnd(AZ::EntityId parentId, AZ::EntityId childId) { - // If the parent's entityId is in the list, add the child. - if (auto iter = AZStd::find(m_focusedEntityIdList.begin(), m_focusedEntityIdList.end(), parentId); - iter != m_focusedEntityIdList.end()) + // If the parent's entityId is in the list and the child isn't, add the child to the list. + bool isParentInList = false; + bool isChildInList = false; + + for (auto iter = m_focusedEntityIdList.begin(); iter != m_focusedEntityIdList.end(); ++iter) + { + if (*iter == parentId) + { + isParentInList = true; + } + + if (*iter == childId) + { + isChildInList = true; + } + + // Early out + if (isChildInList) + { + break; + } + } + + if (isParentInList && !isChildInList) { m_focusedEntityIdList.push_back(childId); } @@ -140,7 +163,9 @@ namespace AzToolsFramework if (auto iter = AZStd::find(m_focusedEntityIdList.begin(), m_focusedEntityIdList.end(), childId); iter != m_focusedEntityIdList.end()) { - m_focusedEntityIdList.erase(iter); + // Swap and pop since we don't care about the ordering. + *iter = m_focusedEntityIdList.back(); + m_focusedEntityIdList.pop_back(); } } @@ -162,7 +187,10 @@ namespace AzToolsFramework AZ::EntityId entityId = entityIdQueue.front(); entityIdQueue.pop(); - m_focusedEntityIdList.push_back(entityId); + if (entityId.IsValid()) + { + m_focusedEntityIdList.push_back(entityId); + } EntityIdList children; EditorEntityInfoRequestBus::EventResult(children, entityId, &EditorEntityInfoRequestBus::Events::GetChildren); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index eb246a387f..9b07f7cffd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -46,7 +46,7 @@ namespace AzToolsFramework void SetFocusRoot(AZ::EntityId entityId) override; void ClearFocusRoot(AzFramework::EntityContextId entityContextId) override; AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) override; - EntityIdList GetFocusedEntities(AzFramework::EntityContextId entityContextId) override; + const EntityIdList& GetFocusedEntities(AzFramework::EntityContextId entityContextId) override; bool IsInFocusSubTree(AZ::EntityId entityId) const override; // EditorEntityInfoNotificationBus overrides ... diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/Mocks/MockFocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/Mocks/MockFocusModeInterface.h index 7bb5db14c5..6ad5e79b7a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/Mocks/MockFocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/Mocks/MockFocusModeInterface.h @@ -23,7 +23,7 @@ namespace UnitTest MOCK_METHOD1(SetFocusRoot, void(AZ::EntityId entityId)); MOCK_METHOD1(ClearFocusRoot, void(AzFramework::EntityContextId entityContextId)); MOCK_METHOD1(GetFocusRoot, AZ::EntityId(AzFramework::EntityContextId entityContextId)); - MOCK_METHOD1(GetFocusedEntities, AzToolsFramework::EntityIdList(AzFramework::EntityContextId entityContextId)); + MOCK_METHOD1(GetFocusedEntities, const AzToolsFramework::EntityIdList&(AzFramework::EntityContextId entityContextId)); MOCK_CONST_METHOD1(IsInFocusSubTree, bool(AZ::EntityId entityId)); }; } // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp index 524323230b..f6783cac07 100644 --- a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp @@ -31,18 +31,58 @@ namespace UnitTest EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), AZ::EntityId()); } + TEST_F(EditorFocusModeFixture, GetFocusedEntitiesRoot) + { + AzToolsFramework::EntityIdList entities = m_focusModeInterface->GetFocusedEntities(m_editorEntityContextId); + + using ::testing::UnorderedElementsAre; + EXPECT_EQ(entities.size(), 6); + EXPECT_THAT( + entities, + UnorderedElementsAre( + m_entityMap[CityEntityName], + m_entityMap[StreetEntityName], + m_entityMap[CarEntityName], + m_entityMap[Passenger1EntityName], + m_entityMap[SportsCarEntityName], + m_entityMap[Passenger2EntityName] + ) + ); + } + + TEST_F(EditorFocusModeFixture, GetFocusedEntitiesClear) + { + m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); + + AzToolsFramework::EntityIdList entities = m_focusModeInterface->GetFocusedEntities(m_editorEntityContextId); + + using ::testing::UnorderedElementsAre; + EXPECT_EQ(entities.size(), 6); + EXPECT_THAT( + entities, + UnorderedElementsAre( + m_entityMap[CityEntityName], m_entityMap[StreetEntityName], m_entityMap[CarEntityName], m_entityMap[Passenger1EntityName], + m_entityMap[SportsCarEntityName], m_entityMap[Passenger2EntityName])); + } + TEST_F(EditorFocusModeFixture, GetFocusedEntitiesBase) { m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]); AzToolsFramework::EntityIdList entities = m_focusModeInterface->GetFocusedEntities(m_editorEntityContextId); + using ::testing::UnorderedElementsAre; EXPECT_EQ(entities.size(), 5); - EXPECT_TRUE(AZStd::find(entities.begin(), entities.end(), m_entityMap[StreetEntityName]) != entities.end()); - EXPECT_TRUE(AZStd::find(entities.begin(), entities.end(), m_entityMap[CarEntityName]) != entities.end()); - EXPECT_TRUE(AZStd::find(entities.begin(), entities.end(), m_entityMap[Passenger1EntityName]) != entities.end()); - EXPECT_TRUE(AZStd::find(entities.begin(), entities.end(), m_entityMap[SportsCarEntityName]) != entities.end()); - EXPECT_TRUE(AZStd::find(entities.begin(), entities.end(), m_entityMap[Passenger2EntityName]) != entities.end()); + EXPECT_THAT( + entities, + UnorderedElementsAre( + m_entityMap[StreetEntityName], + m_entityMap[CarEntityName], + m_entityMap[Passenger1EntityName], + m_entityMap[SportsCarEntityName], + m_entityMap[Passenger2EntityName] + ) + ); } TEST_F(EditorFocusModeFixture, GetFocusedEntitiesSiblings) @@ -51,9 +91,9 @@ namespace UnitTest AzToolsFramework::EntityIdList entities = m_focusModeInterface->GetFocusedEntities(m_editorEntityContextId); + using ::testing::UnorderedElementsAre; EXPECT_EQ(entities.size(), 2); - EXPECT_TRUE(AZStd::find(entities.begin(), entities.end(), m_entityMap[SportsCarEntityName]) != entities.end()); - EXPECT_TRUE(AZStd::find(entities.begin(), entities.end(), m_entityMap[Passenger2EntityName]) != entities.end()); + EXPECT_THAT(entities, UnorderedElementsAre(m_entityMap[SportsCarEntityName], m_entityMap[Passenger2EntityName])); } TEST_F(EditorFocusModeFixture, GetFocusedEntitiesAddEntity)