diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.cpp index 7536e1e8a5..f3d6ec041c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.cpp @@ -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); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index 284caaa7a2..0b882324b5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -117,8 +117,12 @@ namespace AzToolsFramework ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId); m_editorEntityUiInterface = AZ::Interface::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::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()) { 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::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()); + 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) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index b785dcb31e..f099ed504a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -290,6 +290,7 @@ namespace AzToolsFramework EditorEntityUiInterface* m_editorEntityUiInterface = nullptr; FocusModeInterface* m_focusModeInterface = nullptr; + ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr; }; class EntityOutlinerCheckBox diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index 0609f8113d..857fdb5f80 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -35,11 +36,14 @@ namespace AzToolsFramework setHeaderHidden(true); m_editorEntityFrameworkInterface = AZ::Interface::Get(); - AZ_Assert((m_editorEntityFrameworkInterface != nullptr), "EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction."); - + m_readOnlyEntityPublicInterface = AZ::Interface::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::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; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 42b42a59b4..9262da9a73 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -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; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index d6e9cac754..2e3955bed7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,11 @@ namespace AzToolsFramework m_editorEntityUiInterface = AZ::Interface::Get(); AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize."); + m_readOnlyEntityPublicInterface = AZ::Interface::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()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx index 431861ab67..b0925b5219 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.hxx @@ -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; }; }