LYN-7195 + LYN-7185 + LYN-5301 | Hide viewport helpers for entities out of focus + selection shortcut adjustments (#4615)

* Light refactoring of selection logic. Only draw helpers for selectable entities according to Editor Focus Mode and Container Entity systems.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* When Escape is pressed, clear the Prefab Focus.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Alter Ctrl+A and Ctrl+Shift+I to take editor focus mode and container entity behaviors into account.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Remove redundant comments and reduce footprint of tests.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Introduce loop protection, as GetParentId is known to loop in some situations possibly causing timeouts.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
This commit is contained in:
Danilo Aimini
2021-10-12 15:53:38 -07:00
committed by GitHub
parent 3b78132833
commit 1cb26a31f8
8 changed files with 130 additions and 12 deletions
@@ -65,6 +65,9 @@ namespace AzToolsFramework
//! @return An error message if any container was registered for the context, success otherwise.
virtual ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) = 0;
//! Returns true if one of the ancestors of entityId is a closed container entity.
virtual bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const = 0;
};
} // namespace AzToolsFramework
@@ -102,8 +102,17 @@ namespace AzToolsFramework
AZ::EntityId ContainerEntitySystemComponent::FindHighestSelectableEntity(AZ::EntityId entityId) const
{
if (!entityId.IsValid())
{
return entityId;
}
// Return the highest closed container, or the entity if none is found.
AZ::EntityId highestSelectableEntityId = entityId;
// Skip the queried entity, as we only want to check its ancestors.
AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
// Go up the hierarchy until you hit the root
while (entityId.IsValid())
{
@@ -152,4 +161,40 @@ namespace AzToolsFramework
return AZ::Success();
}
bool ContainerEntitySystemComponent::IsUnderClosedContainerEntity(AZ::EntityId entityId) const
{
if (!entityId.IsValid())
{
return false;
}
// Skip the queried entity, as we only want to check its ancestors.
AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
// Go up the hierarchy until you hit the root.
while (entityId.IsValid())
{
if (!IsContainerOpen(entityId))
{
// One of the ancestors is a container and it's closed.
return true;
}
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
if (parentId == entityId)
{
// In some circumstances, querying a root level entity with GetParentId will return
// the entity itself instead of an invalid entityId.
break;
}
entityId = parentId;
}
// All ancestors are either regular entities or open containers.
return false;
}
} // namespace AzToolsFramework
@@ -48,6 +48,7 @@ namespace AzToolsFramework
bool IsContainerOpen(AZ::EntityId entityId) const override;
AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const override;
ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) override;
bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const override;
// EditorEntityContextNotificationBus overrides ...
void OnEntityStreamLoadSuccess() override;
@@ -16,9 +16,11 @@
#include <AzCore/RTTI/AttributeReader.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Commands/EntityStateCommand.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/Slice/SliceMetadataEntityContextBus.h>
#include <AzToolsFramework/Slice/SliceUtilities.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
@@ -588,15 +590,40 @@ namespace AzToolsFramework
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
// Detect if the Entity is Visible
bool visible = false;
EditorEntityInfoRequestBus::EventResult(
visible, entityId, &EditorEntityInfoRequestBus::Events::IsVisible);
bool locked = false;
EditorEntityInfoRequestBus::EventResult(
locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
if (!visible)
{
return false;
}
return visible && !locked;
// Detect if the Entity is Locked
bool locked = false;
EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
if (locked)
{
return false;
}
// Detect if the Entity is part of the Editor Focus
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
!focusModeInterface->IsInFocusSubTree(entityId))
{
return false;
}
// Detect if the Entity is a descendant of a closed container
if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
containerEntityInterface->IsUnderClosedContainerEntity(entityId))
{
return false;
}
return true;
}
static void SetEntityLockStateRecursively(
@@ -137,6 +137,7 @@ namespace AzToolsFramework
}
EditorContextMenuBus::Handler::BusConnect();
EditorEventsBus::Handler::BusConnect();
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabIntegrationInterface>::Register(this);
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension);
@@ -147,6 +148,7 @@ namespace AzToolsFramework
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect();
AZ::Interface<PrefabIntegrationInterface>::Unregister(this);
PrefabInstanceContainerNotificationBus::Handler::BusDisconnect();
EditorEventsBus::Handler::BusDisconnect();
EditorContextMenuBus::Handler::BusDisconnect();
}
@@ -313,6 +315,11 @@ namespace AzToolsFramework
}
}
void PrefabIntegrationManager::OnEscape()
{
s_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
}
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
{
auto instantiatePrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(sourceFilePath, parentId, position);
@@ -51,6 +51,7 @@ namespace AzToolsFramework
class PrefabIntegrationManager final
: public EditorContextMenuBus::Handler
, public EditorEventsBus::Handler
, public AssetBrowser::AssetBrowserSourceDropBus::Handler
, public PrefabInstanceContainerNotificationBus::Handler
, public PrefabIntegrationInterface
@@ -64,19 +65,22 @@ namespace AzToolsFramework
static void Reflect(AZ::ReflectContext* context);
// EditorContextMenuBus...
// EditorContextMenuBus overrides ...
int GetMenuPosition() const override;
AZStd::string GetMenuIdentifier() const override;
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
// EntityOutlinerSourceDropHandlingBus...
// EditorEventsBus overrides ...
void OnEscape();
// EntityOutlinerSourceDropHandlingBus overrides ...
void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override;
// PrefabInstanceContainerNotificationBus...
// PrefabInstanceContainerNotificationBus overrides ...
void OnPrefabComponentActivate(AZ::EntityId entityId) override;
void OnPrefabComponentDeactivate(AZ::EntityId entityId) override;
// PrefabIntegrationInterface...
// PrefabIntegrationInterface overrides ...
AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override;
int ExecuteClosePrefabDialog(TemplateId templateId) override;
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
@@ -187,15 +187,14 @@ namespace AzToolsFramework
}
// Verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor))
if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor))
{
return AZ::EntityId();
}
// Container Entity support - if the entity that is being selected is part of a closed container,
// change the selection to the container instead.
ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
if (containerEntityInterface)
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
{
return containerEntityInterface->FindHighestSelectableEntity(entityIdUnderCursor);
}
@@ -217,7 +216,7 @@ namespace AzToolsFramework
{
const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex))
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId))
{
continue;
}
@@ -263,4 +262,24 @@ namespace AzToolsFramework
}
}
}
bool EditorHelpers::IsSelectableInViewport(AZ::EntityId entityId)
{
return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId);
}
bool EditorHelpers::IsSelectableAccordingToFocusMode(AZ::EntityId entityId)
{
return m_focusModeInterface->IsInFocusSubTree(entityId);
}
bool EditorHelpers::IsSelectableAccordingToContainerEntities(AZ::EntityId entityId)
{
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
{
return !containerEntityInterface->IsUnderClosedContainerEntity(entityId);
}
return true;
}
} // namespace AzToolsFramework
@@ -58,7 +58,19 @@ namespace AzToolsFramework
AzFramework::DebugDisplayRequests& debugDisplay,
const AZStd::function<bool(AZ::EntityId)>& showIconCheck);
//! Returns whether the entityId can be selected in the viewport according
//! to the current Editor Focus Mode and Container Entity setup.
bool IsSelectableInViewport(AZ::EntityId entityId);
private:
//! Returns whether the entityId can be selected in the viewport according
//! to the current Editor Focus Mode setup.
bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId);
//! Returns whether the entityId can be selected in the viewport according
//! to the current Container Entityu setup.
bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId);
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
const FocusModeInterface* m_focusModeInterface = nullptr;
};