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:
+4
-2
@@ -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;
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
+2
-1
@@ -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();
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
+109
-3
@@ -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
|
||||
|
||||
+17
-7
@@ -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.
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user