LYN-8633 | Read-Only Entities - Entity Outliner behavior changes (#6230)

* Add invalid entityId check to IsReadOnly

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

* Ensure read-only entities editing is limited (no renaming, limited drag/drop, no asset drop on them)

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

* Removing redundant class definition in wrong namespace.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
This commit is contained in:
Danilo Aimini
2021-12-07 16:58:29 -08:00
committed by GitHub
parent 3f86f31f2a
commit 3a7d9f9b3a
7 changed files with 106 additions and 15 deletions
@@ -42,6 +42,11 @@ namespace AzToolsFramework
bool ReadOnlyEntitySystemComponent::IsReadOnly(const AZ::EntityId& entityId)
{
if (!entityId.IsValid())
{
return false;
}
if (!m_readOnlystates.contains(entityId))
{
QueryReadOnlyStateForEntity(entityId);
@@ -117,8 +117,12 @@ namespace AzToolsFramework
ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId);
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(m_editorEntityUiInterface != nullptr,
"EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize.");
AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize.");
m_readOnlyEntityPublicInterface = AZ::Interface<AzToolsFramework::ReadOnlyEntityPublicInterface>::Get();
AZ_Assert(
(m_readOnlyEntityPublicInterface != nullptr),
"EntityOutlinerListModel requires a ReadOnlyEntityPublicInterface instance on Initialize.");
}
int EntityOutlinerListModel::rowCount(const QModelIndex& parent) const
@@ -451,9 +455,15 @@ namespace AzToolsFramework
if (value.canConvert<Qt::CheckState>())
{
const auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
// Disable lock and visibility toggling if the UI Handler blocked it.
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
bool canToggleLockVisibility = !entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId);
// Disable lock and visibility toggling for read-only entities.
bool isReadOnly = m_readOnlyEntityPublicInterface->IsReadOnly(entityId);
if (canToggleLockVisibility && !isReadOnly)
{
switch (index.column())
{
@@ -535,6 +545,15 @@ namespace AzToolsFramework
return Qt::ItemIsDropEnabled;
}
AZ::EntityId entityId = GetEntityFromIndex(index);
// Only allow renaming the entity if the UI Handler did not block it.
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
bool canRename = !entityUiHandler || entityUiHandler->CanRename(entityId);
// Disable renaming for read-only entities.
bool isReadOnly = m_readOnlyEntityPublicInterface->IsReadOnly(entityId);
Qt::ItemFlags itemFlags = QAbstractItemModel::flags(index);
switch (index.column())
{
@@ -544,7 +563,21 @@ namespace AzToolsFramework
break;
case ColumnName:
itemFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsEditable;
if (canRename && !isReadOnly)
{
itemFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsEditable;
}
else
{
if (isReadOnly)
{
itemFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
else
{
itemFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled;
}
}
break;
default:
@@ -552,7 +585,8 @@ namespace AzToolsFramework
break;
}
if (AZ::EntityId entityId = GetEntityFromIndex(index); !m_focusModeInterface->IsInFocusSubTree(entityId))
// Disable entities outside the focus subtree.
if (!m_focusModeInterface->IsInFocusSubTree(entityId))
{
itemFlags &= !Qt::ItemIsEnabled;
}
@@ -773,14 +807,21 @@ namespace AzToolsFramework
[[maybe_unused]] int column,
const QModelIndex& parent) const
{
// Disable dropping assets on closed container entities.
AZ::EntityId parentId = GetEntityFromIndex(parent);
// Disable dropping assets on closed container entities.
if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
!containerEntityInterface->IsContainerOpen(parentId))
{
return false;
}
// Disable dropping assets on read-only entities.
if (m_readOnlyEntityPublicInterface->IsReadOnly(parentId))
{
return false;
}
if (data->hasFormat(AssetBrowser::AssetBrowserEntry::GetMimeType()))
{
return DecodeAssetMimeData(data);
@@ -2211,6 +2252,13 @@ namespace AzToolsFramework
QColor transparentColor(0, 0, 0, 0);
checkboxPalette.setColor(QPalette::ColorRole::Window, transparentColor);
// Disable hover rendering for read-only entities
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
if (m_readOnlyEntityPublicInterface->IsReadOnly(entityId))
{
isHovered = false;
}
// We're only using these check boxes as renderers so their actual state doesn't matter.
// We can set it right before we draw using information from the model data.
if (index.column() == EntityOutlinerListModel::ColumnVisibilityToggle)
@@ -290,6 +290,7 @@ namespace AzToolsFramework
EditorEntityUiInterface* m_editorEntityUiInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
};
class EntityOutlinerCheckBox
@@ -14,6 +14,7 @@
#include <AzCore/std/algorithm.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
@@ -35,11 +36,14 @@ namespace AzToolsFramework
setHeaderHidden(true);
m_editorEntityFrameworkInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert((m_editorEntityFrameworkInterface != nullptr),
"EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction.");
m_readOnlyEntityPublicInterface = AZ::Interface<AzToolsFramework::ReadOnlyEntityPublicInterface>::Get();
AZ_Assert(
(m_readOnlyEntityPublicInterface != nullptr),
"EntityOutlinerTreeView requires a ReadOnlyEntityPublicInterface instance on Construction.");
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
@@ -157,11 +161,22 @@ namespace AzToolsFramework
void EntityOutlinerTreeView::startDrag(Qt::DropActions supportedActions)
{
QModelIndex index = indexAt(m_queuedMouseEvent->pos());
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
AZ::EntityId parentEntityId;
EditorEntityInfoRequestBus::EventResult(parentEntityId, entityId, &EditorEntityInfoRequestBus::Events::GetParent);
// If the entity is parented to a read-only entity, cancel the drag operation.
if (m_readOnlyEntityPublicInterface->IsReadOnly(parentEntityId))
{
return;
}
//if we are attempting to drag an unselected item then we must special case drag and drop logic
//QAbstractItemView::startDrag only supports selected items
if (m_queuedMouseEvent)
{
QModelIndex index = indexAt(m_queuedMouseEvent->pos());
if (!index.isValid() || index.column() != 0)
{
return;
@@ -26,6 +26,7 @@ class QMouseEvent;
namespace AzToolsFramework
{
class EditorEntityUiInterface;
class ReadOnlyEntityPublicInterface;
//! This class largely exists to emit events for the OutlinerWidget to listen in on.
//! The logic for these events is best off not happening within the tree itself,
@@ -92,7 +93,8 @@ namespace AzToolsFramework
QModelIndex m_currentHoveredIndex;
EditorEntityUiInterface* m_editorEntityFrameworkInterface;
EditorEntityUiInterface* m_editorEntityFrameworkInterface = nullptr;
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
};
}
@@ -26,6 +26,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
@@ -156,6 +157,11 @@ namespace AzToolsFramework
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize.");
m_readOnlyEntityPublicInterface = AZ::Interface<AzToolsFramework::ReadOnlyEntityPublicInterface>::Get();
AZ_Assert(
(m_readOnlyEntityPublicInterface != nullptr),
"EntityOutlinerListModel requires a ReadOnlyEntityPublicInterface instance on Initialize.");
m_gui = new Ui::EntityOutlinerWidgetUI();
m_gui->setupUi(this);
@@ -585,9 +591,15 @@ namespace AzToolsFramework
if (m_selectedEntityIds.size() == 1)
{
auto entityId = m_selectedEntityIds.front();
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanRename(entityId))
// Only allow renaming the entity if the UI Handler did not block it.
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
bool canRename = !entityUiHandler || entityUiHandler->CanRename(entityId);
// Disable renaming for read-only entities.
bool isReadOnly = m_readOnlyEntityPublicInterface->IsReadOnly(entityId);
if (canRename && !isReadOnly)
{
contextMenu->addAction(m_actionToRenameSelection);
}
@@ -717,9 +729,15 @@ namespace AzToolsFramework
if (m_selectedEntityIds.size() == 1)
{
auto entityId = m_selectedEntityIds.front();
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanRename(entityId))
// Only allow renaming the entity if the UI Handler did not block it.
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
bool canRename = !entityUiHandler || entityUiHandler->CanRename(entityId);
// Disable renaming for read-only entities.
bool isReadOnly = m_readOnlyEntityPublicInterface->IsReadOnly(entityId);
if (canRename && !isReadOnly)
{
const QModelIndex proxyIndex = GetIndexFromEntityId(entityId);
if (proxyIndex.isValid())
@@ -43,6 +43,7 @@ namespace AzToolsFramework
class EntityOutlinerListModel;
class EntityOutlinerContainerProxyModel;
class EntityOutlinerSortFilterProxyModel;
class ReadOnlyEntityPublicInterface;
namespace EntityOutliner
{
@@ -204,6 +205,7 @@ namespace AzToolsFramework
bool m_sortContentQueued;
EditorEntityUiInterface* m_editorEntityUiInterface = nullptr;
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
};
}