LYN-7333 | Fix multiple selection by dragging to take focus mode and containers into account. (#4620)

* Change FocusModeNotificationBus's OnEditorFocusChanged arguments to also pass the previous focus root entity id.

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

* Add focus mode and container entity states to the visibility cache for the viewport. Use that data to correctly select entities when a rect is dragged on the viewport.

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

* Minor code adjustments

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

* Minor fixes and optimizations

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
This commit is contained in:
Danilo Aimini
2021-10-13 14:17:09 -07:00
committed by GitHub
parent 5ba30f9a37
commit f7e2d07a4b
7 changed files with 136 additions and 16 deletions
@@ -28,8 +28,10 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
//! Triggered when the editor focus is changed to a different entity.
//! @param entityId The entity the focus has been moved to.
virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0;
//! @param previousFocusEntityId The entity the focus has been moved from.
//! @param newFocusEntityId The entity the focus has been moved to.
virtual void OnEditorFocusChanged(
[[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) {}
protected:
~FocusModeNotifications() = default;
@@ -71,8 +71,9 @@ namespace AzToolsFramework
return;
}
AZ::EntityId previousFocusEntityId = m_focusRoot;
m_focusRoot = entityId;
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot);
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot);
if (auto tracker = AZ::Interface<ViewportEditorModeTrackerInterface>::Get();
tracker != nullptr)
@@ -313,7 +313,8 @@ namespace AzToolsFramework
StyledTreeView::StartCustomDrag(indexListSorted, supportedActions);
}
void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId)
void EntityOutlinerTreeView::OnEditorFocusChanged(
[[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId)
{
viewport()->repaint();
}
@@ -64,7 +64,7 @@ namespace AzToolsFramework
void leaveEvent(QEvent* event) override;
// FocusModeNotificationBus overrides ...
void OnEditorFocusChanged(AZ::EntityId entityId) override;
void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override;
//! Renders the left side of the item: appropriate background, branch lines, icons.
void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override;
@@ -407,7 +407,7 @@ namespace AzToolsFramework
const AzFramework::CameraState cameraState = GetCameraState(viewportId);
for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex)
{
if (entityDataCache.IsVisibleEntityLocked(entityCacheIndex) || !entityDataCache.IsVisibleEntityVisible(entityCacheIndex))
if (!entityDataCache.IsVisibleEntitySelectableInViewport(entityCacheIndex))
{
continue;
}
@@ -9,7 +9,9 @@
#include "EditorVisibleEntityDataCache.h"
#include <AzCore/std/sort.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityModel.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <Entity/EditorEntityHelpers.h>
@@ -21,13 +23,23 @@ namespace AzToolsFramework
using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType;
EntityData() = default;
EntityData(AZ::EntityId entityId, const AZ::Transform& worldFromLocal, bool locked, bool visible, bool selected, bool iconHidden);
EntityData(
AZ::EntityId entityId,
const AZ::Transform& worldFromLocal,
bool locked,
bool visible,
bool inFocus,
bool descendantOfClosedContainer,
bool selected,
bool iconHidden);
AZ::Transform m_worldFromLocal;
AZ::EntityId m_entityId;
ComponentEntityAccentType m_accent = ComponentEntityAccentType::None;
bool m_locked = false;
bool m_visible = true;
bool m_inFocus = true;
bool m_descendantOfClosedContainer = false;
bool m_selected = false;
bool m_iconHidden = false;
};
@@ -57,12 +69,16 @@ namespace AzToolsFramework
const AZ::Transform& worldFromLocal,
const bool locked,
const bool visible,
const bool inFocus,
const bool descendantOfClosedContainer,
const bool selected,
const bool iconHidden)
: m_worldFromLocal(worldFromLocal)
, m_entityId(entityId)
, m_locked(locked)
, m_visible(visible)
, m_inFocus(inFocus)
, m_descendantOfClosedContainer(descendantOfClosedContainer)
, m_selected(selected)
, m_iconHidden(iconHidden)
{
@@ -106,6 +122,18 @@ namespace AzToolsFramework
bool locked = false;
EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
bool inFocus = false;
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get())
{
inFocus = focusModeInterface->IsInFocusSubTree(entityId);
}
bool descendantOfClosedContainer = false;
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
{
descendantOfClosedContainer = containerEntityInterface->IsUnderClosedContainerEntity(entityId);
}
bool iconHidden = false;
EditorEntityIconComponentRequestBus::EventResult(
iconHidden, entityId, &EditorEntityIconComponentRequests::IsEntityIconHiddenInViewport);
@@ -113,7 +141,7 @@ namespace AzToolsFramework
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM);
return { entityId, worldFromLocal, locked, visible, IsSelected(entityId), iconHidden };
return { entityId, worldFromLocal, locked, visible, inFocus, descendantOfClosedContainer, IsSelected(entityId), iconHidden };
}
EditorVisibleEntityDataCache::EditorVisibleEntityDataCache()
@@ -126,10 +154,17 @@ namespace AzToolsFramework
EntitySelectionEvents::Bus::Router::BusRouterConnect();
EditorEntityIconComponentNotificationBus::Router::BusRouterConnect();
ToolsApplicationNotificationBus::Handler::BusConnect();
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId);
FocusModeNotificationBus::Handler::BusConnect(editorEntityContextId);
}
EditorVisibleEntityDataCache::~EditorVisibleEntityDataCache()
{
FocusModeNotificationBus::Handler::BusDisconnect();
ContainerEntityNotificationBus::Handler::BusDisconnect();
ToolsApplicationNotificationBus::Handler::BusDisconnect();
EditorEntityIconComponentNotificationBus::Router::BusRouterDisconnect();
EntitySelectionEvents::Bus::Router::BusRouterDisconnect();
@@ -260,7 +295,10 @@ namespace AzToolsFramework
bool EditorVisibleEntityDataCache::IsVisibleEntitySelectableInViewport(size_t index) const
{
return m_impl->m_visibleEntityDatas[index].m_visible && !m_impl->m_visibleEntityDatas[index].m_locked;
return m_impl->m_visibleEntityDatas[index].m_visible
&& !m_impl->m_visibleEntityDatas[index].m_locked
&& m_impl->m_visibleEntityDatas[index].m_inFocus
&& !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
}
AZStd::optional<size_t> EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const
@@ -371,4 +409,72 @@ namespace AzToolsFramework
m_impl->m_visibleEntityDatas[entityIndex.value()].m_iconHidden = iconHidden;
}
}
void EditorVisibleEntityDataCache::OnContainerEntityStatusChanged(AZ::EntityId entityId, [[maybe_unused]] bool open)
{
// Get container descendants
AzToolsFramework::EntityIdList descendantIds;
AZ::TransformBus::EventResult(descendantIds, entityId, &AZ::TransformBus::Events::GetAllDescendants);
// Update cached values
if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
{
for (AZ::EntityId descendantId : descendantIds)
{
if (AZStd::optional<size_t> entityIndex = GetVisibleEntityIndexFromId(descendantId))
{
m_impl->m_visibleEntityDatas[entityIndex.value()].m_descendantOfClosedContainer =
containerEntityInterface->IsUnderClosedContainerEntity(descendantId);
}
}
}
}
void EditorVisibleEntityDataCache::OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId)
{
if (previousFocusEntityId.IsValid() && newFocusEntityId.IsValid())
{
// Get previous focus root descendants
AzToolsFramework::EntityIdList previousDescendantIds;
AZ::TransformBus::EventResult(previousDescendantIds, previousFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants);
// Get new focus root descendants
AzToolsFramework::EntityIdList newDescendantIds;
AZ::TransformBus::EventResult(newDescendantIds, newFocusEntityId, &AZ::TransformBus::Events::GetAllDescendants);
// Merge EntityId Lists to avoid refreshing values twice
AzToolsFramework::EntityIdSet descendantsSet;
descendantsSet.insert(previousFocusEntityId);
descendantsSet.insert(newFocusEntityId);
descendantsSet.insert(previousDescendantIds.begin(), previousDescendantIds.end());
descendantsSet.insert(newDescendantIds.begin(), newDescendantIds.end());
// Update cached values
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get())
{
for (const AZ::EntityId& descendantId : descendantsSet)
{
if (AZStd::optional<size_t> entityIndex = GetVisibleEntityIndexFromId(descendantId))
{
m_impl->m_visibleEntityDatas[entityIndex.value()].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId);
}
}
}
}
else
{
// If either focus was the invalid entity, refresh all entities.
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get())
{
for (size_t entityIndex = 0; entityIndex < m_impl->m_visibleEntityDatas.size(); ++entityIndex)
{
if (AZ::EntityId descendantId = GetVisibleEntityId(entityIndex); descendantId.IsValid())
{
m_impl->m_visibleEntityDatas[entityIndex].m_inFocus = focusModeInterface->IsInFocusSubTree(descendantId);
}
}
}
}
}
} // namespace AzToolsFramework
@@ -11,6 +11,8 @@
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/optional.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h>
#include <AzToolsFramework/FocusMode/FocusModeNotificationBus.h>
#include <AzToolsFramework/ToolsComponents/EditorEntityIconComponentBus.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
#include <AzToolsFramework/ToolsComponents/EditorSelectionAccentSystemComponent.h>
@@ -28,6 +30,8 @@ namespace AzToolsFramework
, private EntitySelectionEvents::Bus::Router
, private EditorEntityIconComponentNotificationBus::Router
, private ToolsApplicationNotificationBus::Handler
, private ContainerEntityNotificationBus::Handler
, private FocusModeNotificationBus::Handler
{
public:
EditorVisibleEntityDataCache();
@@ -58,28 +62,34 @@ namespace AzToolsFramework
void AddEntityIds(const EntityIdList& entityIds);
private:
// ToolsApplicationNotificationBus
// ToolsApplicationNotificationBus overrides ...
void AfterUndoRedo() override;
// EditorEntityVisibilityNotificationBus
// EditorEntityVisibilityNotificationBus overrides ...
void OnEntityVisibilityChanged(bool visibility) override;
// EditorEntityLockComponentNotificationBus
// EditorEntityLockComponentNotificationBus overrides ...
void OnEntityLockChanged(bool locked) override;
// TransformNotificationBus
// TransformNotificationBus overrides ...
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
// EditorComponentSelectionNotificationsBus
// EditorComponentSelectionNotificationsBus overrides ...
void OnAccentTypeChanged(EntityAccentType accent) override;
// EntitySelectionEvents::Bus
// EntitySelectionEvents::Bus overrides ...
void OnSelected() override;
void OnDeselected() override;
// EditorEntityIconComponentNotificationBus
// EditorEntityIconComponentNotificationBus overrides ...
void OnEntityIconChanged(const AZ::Data::AssetId& entityIconAssetId) override;
// ContainerEntityNotificationBus overrides ...
void OnContainerEntityStatusChanged(AZ::EntityId entityId, bool open) override;
// FocusModeNotificationBus overrides ...
void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override;
class EditorVisibleEntityDataCacheImpl;
AZStd::unique_ptr<EditorVisibleEntityDataCacheImpl> m_impl; //!< Internal representation of entity data cache.
};