/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include #include namespace AzToolsFramework::Prefab { PrefabFocusHandler::PrefabFocusHandler() { m_instanceEntityMapperInterface = AZ::Interface::Get(); AZ_Assert( m_instanceEntityMapperInterface, "Prefab - PrefabFocusHandler - " "Instance Entity Mapper Interface could not be found. " "Check that it is being correctly initialized."); EditorEntityInfoNotificationBus::Handler::BusConnect(); EditorEntityContextNotificationBus::Handler::BusConnect(); PrefabPublicNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); AZ::Interface::Register(this); } PrefabFocusHandler::~PrefabFocusHandler() { AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); PrefabPublicNotificationBus::Handler::BusDisconnect(); EditorEntityContextNotificationBus::Handler::BusDisconnect(); EditorEntityInfoNotificationBus::Handler::BusDisconnect(); } void PrefabFocusHandler::InitializeEditorInterfaces() { m_containerEntityInterface = AZ::Interface::Get(); AZ_Assert( m_containerEntityInterface, "Prefab - PrefabFocusHandler - " "Container Entity Interface could not be found. " "Check that it is being correctly initialized."); m_focusModeInterface = AZ::Interface::Get(); AZ_Assert( m_focusModeInterface, "Prefab - PrefabFocusHandler - " "Focus Mode Interface could not be found. " "Check that it is being correctly initialized."); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) { // Initialize Undo Batch object ScopedUndoBatch undoBatch("Edit Prefab"); // Clear selection { const EntityIdList selectedEntities = EntityIdList{}; auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection"); selectionUndo->SetParent(undoBatch.GetUndoBatch()); ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities); } // Edit Prefab { auto editUndo = aznew PrefabFocusUndo("Edit Prefab"); editUndo->Capture(entityId); editUndo->SetParent(undoBatch.GetUndoBatch()); ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo); } return AZ::Success(); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) { if (index < 0 || index >= m_instanceFocusHierarchy.size()) { return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); } InstanceOptionalReference focusedInstance = m_instanceFocusHierarchy[index]; FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId()); return AZ::Success(); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) { InstanceOptionalReference focusedInstance; if (!entityId.IsValid()) { PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); if (!prefabEditorEntityOwnershipInterface) { return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error " "(PrefabEditorEntityOwnershipInterface unavailable).")); } focusedInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); } else { focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); } return FocusOnPrefabInstance(focusedInstance); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance) { if (!focusedInstance.has_value()) { return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on.")); } // Close all container entities in the old path. CloseInstanceContainers(m_instanceFocusHierarchy); m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); AZ::EntityId containerEntityId; if (focusedInstance->get().GetParentInstance() != AZStd::nullopt) { containerEntityId = focusedInstance->get().GetContainerEntityId(); } else { containerEntityId = AZ::EntityId(); } // Focus on the descendants of the container entity in the Editor, if the interface is initialized. if (m_focusModeInterface) { m_focusModeInterface->SetFocusRoot(containerEntityId); } // Refresh path variables. RefreshInstanceFocusList(); RefreshInstanceFocusPath(); // Open all container entities in the new path. OpenInstanceContainers(m_instanceFocusHierarchy); PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); return AZ::Success(); } TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_focusedTemplateId; } InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance( [[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_focusedInstance; } AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { if (!m_focusedInstance.has_value()) { // PrefabFocusHandler has not been initialized yet. return AZ::EntityId(); } return m_focusedInstance->get().GetContainerEntityId(); } bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const { if (!m_focusedInstance.has_value()) { // PrefabFocusHandler has not been initialized yet. return false; } if (!entityId.IsValid()) { return false; } InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_instanceFocusPath; } const int PrefabFocusHandler::GetPrefabFocusPathLength([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return aznumeric_cast(m_instanceFocusHierarchy.size()); } void PrefabFocusHandler::OnContextReset() { // Clear the old focus vector m_instanceFocusHierarchy.clear(); // Focus on the root prefab (AZ::EntityId() will default to it) FocusOnPrefabInstanceOwningEntityId(AZ::EntityId()); } void PrefabFocusHandler::OnEntityInfoUpdatedName(AZ::EntityId entityId, [[maybe_unused]]const AZStd::string& name) { // Determine if the entityId is the container for any of the instances in the vector auto result = AZStd::find_if( m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(), [entityId](const InstanceOptionalReference& instance) { return (instance->get().GetContainerEntityId() == entityId); } ); if (result != m_instanceFocusHierarchy.end()) { // Refresh the path and notify changes. RefreshInstanceFocusPath(); PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); } } void PrefabFocusHandler::OnPrefabInstancePropagationEnd() { // Refresh the path and notify changes in case propagation updated any container names. RefreshInstanceFocusPath(); PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); } void PrefabFocusHandler::RefreshInstanceFocusList() { m_instanceFocusHierarchy.clear(); AZStd::list instanceFocusList; InstanceOptionalReference currentInstance = m_focusedInstance; while (currentInstance.has_value()) { m_instanceFocusHierarchy.emplace_back(currentInstance); currentInstance = currentInstance->get().GetParentInstance(); } // Invert the vector, since we need the top instance to be at index 0 AZStd::reverse(m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end()); } void PrefabFocusHandler::RefreshInstanceFocusPath() { m_instanceFocusPath.clear(); for (const InstanceOptionalReference& instance : m_instanceFocusHierarchy) { m_instanceFocusPath.Append(instance->get().GetContainerEntity()->get().GetName()); } } void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector& instances) const { // If this is called outside the Editor, this interface won't be initialized. if (!m_containerEntityInterface) { return; } for (const InstanceOptionalReference& instance : instances) { if (instance.has_value()) { m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), true); } } } void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector& instances) const { // If this is called outside the Editor, this interface won't be initialized. if (!m_containerEntityInterface) { return; } for (const InstanceOptionalReference& instance : instances) { if (instance.has_value()) { m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), false); } } } } // namespace AzToolsFramework::Prefab