From 954642c1ce0ea6f8bec96cebcc7947ca41e1b6f8 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 3 Dec 2021 13:40:07 -0600 Subject: [PATCH 01/12] Updated Entity Inspector to changed behavior when an Entity has been marked as read-only. Signed-off-by: Chris Galvan --- .../PropertyEditor/EntityPropertyEditor.cpp | 39 ++++++++++++++++++- .../PropertyEditor/EntityPropertyEditor.hxx | 4 ++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index fa419d5f14..69368814f7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -45,6 +45,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include #include @@ -497,6 +498,9 @@ namespace AzToolsFramework m_prefabPublicInterface = AZ::Interface::Get(); AZ_Assert(m_prefabPublicInterface != nullptr, "EntityPropertyEditor requires a PrefabPublicInterface instance on Initialize."); + m_readOnlyEntityPublicInterface = AZ::Interface::Get(); + AZ_Assert(m_readOnlyEntityPublicInterface != nullptr, "EntityPropertyEditor requires a ReadOnlyEntityPublicInterface instance on Initialize."); + setObjectName("EntityPropertyEditor"); setAcceptDrops(true); @@ -973,7 +977,7 @@ namespace AzToolsFramework m_gui->m_entityDetailsLabel->setVisible(false); // If we're in edit mode, make the name field editable. - m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled()); + m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled() || m_entityIsReadOnly); // get the name of the entity. auto entity = GetSelectedEntityById(entityId); @@ -1062,6 +1066,12 @@ namespace AzToolsFramework bool EntityPropertyEditor::CanAddComponentsToSelection(const SelectionEntityTypeInfo& selectionEntityTypeInfo) const { + if (m_entityIsReadOnly) + { + // Can't add components if there is a read only entity in the selection + return false; + } + if (selectionEntityTypeInfo == SelectionEntityTypeInfo::Mixed || selectionEntityTypeInfo == SelectionEntityTypeInfo::None) { @@ -1126,6 +1136,17 @@ namespace AzToolsFramework m_selectedEntityIds.clear(); GetSelectedEntities(m_selectedEntityIds); + // Check if any of the selected entities are marked as read only + m_entityIsReadOnly = false; + for (const auto& entityId : m_selectedEntityIds) + { + if (m_readOnlyEntityPublicInterface->IsReadOnly(entityId)) + { + m_entityIsReadOnly = true; + break; + } + } + SourceControlFileInfo scFileInfo; ToolsApplicationRequests::Bus::BroadcastResult(scFileInfo, &ToolsApplicationRequests::GetSceneSourceControlInfo); @@ -1681,6 +1702,12 @@ namespace AzToolsFramework componentEditor->UpdateExpandability(); componentEditor->InvalidateAll(!componentInFilter ? m_filterString.c_str() : nullptr); + // If we are in read only mode, then show the components as disabled + if (m_entityIsReadOnly) + { + componentEditor->mockDisabledState(true); + } + if (!componentEditor->GetPropertyEditor()->HasFilteredOutNodes() || componentEditor->GetPropertyEditor()->HasVisibleNodes()) { for (AZ::Component* componentInstance : componentInstances) @@ -3077,6 +3104,7 @@ namespace AzToolsFramework } } + m_gui->m_statusComboBox->setDisabled(m_entityIsReadOnly); m_gui->m_statusComboBox->setVisible(!m_isSystemEntityEditor && !m_isLevelEntityEditor); m_gui->m_statusComboBox->style()->unpolish(m_gui->m_statusComboBox); m_gui->m_statusComboBox->style()->polish(m_gui->m_statusComboBox); @@ -3304,7 +3332,8 @@ namespace AzToolsFramework const auto& componentsToEdit = GetSelectedComponents(); const bool hasComponents = !m_selectedEntityIds.empty() && !componentsToEdit.empty(); - const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit); + // Don't allow components to be removed/cut/enabled/disabled if read only + const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit) && !m_entityIsReadOnly; const bool allowCopy = hasComponents && AreComponentsCopyable(componentsToEdit); m_actionToDeleteComponents->setEnabled(allowRemove); @@ -3366,6 +3395,12 @@ namespace AzToolsFramework return false; } + if (m_entityIsReadOnly) + { + // Can't paste components if there is a read only entity in the selection + return false; + } + // Grab component data from clipboard, if exists const QMimeData* mimeData = ComponentMimeData::GetComponentMimeDataFromClipboard(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx index 8dd0ffc4ee..3d62d6fe10 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx @@ -62,6 +62,7 @@ namespace AzToolsFramework class ComponentPaletteWidget; class ComponentModeCollectionInterface; struct SourceControlFileInfo; + class ReadOnlyEntityPublicInterface; namespace AssetBrowser { @@ -623,6 +624,9 @@ namespace AzToolsFramework Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; bool m_prefabsAreEnabled = false; + ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr; + bool m_entityIsReadOnly = false; + // Reordering row widgets within the RPE. static constexpr float MoveFadeSeconds = 0.5f; From be607521e75e91b3b5291635c107574c8e24af8d Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Fri, 3 Dec 2021 14:15:41 -0600 Subject: [PATCH 02/12] Optimizations to graph canvas translation json serializer A handful of places in tight loops were looking up JSON and map entries multiple times in a row Moving and reusing string variables in tight loops Deferring string creation, copying, assignment until needed Signed-off-by: Guthrie Adams --- .../Translation/TranslationSerializer.cpp | 71 ++++++++----------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/Gems/GraphCanvas/Code/Source/Translation/TranslationSerializer.cpp b/Gems/GraphCanvas/Code/Source/Translation/TranslationSerializer.cpp index 80e488f3dc..754267b27d 100644 --- a/Gems/GraphCanvas/Code/Source/Translation/TranslationSerializer.cpp +++ b/Gems/GraphCanvas/Code/Source/Translation/TranslationSerializer.cpp @@ -15,39 +15,39 @@ namespace GraphCanvas void AddEntryToDatabase(const AZStd::string& baseKey, const AZStd::string& name, const rapidjson::Value& it, TranslationFormat* translationFormat) { - AZStd::string finalKey = baseKey; if (it.IsString()) { - if (translationFormat->m_database.find(finalKey) == translationFormat->m_database.end()) + auto translationDbItr = translationFormat->m_database.find(baseKey); + if (translationDbItr == translationFormat->m_database.end()) { - translationFormat->m_database[finalKey] = it.GetString(); + translationFormat->m_database[baseKey] = it.GetString(); } else { - AZStd::string existingValue = translationFormat->m_database[finalKey.c_str()]; + const AZStd::string& existingValue = translationDbItr->second; // There is a name collision - AZStd::string error = AZStd::string::format("Unable to store key: %s with value: %s because that key already exists with value: %s (proposed: %s)", finalKey.c_str(), it.GetString(), existingValue.c_str(), it.GetString()); + const AZStd::string error = AZStd::string::format("Unable to store key: %s with value: %s because that key already exists with value: %s (proposed: %s)", baseKey.c_str(), it.GetString(), existingValue.c_str(), it.GetString()); AZ_Error("TranslationSerializer", false, error.c_str()); } } else if (it.IsObject()) { + AZStd::string finalKey = baseKey; if (!name.empty()) { finalKey.append("."); finalKey.append(name); } - AZStd::string itemKey = finalKey; + AZStd::string itemKey; for (auto objIt = it.MemberBegin(); objIt != it.MemberEnd(); ++objIt) { + itemKey = finalKey; itemKey.append("."); itemKey.append(objIt->name.GetString()); AddEntryToDatabase(itemKey, name, objIt->value, translationFormat); - - itemKey = finalKey; } } @@ -60,18 +60,21 @@ namespace GraphCanvas key.append(name); } - AZStd::string itemKey = key; + AZStd::string itemKey; const rapidjson::Value& array = it; for (rapidjson::SizeType i = 0; i < array.Size(); ++i) { + itemKey = key; + // if there is a "base" member within the object, then use it, otherwise use the index - if (array[i].IsObject()) + const auto& element = array[i]; + if (element.IsObject()) { - if (array[i].HasMember(Schema::Field::key)) + rapidjson::Value::ConstMemberIterator innerKeyItr = element.FindMember(Schema::Field::key); + if (innerKeyItr != element.MemberEnd()) { - AZStd::string innerKey = array[i].FindMember(Schema::Field::key)->value.GetString(); - itemKey.append(AZStd::string::format(".%s", innerKey.c_str())); + itemKey.append(AZStd::string::format(".%s", innerKeyItr->value.GetString())); } else { @@ -79,9 +82,7 @@ namespace GraphCanvas } } - AddEntryToDatabase(itemKey, "", array[i], translationFormat); - - itemKey = key; + AddEntryToDatabase(itemKey, "", element, translationFormat); } } } @@ -114,42 +115,32 @@ namespace GraphCanvas { const rapidjson::Value::ConstMemberIterator entries = inputValue.FindMember(Schema::Field::entries); + AZStd::string keyStr; + AZStd::string contextStr; + AZStd::string variantStr; + AZStd::string baseKey; + rapidjson::SizeType entryCount = entries->value.Size(); for (rapidjson::SizeType i = 0; i < entryCount; ++i) { const rapidjson::Value& entry = entries->value[i]; - AZStd::string keyStr; - rapidjson::Value::ConstMemberIterator keyValue; - if (entry.HasMember(Schema::Field::key)) - { - keyValue = entry.FindMember(Schema::Field::key); - keyStr = keyValue->value.GetString(); - } + rapidjson::Value::ConstMemberIterator keyItr = entry.FindMember(Schema::Field::key); + keyStr = keyItr != entry.MemberEnd() ? keyItr->value.GetString() : ""; - AZStd::string contextStr; - rapidjson::Value::ConstMemberIterator contextValue; - if (entry.HasMember(Schema::Field::context)) - { - contextValue = entry.FindMember(Schema::Field::context); - contextStr = contextValue->value.GetString(); - } + rapidjson::Value::ConstMemberIterator contextItr = entry.FindMember(Schema::Field::context); + contextStr = contextItr != entry.MemberEnd() ? contextItr->value.GetString() : ""; - AZStd::string variantStr; - rapidjson::Value::ConstMemberIterator variantValue; - if (entry.HasMember(Schema::Field::variant)) - { - variantValue = entry.FindMember(Schema::Field::variant); - variantStr = variantValue->value.GetString(); - } + rapidjson::Value::ConstMemberIterator variantItr = entry.FindMember(Schema::Field::variant); + variantStr = variantItr != entry.MemberEnd() ? variantItr->value.GetString() : ""; - AZStd::string baseKey = contextStr; if (keyStr.empty()) { - AZ_Error("TranslationDatabase", false, "Every entry in the Translation data must have a key: %s", baseKey.c_str()); + AZ_Error("TranslationDatabase", false, "Every entry in the Translation data must have a key: %s", contextStr.c_str()); return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, "Every entry in the Translation data must have a key"); } + baseKey = contextStr; if (!baseKey.empty()) { baseKey.append("."); @@ -167,7 +158,7 @@ namespace GraphCanvas for (auto it = entry.MemberBegin(); it != entry.MemberEnd(); ++it) { // Skip the fixed elements - if (it == keyValue || it == contextValue || it == variantValue) + if (it == keyItr || it == contextItr || it == variantItr) { continue; } From d56cfa40dd81e3b15c4d6cb8dfa7aa7ff56e8656 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 2 Dec 2021 16:29:55 -0600 Subject: [PATCH 03/12] Changing trace logger to clear log file when opened Also switched the log message sink from a vector to a list so the entire container does not have to be rebuilt every time and entry is added. Signed-off-by: Guthrie Adams --- .../AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp | 4 ++-- .../AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp index ce73826388..386f5ea179 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp @@ -73,7 +73,7 @@ namespace AzToolsFramework AZStd::string logPath; StringFunc::Path::Join(logDirectory.c_str(), logFileName.c_str(), logPath); - m_logFile.reset(aznew LogFile(logPath.c_str())); + m_logFile.reset(aznew LogFile(logPath.c_str(), true)); if (m_logFile) { m_logFile->SetMachineReadable(false); @@ -81,7 +81,7 @@ namespace AzToolsFramework { m_logFile->AppendLog(LogFile::SEV_NORMAL, message.window.c_str(), message.message.c_str()); } - m_startupLogSink = {}; + m_startupLogSink.clear(); m_logFile->FlushLog(); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h index 10708e3239..dafd3becc8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h @@ -55,7 +55,8 @@ namespace AzToolsFramework AZStd::string window; AZStd::string message; }; - AZStd::vector m_startupLogSink; + + AZStd::list m_startupLogSink; AZStd::unordered_set m_windowFilters; AZStd::unordered_set m_messageFilters; AZStd::unique_ptr m_logFile; From af75b3a52aa290595768b27d586608f7b913f0ef Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Fri, 3 Dec 2021 15:25:54 -0600 Subject: [PATCH 04/12] Made clearing the log file optional and renamed the function Signed-off-by: Guthrie Adams --- .../AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp | 4 ++-- .../AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h | 2 +- .../Code/Source/Application/AtomToolsApplication.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp index 386f5ea179..8c7150f027 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp @@ -50,7 +50,7 @@ namespace AzToolsFramework return false; } - void TraceLogger::PrepareLogFile(const AZStd::string& logFileName) + void TraceLogger::OpenLogFile(const AZStd::string& logFileName, bool clearLogFile) { using namespace AzFramework; @@ -73,7 +73,7 @@ namespace AzToolsFramework AZStd::string logPath; StringFunc::Path::Join(logDirectory.c_str(), logFileName.c_str(), logPath); - m_logFile.reset(aznew LogFile(logPath.c_str(), true)); + m_logFile.reset(aznew LogFile(logPath.c_str(), clearLogFile)); if (m_logFile) { m_logFile->SetMachineReadable(false); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h index dafd3becc8..639674f69e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.h @@ -23,7 +23,7 @@ namespace AzToolsFramework ~TraceLogger(); //! Open log file and dump log sink into it - void PrepareLogFile(const AZStd::string& logFileName); + void OpenLogFile(const AZStd::string& logFileName, bool clearLogFile); //! Add filter to ignore messages for windows with matching names void AddWindowFilter(const AZStd::string& filter); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 0f21dcb70e..ec365d7a6a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -175,7 +175,8 @@ namespace AtomToolsFramework Base::StartCommon(systemEntity); - m_traceLogger.PrepareLogFile(GetBuildTargetName() + ".log"); + const bool clearLogFile = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false); + m_traceLogger.OpenLogFile(GetBuildTargetName() + ".log", clearLogFile); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusConnect(); AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( From 273fb87eebbd5bae61dd67c1670d7b0c8e61f8fa Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 3 Dec 2021 16:21:31 -0600 Subject: [PATCH 05/12] Renamed variable to be more descriptive. Signed-off-by: Chris Galvan --- .../UI/PropertyEditor/EntityPropertyEditor.cpp | 16 ++++++++-------- .../UI/PropertyEditor/EntityPropertyEditor.hxx | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index 69368814f7..f69aa84709 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -977,7 +977,7 @@ namespace AzToolsFramework m_gui->m_entityDetailsLabel->setVisible(false); // If we're in edit mode, make the name field editable. - m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled() || m_entityIsReadOnly); + m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled() || m_selectionContainsReadOnlyEntity); // get the name of the entity. auto entity = GetSelectedEntityById(entityId); @@ -1066,7 +1066,7 @@ namespace AzToolsFramework bool EntityPropertyEditor::CanAddComponentsToSelection(const SelectionEntityTypeInfo& selectionEntityTypeInfo) const { - if (m_entityIsReadOnly) + if (m_selectionContainsReadOnlyEntity) { // Can't add components if there is a read only entity in the selection return false; @@ -1137,12 +1137,12 @@ namespace AzToolsFramework GetSelectedEntities(m_selectedEntityIds); // Check if any of the selected entities are marked as read only - m_entityIsReadOnly = false; + m_selectionContainsReadOnlyEntity = false; for (const auto& entityId : m_selectedEntityIds) { if (m_readOnlyEntityPublicInterface->IsReadOnly(entityId)) { - m_entityIsReadOnly = true; + m_selectionContainsReadOnlyEntity = true; break; } } @@ -1703,7 +1703,7 @@ namespace AzToolsFramework componentEditor->InvalidateAll(!componentInFilter ? m_filterString.c_str() : nullptr); // If we are in read only mode, then show the components as disabled - if (m_entityIsReadOnly) + if (m_selectionContainsReadOnlyEntity) { componentEditor->mockDisabledState(true); } @@ -3104,7 +3104,7 @@ namespace AzToolsFramework } } - m_gui->m_statusComboBox->setDisabled(m_entityIsReadOnly); + m_gui->m_statusComboBox->setDisabled(m_selectionContainsReadOnlyEntity); m_gui->m_statusComboBox->setVisible(!m_isSystemEntityEditor && !m_isLevelEntityEditor); m_gui->m_statusComboBox->style()->unpolish(m_gui->m_statusComboBox); m_gui->m_statusComboBox->style()->polish(m_gui->m_statusComboBox); @@ -3333,7 +3333,7 @@ namespace AzToolsFramework const bool hasComponents = !m_selectedEntityIds.empty() && !componentsToEdit.empty(); // Don't allow components to be removed/cut/enabled/disabled if read only - const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit) && !m_entityIsReadOnly; + const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit) && !m_selectionContainsReadOnlyEntity; const bool allowCopy = hasComponents && AreComponentsCopyable(componentsToEdit); m_actionToDeleteComponents->setEnabled(allowRemove); @@ -3395,7 +3395,7 @@ namespace AzToolsFramework return false; } - if (m_entityIsReadOnly) + if (m_selectionContainsReadOnlyEntity) { // Can't paste components if there is a read only entity in the selection return false; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx index 3d62d6fe10..bf8cac8968 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx @@ -625,7 +625,7 @@ namespace AzToolsFramework bool m_prefabsAreEnabled = false; ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr; - bool m_entityIsReadOnly = false; + bool m_selectionContainsReadOnlyEntity = false; // Reordering row widgets within the RPE. static constexpr float MoveFadeSeconds = 0.5f; From b470f917caae150589aa22b9eecc48e21c3bfcd9 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 3 Dec 2021 16:37:35 -0600 Subject: [PATCH 06/12] Listen to read-only entity state changes so we can refresh if the read-only state changes on one of our selected entities. Signed-off-by: Chris Galvan --- .../UI/PropertyEditor/EntityPropertyEditor.cpp | 15 +++++++++++++++ .../UI/PropertyEditor/EntityPropertyEditor.hxx | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index f69aa84709..0b9420a629 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -569,6 +569,12 @@ namespace AzToolsFramework AZ::EntitySystemBus::Handler::BusConnect(); EntityPropertyEditorRequestBus::Handler::BusConnect(); EditorWindowUIRequestBus::Handler::BusConnect(); + + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + ReadOnlyEntityPublicNotificationBus::Handler::BusConnect(editorEntityContextId); + m_spacer = nullptr; m_emptyIcon = QIcon(); @@ -618,6 +624,7 @@ namespace AzToolsFramework { qApp->removeEventFilter(this); + ReadOnlyEntityPublicNotificationBus::Handler::BusDisconnect(); EditorWindowUIRequestBus::Handler::BusDisconnect(); EntityPropertyEditorRequestBus::Handler::BusDisconnect(); ToolsApplicationEvents::Bus::Handler::BusDisconnect(); @@ -5762,6 +5769,14 @@ namespace AzToolsFramework SaveComponentEditorState(); } + void EntityPropertyEditor::OnReadOnlyEntityStatusChanged(const AZ::EntityId& entityId, [[maybe_unused]] bool readOnly) + { + if (IsEntitySelected(entityId)) + { + UpdateContents(); + } + } + void EntityPropertyEditor::OnEditorModeActivated( [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx index bf8cac8968..254b70996b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,7 @@ namespace AzToolsFramework , public AZ::EntitySystemBus::Handler , public AZ::TickBus::Handler , private EditorWindowUIRequestBus::Handler + , private ReadOnlyEntityPublicNotificationBus::Handler { Q_OBJECT; public: @@ -254,6 +256,9 @@ namespace AzToolsFramework // EditorWindowRequestBus overrides void SetEditorUiEnabled(bool enable) override; + // ReadOnlyEntityPublicNotificationBus overrides ... + void OnReadOnlyEntityStatusChanged(const AZ::EntityId& entityId, bool readOnly) override; + bool IsEntitySelected(const AZ::EntityId& id) const; bool IsSingleEntitySelected(const AZ::EntityId& id) const; From fc8fd89ff9703c100db7572dc0c84adfa7c4db6c Mon Sep 17 00:00:00 2001 From: gusmccallum Date: Fri, 3 Dec 2021 21:46:16 -0500 Subject: [PATCH 07/12] Updated android launcher to remove confusing branding Signed-off-by: gusmccallum --- Code/LauncherUnified/Platform/Android/Launcher_Android.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp b/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp index de548a49d7..0890b5a193 100644 --- a/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp +++ b/Code/LauncherUnified/Platform/Android/Launcher_Android.cpp @@ -299,7 +299,7 @@ void android_main(android_app* appState) { // Adding a start up banner so you can see when the game is starting up in amongst the logcat spam LOGI("****************************************************************"); - LOGI("* Amazon Lumberyard - Launching Game... *"); + LOGI("* Launching Game... *"); LOGI("****************************************************************"); // setup the system command handler which are guaranteed to be called on the same From a7989df0516daea2e6a1be8140a7869766af0b6e Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Mon, 6 Dec 2021 10:39:49 -0600 Subject: [PATCH 08/12] Fixed status combo box style in Entity Inspector. Signed-off-by: Chris Galvan --- .../UI/PropertyEditor/EntityPropertyEditor.cpp | 4 ---- .../UI/PropertyEditor/EntityPropertyEditor.ui | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index 0b9420a629..8104aee4fd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -539,10 +539,6 @@ namespace AzToolsFramework model->setItem(row, 0, m_comboItems[row]); } m_gui->m_statusComboBox->setModel(model); - m_gui->m_statusComboBox->setStyleSheet("QComboBox {border: 0px; border-radius:3px; background-color:#555555; color:white}" - "QComboBox:on {background-color:#e9e9e9; color:black; border:0px}" - "QComboBox::down-arrow:on {image: url(:/stylesheet/img/dropdowns/black_down_arrow.png)}" - "QComboBox::drop-down {border-radius: 3p}"); AzQtComponents::ComboBox::addCustomCheckStateStyle(m_gui->m_statusComboBox); EnableEditor(true); m_sceneIsNew = true; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.ui b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.ui index 8a86d429f1..b7cb5a0156 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.ui +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.ui @@ -191,7 +191,7 @@ - background-color:rgb(51, 51, 51) + QWidget#m_darkBox { background-color:rgb(51, 51, 51) } @@ -444,6 +444,9 @@ Qt::Horizontal + + background-color:rgb(51, 51, 51) + From be252d797dc8842344879781eb01c5348cc1d7fb Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Mon, 6 Dec 2021 09:11:56 -0800 Subject: [PATCH 09/12] Remove disabled failed asset load tests trait from linux (#6100) * Remove trait AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS for Linux Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> --- .../Tests/Asset/AssetManagerLoadingTests.cpp | 16 ++++++++-------- .../AzTest/Platform/Linux/AzTest_Traits_Linux.h | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp b/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp index 043eb1aee6..c3be9b886a 100644 --- a/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp +++ b/Code/Framework/AzCore/Tests/Asset/AssetManagerLoadingTests.cpp @@ -914,11 +914,11 @@ namespace UnitTest m_testAssetManager->SetParallelDependentLoadingEnabled(true); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS TEST_F(AssetJobsFloodTest, DISABLED_LoadTest_SameAsset_DifferentFilters) #else TEST_F(AssetJobsFloodTest, LoadTest_SameAsset_DifferentFilters) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS { m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect(); @@ -1263,11 +1263,11 @@ namespace UnitTest m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect(); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded) #else TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS { m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect(); // Setup has already created/destroyed assets @@ -1304,11 +1304,11 @@ namespace UnitTest m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect(); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad) #else TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS { m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect(); // Setup has already created/destroyed assets @@ -1343,11 +1343,11 @@ namespace UnitTest m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect(); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed) #else TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS +#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS { m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect(); // Setup has already created/destroyed assets diff --git a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h index 6d6513043b..94f3dd004a 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h +++ b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h @@ -14,7 +14,6 @@ #define AZ_TRAIT_UNIT_TEST_DILLER_TRIGGER_EVENT_COUNT 100000 #define AZ_TRAIT_DISABLE_FAILED_AP_CONNECTION_TESTS true -#define AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS true From e3306f948f0fce67c2fbb47b5f84b132555a22cc Mon Sep 17 00:00:00 2001 From: smurly Date: Mon, 6 Dec 2021 09:18:16 -0800 Subject: [PATCH 10/12] Atom Level component test support in editor_entity_utils.py and tests for level components (#6082) * Level component helper and test cases for Atom Level components which currently crash Signed-off-by: Scott Murray * moved a close paren to end of previous line to match existing style Signed-off-by: Scott Murray * adding a forgotten import Signed-off-by: Scott Murray * minor refactor and prep for adding back the Display Mapper Game component test Signed-off-by: Scott Murray * putting Game compoenent Display Mapper test back with addition of setting property Enabled to true Signed-off-by: Scott Murray * typo in line fixed Test. bocomes Tests. Signed-off-by: Scott Murray --- .../Gem/PythonTests/Atom/TestSuite_Sandbox.py | 15 +++ .../Atom/atom_utils/atom_constants.py | 26 +++- ...ntsLevel_DiffuseGlobalIlluminationAdded.py | 109 +++++++++++++++ ...ditorComponentsLevel_DisplayMapperAdded.py | 124 ++++++++++++++++++ ...AtomEditorComponents_DisplayMapperAdded.py | 75 ++++++----- .../editor_entity_utils.py | 98 +++++++++++++- 6 files changed, 409 insertions(+), 38 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py index 292bb9c19c..f0bb6e72ca 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py @@ -12,6 +12,7 @@ import pytest import ly_test_tools.environment.file_system as file_system import editor_python_test_tools.hydra_test_utils as hydra +from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite from Atom.atom_utils.atom_constants import LIGHT_TYPES logger = logging.getLogger(__name__) @@ -159,3 +160,17 @@ class TestMaterialEditorBasicTests(object): enable_prefab_system=False, ) + +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +class TestAutomation(EditorTestSuite): + + enable_prefab_system = False + + @pytest.mark.test_case_id("C36529666") + class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module + + @pytest.mark.test_case_id("C36525660") + class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index 290e1cd2e4..c73f75fbc0 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -18,6 +18,13 @@ LIGHT_TYPES = { 'simple_spot': 7, } +# Qualiity Level settings for Diffuse Global Illumination level component +GLOBAL_ILLUMINATION_QUALITY = { + 'Low': 0, + 'Medium': 1, + 'High': 2, +} + class AtomComponentProperties: """ @@ -116,6 +123,21 @@ class AtomComponentProperties: } return properties[property] + @staticmethod + def diffuse_global_illumination(property: str = 'name') -> str: + """ + Diffuse Global Illumination level component properties. + Controls global settings for Diffuse Probe Grid components. + - 'Quality Level' from atom_constants.py GLOBAL_ILLUMINATION_QUALITY + :param property: From the last element of the property tree path. Default 'name' for component name string. + :return: Full property path OR component name if no property specified. + """ + properties = { + 'name': 'Diffuse Global Illumination', + 'Quality Level': 'Controller|Configuration|Quality Level' + } + return properties[property] + @staticmethod def diffuse_probe_grid(property: str = 'name') -> str: """ @@ -148,7 +170,8 @@ class AtomComponentProperties: @staticmethod def display_mapper(property: str = 'name') -> str: """ - Display Mapper component properties. + Display Mapper level component properties. + - 'Enable LDR color grading LUT' toggles the use of LDR color grading LUT - 'LDR color Grading LUT' is the Low Definition Range (LDR) color grading for Look-up Textures (LUT) which is an Asset.id value corresponding to a lighting asset file. :param property: From the last element of the property tree path. Default 'name' for component name string. @@ -156,6 +179,7 @@ class AtomComponentProperties: """ properties = { 'name': 'Display Mapper', + 'Enable LDR color grading LUT': 'Controller|Configuration|Enable LDR color grading LUT', 'LDR color Grading LUT': 'Controller|Configuration|LDR color Grading LUT', } return properties[property] diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py new file mode 100644 index 0000000000..ddcd5c3c46 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded.py @@ -0,0 +1,109 @@ +""" +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 +""" + +class Tests: + creation_undo = ( + "UNDO Level component addition success", + "UNDO Level component addition failed") + creation_redo = ( + "REDO Level component addition success", + "REDO Level component addition failed") + diffuse_global_illumination_component = ( + "Level has a Diffuse Global Illumination component", + "Level failed to find Diffuse Global Illumination component") + diffuse_global_illumination_quality = ( + "Quality Level set", + "Quality Level could not be set") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + + +def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity(): + """ + Summary: + Tests the Diffuse Global Illumination level component can be added to the level entity and is stable. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Add Diffuse Global Illumination level component to the level entity. + 2) UNDO the level component addition. + 3) REDO the level component addition. + 4) Set Quality Level property to Low + 5) Enter/Exit game mode. + 6) Look for errors and asserts. + + :return: None + """ + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorLevelEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + from Atom.atom_utils.atom_constants import AtomComponentProperties, GLOBAL_ILLUMINATION_QUALITY + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Add Diffuse Global Illumination level component to the level entity. + diffuse_global_illumination_component = EditorLevelEntity.add_component( + AtomComponentProperties.diffuse_global_illumination()) + Report.critical_result( + Tests.diffuse_global_illumination_component, + EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination())) + + # 2. UNDO the level component addition. + # -> UNDO component addition. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, + not EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination())) + + # 3. REDO the level component addition. + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, + EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination())) + + # 4. Set Quality Level property to Low + diffuse_global_illumination_component.set_component_property_value( + AtomComponentProperties.diffuse_global_illumination('Quality Level', GLOBAL_ILLUMINATION_QUALITY['Low'])) + quality = diffuse_global_illumination_component.get_component_property_value( + AtomComponentProperties.diffuse_global_illumination('Quality Level')) + Report.result(diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low']) + + # 5. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 6. Look for errors and asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py new file mode 100644 index 0000000000..c050dd76d4 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponentsLevel_DisplayMapperAdded.py @@ -0,0 +1,124 @@ +""" +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 +""" + + +class Tests: + creation_undo = ( + "UNDO level component addition success", + "UNDO level component addition failed") + creation_redo = ( + "REDO Level component addition success", + "REDO Level component addition failed") + display_mapper_component = ( + "Level has a Display Mapper component", + "Level failed to find Display Mapper component") + ldr_color_grading_lut = ( + "LDR color Grading LUT asset set", + "LDR color Grading LUT asset could not be set") + enable_ldr_color_grading_lut = ( + "Enable LDR color grading LUT set", + "Enable LDR color grading LUT could not be set") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + + +def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity(): + """ + Summary: + Tests the Display Mapper level component can be added to the level entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Add Display Mapper level component to the level entity. + 2) UNDO the level component addition. + 3) REDO the level component addition. + 4) Set LDR color Grading LUT asset. + 5) Set Enable LDR color grading LUT property True + 6) Enter/Exit game mode. + 7) Look for errors and asserts. + + :return: None + """ + import os + + import azlmbr.legacy.general as general + + from editor_python_test_tools.asset_utils import Asset + from editor_python_test_tools.editor_entity_utils import EditorLevelEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + from Atom.atom_utils.atom_constants import AtomComponentProperties + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Add Display Mapper level component to the level entity. + display_mapper_component = EditorLevelEntity.add_component(AtomComponentProperties.display_mapper()) + Report.critical_result( + Tests.display_mapper_component, + EditorLevelEntity.has_component(AtomComponentProperties.display_mapper())) + + # 2. UNDO the level component addition. + # -> UNDO component addition. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not EditorLevelEntity.has_component(AtomComponentProperties.display_mapper())) + + # 3. REDO the level component addition. + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, EditorLevelEntity.has_component(AtomComponentProperties.display_mapper())) + + # 4. Set LDR color Grading LUT asset. + display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset") + display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False) + display_mapper_component.set_component_property_value( + AtomComponentProperties.display_mapper('LDR color Grading LUT'), display_mapper_asset.id) + Report.result( + Tests.ldr_color_grading_lut, + display_mapper_component.get_component_property_value( + AtomComponentProperties.display_mapper('LDR color Grading LUT')) == display_mapper_asset.id) + + # 5. Set Enable LDR color grading LUT property True + display_mapper_component.set_component_property_value( + AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True) + Report.result( + Test.enable_ldr_color_grading_lut, + display_mapper_component.get_component_property_value( + AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True) + + # 6. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 7. Look for errors and asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponentsLevel_DisplayMapper_AddedToEntity) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py index e2e432364d..558d69046e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py @@ -7,15 +7,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT class Tests: - camera_creation = ( - "Camera Entity successfully created", - "Camera Entity failed to be created") - camera_component_added = ( - "Camera component was added to entity", - "Camera component failed to be added to entity") - camera_component_check = ( - "Entity has a Camera component", - "Entity failed to find Camera component") creation_undo = ( "UNDO Entity creation success", "UNDO Entity creation failed") @@ -43,6 +34,9 @@ class Tests: ldr_color_grading_lut = ( "LDR color Grading LUT asset set", "LDR color Grading LUT asset could not be set") + enable_ldr_color_grading_lut = ( + "Enable LDR color grading LUT set", + "Enable LDR color grading LUT could not be set") entity_deleted = ( "Entity deleted", "Entity was not deleted") @@ -72,14 +66,15 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity(): 2) Add Display Mapper component to Display Mapper entity. 3) UNDO the entity creation and component addition. 4) REDO the entity creation and component addition. - 5) Enter/Exit game mode. - 6) Test IsHidden. - 7) Test IsVisible. - 8) Set LDR color Grading LUT asset. - 9) Delete Display Mapper entity. - 10) UNDO deletion. - 11) REDO deletion. - 12) Look for errors and asserts. + 5) Set LDR color Grading LUT asset. + 6) Set Enable LDR color grading LUT property True + 7) Enter/Exit game mode. + 8) Test IsHidden. + 9) Test IsVisible. + 10) Delete Display Mapper entity. + 11) UNDO deletion. + 12) REDO deletion. + 13) Look for errors and asserts. :return: None """ @@ -133,21 +128,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity(): general.idle_wait_frames(1) Report.result(Tests.creation_redo, display_mapper_entity.exists()) - # 5. Enter/Exit game mode. - TestHelper.enter_game_mode(Tests.enter_game_mode) - general.idle_wait_frames(1) - TestHelper.exit_game_mode(Tests.exit_game_mode) - - # 6. Test IsHidden. - display_mapper_entity.set_visibility_state(False) - Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True) - - # 7. Test IsVisible. - display_mapper_entity.set_visibility_state(True) - general.idle_wait_frames(1) - Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True) - - # 8. Set LDR color Grading LUT asset. + # 5. Set LDR color Grading LUT asset. display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset") display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False) display_mapper_component.set_component_property_value( @@ -157,19 +138,41 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity(): display_mapper_component.get_component_property_value( AtomComponentProperties.display_mapper("LDR color Grading LUT")) == display_mapper_asset.id) - # 9. Delete Display Mapper entity. + # 6. Set Enable LDR color grading LUT property True + display_mapper_component.set_component_property_value( + AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True) + Report.result( + Tests.enable_ldr_color_grading_lut, + display_mapper_component.get_component_property_value( + AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True) + + # 7. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 8. Test IsHidden. + display_mapper_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True) + + # 9. Test IsVisible. + display_mapper_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True) + + # 10. Delete Display Mapper entity. display_mapper_entity.delete() Report.result(Tests.entity_deleted, not display_mapper_entity.exists()) - # 10. UNDO deletion. + # 11. UNDO deletion. general.undo() Report.result(Tests.deletion_undo, display_mapper_entity.exists()) - # 11. REDO deletion. + # 12. REDO deletion. general.redo() Report.result(Tests.deletion_redo, not display_mapper_entity.exists()) - # 12. Look for errors and asserts. + # 13. Look for errors and asserts. TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) for error_info in error_tracer.errors: Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py index 309601b0ba..2d1c34b9c4 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py @@ -107,7 +107,6 @@ class EditorComponent: return type_ids - def convert_to_azvector3(xyz) -> azlmbr.math.Vector3: """ Converts a vector3-like element into a azlmbr.math.Vector3 @@ -120,6 +119,7 @@ def convert_to_azvector3(xyz) -> azlmbr.math.Vector3: else: raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3") + class EditorEntity: """ Entity class is used to create and interact with Editor Entities. @@ -470,3 +470,99 @@ class EditorEntity: assert self.id.isValid(), "A valid entity id is required to focus on its owning prefab." focus_prefab_result = azlmbr.prefab.PrefabFocusPublicRequestBus(bus.Broadcast, "FocusOnOwningPrefab", self.id) assert focus_prefab_result.IsSuccess(), f"Prefab operation 'FocusOnOwningPrefab' failed. Error: {focus_prefab_result.GetError()}" + + +class EditorLevelEntity: + """ + EditorLevel class used to add and fetch level components. + Level entity is a special entity that you do not create/destroy independently of larger systems of level creation. + This collects a number of staticmethods that do not rely on entityId since Level entity is found internally by + EditorLevelComponentAPIBus requests. + """ + + @staticmethod + def get_type_ids(component_names: list) -> list: + """ + Used to get type ids of given components list for EntityType Level + :param: component_names: List of components to get type ids + :return: List of type ids of given components. + """ + type_ids = editor.EditorComponentAPIBus( + bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, azlmbr.entity.EntityType().Level + ) + return type_ids + + @staticmethod + def add_component(component_name: str) -> EditorComponent: + """ + Used to add new component to Level. + :param component_name: String of component name to add. + :return: Component object of newly added component. + """ + component = EditorLevelEntity.add_components([component_name])[0] + return component + + @staticmethod + def add_components(component_names: list) -> List[EditorComponent]: + """ + Used to add multiple components + :param: component_names: List of components to add to level + :return: List of newly added components to the level + """ + components = [] + type_ids = EditorLevelEntity.get_type_ids(component_names) + for type_id in type_ids: + new_comp = EditorComponent() + new_comp.type_id = type_id + add_component_outcome = editor.EditorLevelComponentAPIBus( + bus.Broadcast, "AddComponentsOfType", [type_id] + ) + assert ( + add_component_outcome.IsSuccess() + ), f"Failure: Could not add component: '{new_comp.get_component_name()}' to level" + new_comp.id = add_component_outcome.GetValue()[0] + components.append(new_comp) + return components + + @staticmethod + def get_components_of_type(component_names: list) -> List[EditorComponent]: + """ + Used to get components of type component_name that already exists on the level + :param component_names: List of names of components to check + :return: List of Level Component objects of given component name + """ + component_list = [] + type_ids = EditorLevelEntity.get_type_ids(component_names) + for type_id in type_ids: + component = EditorComponent() + component.type_id = type_id + get_component_of_type_outcome = editor.EditorLevelComponentAPIBus( + bus.Broadcast, "GetComponentOfType", type_id + ) + assert ( + get_component_of_type_outcome.IsSuccess() + ), f"Failure: Level does not have component:'{component.get_component_name()}'" + component.id = get_component_of_type_outcome.GetValue() + component_list.append(component) + + return component_list + + @staticmethod + def has_component(component_name: str) -> bool: + """ + Used to verify if the level has the specified component + :param component_name: Name of component to check for + :return: True, if level has specified component. Else, False + """ + type_ids = EditorLevelEntity.get_type_ids([component_name]) + return editor.EditorLevelComponentAPIBus(bus.Broadcast, "HasComponentOfType", type_ids[0]) + + @staticmethod + def count_components_of_type(component_name: str) -> int: + """ + Used to get a count of the specified level component attached to the level + :param component_name: Name of component to check for + :return: integer count of occurences of level component attached to level or zero if none are present + """ + type_ids = EditorLevelEntity.get_type_ids([component_name]) + return editor.EditorLevelComponentAPIBus(bus.Broadcast, "CountComponentsOfType", type_ids[0]) From 83cb085a2863c9d2f260b9346eb8a81f640518bb Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Mon, 6 Dec 2021 11:24:04 -0600 Subject: [PATCH 11/12] Fixed the register.py command with a --project-path to check against a (#6158) supplied --engine-path parameter to determine if it should update the project.json if one has been provided. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- scripts/o3de/o3de/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index 2500df1568..62a342f948 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -411,7 +411,7 @@ def register_project_path(json_data: dict, if not remove: # registering a project has the additional step of setting the project.json 'engine' field - this_engine_json = manifest.get_engine_json_data(engine_path=manifest.get_this_engine_path()) + this_engine_json = manifest.get_engine_json_data(engine_path=engine_path if engine_path else manifest.get_this_engine_path()) if not this_engine_json: return 1 project_json_data = manifest.get_project_json_data(project_path=project_path) 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 12/12] 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])); + } }