From 476e66d902bd31247f792d6615576ffdd69b5d1b Mon Sep 17 00:00:00 2001
From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
Date: Mon, 6 Dec 2021 12:16:16 -0800
Subject: [PATCH] LYN-8631 + LYN-8632 | Display appropriate read-only icons and
procedural prefabs ui in the Outliner (#6160)
* First step of procedural prefab styling and read-only registration.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* WIP - Introduce read-only handler for procedural prefabs (not hooked up)
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Introduce read-only entity interface, handler and unit tests.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Introduce temp read-only icon, use new icon hierarchy in Outliner.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Switch from a push paradigm to a pull paradigm - handlers get to implement logic to determine if an entity should be read-only. This allows multiple systems to weigh into whether an entity is read-only.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Fixed to missing call in test
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Post-rebase fixes to class name changes
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Display a lock icon on top of the entity icon in the Entity Outliner. This icon is added programmatically, which prevents having to alter all Outliner icons and future-proofs this functionality.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Minor style changes to procedural prefabs in the Outliner (use white icon)
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Ensure cache is refreshed when the handler is created, and also whenever the focus changes.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Remove Procedural Prefab setreg that was added in the wrong place
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Remove redundant function
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Fix spacing issue caused by rebase
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Change tooltip so that it accurately says "inspect" instead of "edit" for procedural prefabs.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
* Address minor styling issues mentioned in PR.
Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
---
.../Entity/prefab_edit_open_readonly.svg | 3 +
.../AzQtComponents/Images/Entity/readonly.svg | 3 +
.../AzQtComponents/Images/resources.qrc | 2 +
.../ReadOnly/ReadOnlyEntitySystemComponent.h | 4 +-
.../Prefab/PrefabFocusHandler.cpp | 16 +++++
.../Prefab/PrefabFocusHandler.h | 2 +
.../Prefab/PrefabPublicHandler.cpp | 12 ++++
.../Prefab/PrefabPublicHandler.h | 1 +
.../Prefab/PrefabPublicInterface.h | 7 ++
.../UI/Outliner/EntityOutlinerListModel.cpp | 37 ++++++++--
.../UI/Outliner/EntityOutlinerListModel.hxx | 15 +++-
.../UI/Prefab/PrefabIntegrationManager.cpp | 10 ++-
.../UI/Prefab/PrefabIntegrationManager.h | 13 +++-
.../UI/Prefab/PrefabUiHandler.cpp | 11 ---
.../UI/Prefab/PrefabUiHandler.h | 26 +++----
.../ProceduralPrefabReadOnlyHandler.cpp | 68 +++++++++++++++++++
.../ProceduralPrefabReadOnlyHandler.h | 43 ++++++++++++
.../Procedural/ProceduralPrefabUiHandler.cpp | 32 +++++++++
.../Procedural/ProceduralPrefabUiHandler.h | 36 ++++++++++
.../aztoolsframework_files.cmake | 4 ++
.../Entity/ReadOnly/ReadOnlyEntityTests.cpp | 18 +++++
21 files changed, 325 insertions(+), 38 deletions(-)
create mode 100644 Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open_readonly.svg
create mode 100644 Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/readonly.svg
create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.cpp
create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h
create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.cpp
create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.h
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open_readonly.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open_readonly.svg
new file mode 100644
index 0000000000..9f16c88211
--- /dev/null
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/prefab_edit_open_readonly.svg
@@ -0,0 +1,3 @@
+
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/readonly.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/readonly.svg
new file mode 100644
index 0000000000..2e942e87f8
--- /dev/null
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Entity/readonly.svg
@@ -0,0 +1,3 @@
+
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc
index 15049aeb69..7e7fafc9ee 100644
--- a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc
@@ -7,7 +7,9 @@
Entity/prefab.svg
Entity/prefab_edit.svg
Entity/prefab_edit_open.svg
+ Entity/prefab_edit_open_readonly.svg
Entity/prefab_edit_close.svg
+ Entity/readonly.svg
Level/level.svg
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h
index efc91e9d89..4b8942bcd1 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h
@@ -37,9 +37,9 @@ namespace AzToolsFramework
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
- // ReadOnlyEntityPublicNotifications overrides ...
+ // ReadOnlyEntityPublicInterface overrides ...
bool IsReadOnly(const AZ::EntityId& entityId) override;
-
+
// ReadOnlyEntityQueryInterface overrides ...
void RefreshReadOnlyState(const EntityIdList& entityIds) override;
void RefreshReadOnlyStateForAllEntities() override;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp
index 2ab68bfc10..3426c56c99 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -74,6 +75,13 @@ namespace AzToolsFramework::Prefab
"Prefab - PrefabFocusHandler - "
"Focus Mode Interface could not be found. "
"Check that it is being correctly initialized.");
+
+ m_readOnlyEntityQueryInterface = AZ::Interface::Get();
+ AZ_Assert(
+ m_readOnlyEntityQueryInterface,
+ "Prefab - PrefabFocusHandler - "
+ "ReadOnly Entity Query Interface could not be found. "
+ "Check that it is being correctly initialized.");
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
@@ -186,6 +194,8 @@ namespace AzToolsFramework::Prefab
// Close all container entities in the old path.
CloseInstanceContainers(m_instanceFocusHierarchy);
+ AZ::EntityId previousContainerEntityId = m_focusedInstanceContainerEntityId;
+
// Do not store the container for the root instance, use an invalid EntityId instead.
m_focusedInstanceContainerEntityId = focusedInstance->get().GetParentInstance().has_value() ? focusedInstance->get().GetContainerEntityId() : AZ::EntityId();
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
@@ -201,6 +211,12 @@ namespace AzToolsFramework::Prefab
m_focusModeInterface->SetFocusRoot(containerEntityId);
}
+ // Refresh the read-only cache, if the interface is initialized.
+ if (m_readOnlyEntityQueryInterface)
+ {
+ m_readOnlyEntityQueryInterface->RefreshReadOnlyState({ previousContainerEntityId, m_focusedInstanceContainerEntityId });
+ }
+
// Refresh path variables.
RefreshInstanceFocusList();
RefreshInstanceFocusPath();
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h
index b02106d69d..4f3dbdd708 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h
@@ -22,6 +22,7 @@ namespace AzToolsFramework
{
class ContainerEntityInterface;
class FocusModeInterface;
+ class ReadOnlyEntityQueryInterface;
}
namespace AzToolsFramework::Prefab
@@ -93,6 +94,7 @@ namespace AzToolsFramework::Prefab
ContainerEntityInterface* m_containerEntityInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
+ ReadOnlyEntityQueryInterface* m_readOnlyEntityQueryInterface = nullptr;
};
} // namespace AzToolsFramework::Prefab
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
index e5530b49f4..dfabd42cd1 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
@@ -918,6 +918,18 @@ namespace AzToolsFramework
}
}
+ bool PrefabPublicHandler::IsOwnedByProceduralPrefabInstance(AZ::EntityId entityId) const
+ {
+ if (InstanceOptionalReference instanceReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
+ instanceReference.has_value())
+ {
+ TemplateReference templateReference = m_prefabSystemComponentInterface->FindTemplate(instanceReference->get().GetTemplateId());
+ return (templateReference.has_value()) && (templateReference->get().IsProcedural());
+ }
+
+ return false;
+ }
+
bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const
{
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h
index dd071ac09f..41c9b4a292 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h
@@ -54,6 +54,7 @@ namespace AzToolsFramework
PrefabOperationResult GenerateUndoNodesForEntityChangeAndUpdateCache(AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) override;
+ bool IsOwnedByProceduralPrefabInstance(AZ::EntityId entityId) const override;
bool IsInstanceContainerEntity(AZ::EntityId entityId) const override;
bool IsLevelInstanceContainerEntity(AZ::EntityId entityId) const override;
AZ::EntityId GetInstanceContainerEntityId(AZ::EntityId entityId) const override;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h
index ede857dd2b..2eb0bfa8e6 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h
@@ -101,6 +101,13 @@ namespace AzToolsFramework
*/
virtual PrefabOperationResult GenerateUndoNodesForEntityChangeAndUpdateCache(
AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) = 0;
+
+ /**
+ * Detects if an entity is owned by a procedural prefab.
+ * @param entityId The entity to query.
+ * @return True if the entity is owned by a procedural prefab instance, false otherwise.
+ */
+ virtual bool IsOwnedByProceduralPrefabInstance(AZ::EntityId entityId) const = 0;
/**
* Detects if an entity is the container entity for its owning prefab instance.
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
index a68a72f00a..284caaa7a2 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
@@ -47,6 +47,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -313,7 +314,7 @@ namespace AzToolsFramework
if (isEditorOnly)
{
- return QIcon(QString(":/Icons/Entity_Editor_Only.svg"));
+ return QIcon(QString(":/Entity/entity_editoronly.svg"));
}
AZ::Entity* entity = nullptr;
@@ -322,10 +323,10 @@ namespace AzToolsFramework
if (!isInitiallyActive)
{
- return QIcon(QString(":/Icons/Entity_Not_Active.svg"));
+ return QIcon(QString(":/Entity/entity_notactive.svg"));
}
- return QIcon(QString(":/Icons/Entity.svg"));
+ return QIcon(QString(":/Entity/entity.svg"));
}
QVariant EntityOutlinerListModel::GetEntityTooltip(const AZ::EntityId& id) const
@@ -1994,9 +1995,13 @@ namespace AzToolsFramework
, m_lockCheckBoxes(parent, "Lock", EntityOutlinerListModel::PartiallyLockedRole, EntityOutlinerListModel::LockedAncestorRole)
{
m_editorEntityFrameworkInterface = AZ::Interface::Get();
-
AZ_Assert((m_editorEntityFrameworkInterface != nullptr),
"EntityOutlinerItemDelegate requires a EditorEntityFrameworkInterface instance on Construction.");
+
+ m_readOnlyEntityPublicInterface = AZ::Interface::Get();
+ AZ_Assert(
+ (m_readOnlyEntityPublicInterface != nullptr),
+ "EntityOutlinerItemDelegate requires a ReadOnlyEntityPublicInterface instance on Construction.");
}
EntityOutlinerItemDelegate::CheckboxGroup::CheckboxGroup(QWidget* parent, AZStd::string prefix,
@@ -2108,6 +2113,12 @@ namespace AzToolsFramework
}
PaintEntityNameAsRichText(painter, customOption, index);
+
+ // Paint Read-Only icon if necessary
+ if (m_readOnlyEntityPublicInterface->IsReadOnly(entityId))
+ {
+ PaintReadOnlyIcon(painter, option, index);
+ }
}
break;
default:
@@ -2166,10 +2177,10 @@ namespace AzToolsFramework
backgroundPath.addRect(backgroundRect);
- QColor backgroundColor = m_hoverColor;
+ QColor backgroundColor = s_hoverColor;
if (isSelected)
{
- backgroundColor = m_selectedColor;
+ backgroundColor = s_selectedColor;
}
painter->fillPath(backgroundPath, backgroundColor);
@@ -2336,6 +2347,20 @@ namespace AzToolsFramework
EntityOutlinerListModel::s_paintingName = false;
}
+ void EntityOutlinerItemDelegate::PaintReadOnlyIcon(QPainter* painter, const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& index) const
+ {
+ // Build the rect that will be used to paint the icon
+ QRect readOnlyRect = QRect(option.rect.topLeft() + s_readOnlyOffset, QSize(s_readOnlyRadius * 2, s_readOnlyRadius * 2));
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(s_readOnlyBackgroundColor);
+ painter->drawEllipse(readOnlyRect.center(), s_readOnlyRadius, s_readOnlyRadius);
+ s_readOnlyIcon.paint(painter, readOnlyRect);
+ painter->restore();
+ }
+
QSize EntityOutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
{
// Get the height of a tall character...
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx
index 0a46ee4850..b785dcb31e 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx
@@ -38,6 +38,7 @@ namespace AzToolsFramework
{
class EditorEntityUiInterface;
class FocusModeInterface;
+ class ReadOnlyEntityPublicInterface;
namespace EntityOutliner
{
@@ -344,6 +345,9 @@ namespace AzToolsFramework
// Paint the entity name using rich text
void PaintEntityNameAsRichText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
+ // Paint the read-only icon on the entity
+ void PaintReadOnlyIcon(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
+
struct CheckboxGroup
{
EntityOutlinerCheckBox m_default;
@@ -372,10 +376,17 @@ namespace AzToolsFramework
// this is a cache, and is hence mutable
mutable QRect m_cachedBoundingRectOfTallCharacter;
- const QColor m_selectedColor = QColor(255, 255, 255, 45);
- const QColor m_hoverColor = QColor(255, 255, 255, 30);
+ inline static const QColor s_selectedColor = QColor(255, 255, 255, 45);
+ inline static const QColor s_hoverColor = QColor(255, 255, 255, 30);
+
+ inline static const QColor s_readOnlyBackgroundColor = QColor("#444444");
+ inline static const QPoint s_readOnlyOffset = QPoint(10, 10);
+ inline static const int s_readOnlyRadius = 6;
+
+ QIcon s_readOnlyIcon = QIcon(QString(":/Entity/readonly.svg"));
EditorEntityUiInterface* m_editorEntityFrameworkInterface = nullptr;
+ ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
};
}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
index bc6700aca5..1c60b78322 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1264,7 +1265,14 @@ namespace AzToolsFramework
}
else
{
- s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
+ if (s_prefabPublicInterface->IsOwnedByProceduralPrefabInstance(entityId))
+ {
+ s_editorEntityUiInterface->RegisterEntity(entityId, m_proceduralPrefabUiHandler.GetHandlerId());
+ }
+ else
+ {
+ s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
+ }
// Register entity as a container
s_containerEntityInterface->RegisterEntityAsContainer(entityId);
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h
index 808a0c2408..8589bee942 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h
@@ -18,10 +18,11 @@
#include
#include
#include
-
#include
#include
#include
+#include
+#include
#include
@@ -92,12 +93,18 @@ namespace AzToolsFramework
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
private:
- // Used to handle the UI for the level root
+ // Used to handle the UI for the level root.
LevelRootUiHandler m_levelRootUiHandler;
- // Used to handle the UI for prefab entities
+ // Used to handle the UI for prefab entities.
PrefabUiHandler m_prefabUiHandler;
+ // Used to handle the UI for procedural prefab entities.
+ ProceduralPrefabUiHandler m_proceduralPrefabUiHandler;
+
+ // Ensures entities owned by procedural prefab instances are marked as read-only correctly.
+ ProceduralPrefabReadOnlyHandler m_proceduralPrefabReadOnlyHandler;
+
// Context menu item handlers
static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities);
static void ContextMenu_InstantiatePrefab();
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp
index 8b56b26508..cc0018753e 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp
@@ -23,17 +23,6 @@ namespace AzToolsFramework
{
AzFramework::EntityContextId PrefabUiHandler::s_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
- const QColor PrefabUiHandler::m_backgroundColor = QColor("#444444");
- const QColor PrefabUiHandler::m_backgroundHoverColor = QColor("#5A5A5A");
- const QColor PrefabUiHandler::m_backgroundSelectedColor = QColor("#656565");
- const QColor PrefabUiHandler::m_prefabCapsuleColor = QColor("#1E252F");
- const QColor PrefabUiHandler::m_prefabCapsuleDisabledColor = QColor("#35383C");
- const QColor PrefabUiHandler::m_prefabCapsuleEditColor = QColor("#4A90E2");
- const QString PrefabUiHandler::m_prefabIconPath = QString(":/Entity/prefab.svg");
- const QString PrefabUiHandler::m_prefabEditIconPath = QString(":/Entity/prefab_edit.svg");
- const QString PrefabUiHandler::m_prefabEditOpenIconPath = QString(":/Entity/prefab_edit_open.svg");
- const QString PrefabUiHandler::m_prefabEditCloseIconPath = QString(":/Entity/prefab_edit_close.svg");
-
PrefabUiHandler::PrefabUiHandler()
{
m_prefabPublicInterface = AZ::Interface::Get();
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h
index bb1c646dbe..a1c624f85a 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h
@@ -46,7 +46,7 @@ namespace AzToolsFramework
void OnOutlinerItemCollapse(const QModelIndex& index) const override;
bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
- private:
+ protected:
Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
@@ -56,17 +56,17 @@ namespace AzToolsFramework
static AzFramework::EntityContextId s_editorEntityContextId;
- static constexpr int m_prefabCapsuleRadius = 6;
- static constexpr int m_prefabBorderThickness = 2;
- static const QColor m_backgroundColor;
- static const QColor m_backgroundHoverColor;
- static const QColor m_backgroundSelectedColor;
- static const QColor m_prefabCapsuleColor;
- static const QColor m_prefabCapsuleDisabledColor;
- static const QColor m_prefabCapsuleEditColor;
- static const QString m_prefabIconPath;
- static const QString m_prefabEditIconPath;
- static const QString m_prefabEditOpenIconPath;
- static const QString m_prefabEditCloseIconPath;
+ int m_prefabCapsuleRadius = 6;
+ int m_prefabBorderThickness = 2;
+ QColor m_backgroundColor = QColor("#444444");
+ QColor m_backgroundHoverColor = QColor("#5A5A5A");
+ QColor m_backgroundSelectedColor = QColor("#656565");
+ QColor m_prefabCapsuleColor = QColor("#1E252F");
+ QColor m_prefabCapsuleDisabledColor = QColor("#35383C");
+ QColor m_prefabCapsuleEditColor = QColor("#4A90E2");
+ QString m_prefabIconPath = QString(":/Entity/prefab.svg");
+ QString m_prefabEditIconPath = QString(":/Entity/prefab_edit.svg");
+ QString m_prefabEditOpenIconPath = QString(":/Entity/prefab_edit_open.svg");
+ QString m_prefabEditCloseIconPath = QString(":/Entity/prefab_edit_close.svg");
};
} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.cpp
new file mode 100644
index 0000000000..ed962ef4ad
--- /dev/null
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+namespace AzToolsFramework
+{
+ namespace Prefab
+ {
+ ProceduralPrefabReadOnlyHandler::ProceduralPrefabReadOnlyHandler()
+ {
+ m_prefabPublicInterface = AZ::Interface::Get();
+ AZ_Assert(
+ m_prefabPublicInterface != nullptr,
+ "ProceduralPrefabReadOnlyHandler requires a PrefabPublicInterface instance on Initialize.");
+
+ m_prefabFocusPublicInterface = AZ::Interface::Get();
+ AZ_Assert(
+ m_prefabFocusPublicInterface != nullptr,
+ "ProceduralPrefabReadOnlyHandler requires a PrefabFocusPublicInterface instance on Initialize.");
+
+ AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
+ EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
+
+ ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
+
+ // Refresh the whole read-only cache
+ if (auto readOnlyEntityQueryInterface = AZ::Interface::Get())
+ {
+ readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
+ }
+ }
+
+ ProceduralPrefabReadOnlyHandler ::~ProceduralPrefabReadOnlyHandler()
+ {
+ ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
+ }
+
+ void ProceduralPrefabReadOnlyHandler::IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly)
+ {
+ if(m_prefabPublicInterface->IsOwnedByProceduralPrefabInstance(entityId))
+ {
+ // All entities nested inside a procedural prefabs should always be marked as read-only.
+ if (!m_prefabPublicInterface->IsInstanceContainerEntity(entityId))
+ {
+ isReadOnly = true;
+ }
+
+ // The container entity of a procedural prefab should only be marked as read-only when the prefab is being edited.
+ if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
+ {
+ isReadOnly = true;
+ }
+ }
+ }
+
+ } // namespace Prefab
+} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h
new file mode 100644
index 0000000000..a4d13752bd
--- /dev/null
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+
+#include
+
+namespace AzToolsFramework
+{
+ namespace Prefab
+ {
+ class PrefabFocusPublicInterface;
+ class PrefabPublicInterface;
+
+ //! Ensures entities in a procedural prefab are correctly reported as read-only.
+ class ProceduralPrefabReadOnlyHandler
+ : public ReadOnlyEntityQueryRequestBus::Handler
+ {
+ public:
+ AZ_CLASS_ALLOCATOR(ProceduralPrefabReadOnlyHandler, AZ::SystemAllocator, 0);
+ AZ_RTTI(AzToolsFramework::ProceduralPrefabReadOnlyHandler, "{A2D72461-8CA3-45EE-81D2-4976BC0B6AE9}");
+
+ ProceduralPrefabReadOnlyHandler();
+ ~ProceduralPrefabReadOnlyHandler() override;
+
+ // ReadOnlyEntityQueryRequestBus overrides ...
+ void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) override;
+
+ private:
+ PrefabPublicInterface* m_prefabPublicInterface = nullptr;
+ PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
+ };
+
+ } // namespace Prefab
+} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.cpp
new file mode 100644
index 0000000000..57b41d3a4b
--- /dev/null
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+
+#include
+
+namespace AzToolsFramework
+{
+ ProceduralPrefabUiHandler::ProceduralPrefabUiHandler()
+ {
+ m_prefabCapsuleColor = QColor("#361561");
+ m_prefabCapsuleDisabledColor = QColor("#4B3455");
+ m_prefabCapsuleEditColor = QColor("#361561");
+ m_prefabIconPath = QString(":/Entity/prefab_edit.svg");
+ m_prefabEditOpenIconPath = QString(":/Entity/prefab_edit_open_readonly.svg");
+ }
+
+ QString ProceduralPrefabUiHandler::GenerateItemTooltip(AZ::EntityId entityId) const
+ {
+ if (AZ::IO::Path path = m_prefabPublicInterface->GetOwningInstancePrefabPath(entityId); !path.empty())
+ {
+ return QObject::tr("Double click to inspect.\n%1").arg(path.Native().data());
+ }
+ return QString();
+ }
+}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.h
new file mode 100644
index 0000000000..2b8c90e0eb
--- /dev/null
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+
+#include
+
+namespace AzToolsFramework
+{
+ namespace Prefab
+ {
+ class PrefabFocusPublicInterface;
+ class PrefabPublicInterface;
+ };
+
+ //! Implements the Editor UI for Procedural Prefabs.
+ class ProceduralPrefabUiHandler
+ : public PrefabUiHandler
+ {
+ public:
+ AZ_CLASS_ALLOCATOR(ProceduralPrefabUiHandler, AZ::SystemAllocator, 0);
+ AZ_RTTI(AzToolsFramework::ProceduralPrefabUiHandler, "{3A3DF9FF-9C2E-4439-B7B4-72173B5A3502}", PrefabUiHandler);
+
+ ProceduralPrefabUiHandler();
+ ~ProceduralPrefabUiHandler() override = default;
+
+ QString GenerateItemTooltip(AZ::EntityId entityId) const override;
+ };
+} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
index 49eaa9b34a..6922ad26a2 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
@@ -768,6 +768,10 @@ set(FILES
UI/Prefab/PrefabUiHandler.cpp
UI/Prefab/PrefabViewportFocusPathHandler.h
UI/Prefab/PrefabViewportFocusPathHandler.cpp
+ UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h
+ UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.cpp
+ UI/Prefab/Procedural/ProceduralPrefabUiHandler.h
+ UI/Prefab/Procedural/ProceduralPrefabUiHandler.cpp
UI/Notifications/ToastNotificationsView.cpp
UI/Notifications/ToastNotificationsView.h
UI/Notifications/ToastBus.h
diff --git a/Code/Framework/AzToolsFramework/Tests/Entity/ReadOnly/ReadOnlyEntityTests.cpp b/Code/Framework/AzToolsFramework/Tests/Entity/ReadOnly/ReadOnlyEntityTests.cpp
index 532325e208..69a130b33f 100644
--- a/Code/Framework/AzToolsFramework/Tests/Entity/ReadOnly/ReadOnlyEntityTests.cpp
+++ b/Code/Framework/AzToolsFramework/Tests/Entity/ReadOnly/ReadOnlyEntityTests.cpp
@@ -96,4 +96,22 @@ namespace AzToolsFramework
// Verify the child entity is no longer marked as read-only
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
}
+
+ TEST_F(ReadOnlyEntityFixture, EnsureCacheIsClearedCorrectlyEvenIfUnchanged)
+ {
+ // Create a handler that sets all entities to read-only.
+ ReadOnlyHandlerAlwaysTrue alwaysTrueHandler;
+
+ {
+ // Create a handler that sets the child entity to read-only.
+ ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
+
+ // Verify the child entity is marked as read-only
+ EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
+ }
+ // When the handler goes out of scope, it calls RefreshReadOnlyStateForAllEntities and refreshes the cache.
+
+ // Verify the child entity is still marked as read-only
+ EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
+ }
}