diff --git a/Code/Framework/AzCore/AzCore/Component/Component.h b/Code/Framework/AzCore/AzCore/Component/Component.h index ed7e45ca7c69..7b95853ee039 100644 --- a/Code/Framework/AzCore/AzCore/Component/Component.h +++ b/Code/Framework/AzCore/AzCore/Component/Component.h @@ -118,6 +118,13 @@ namespace AZ */ void SetId(const ComponentId& id) { m_id = id; } + virtual void SetAlias(const AZStd::string&){}; + + virtual AZStd::string GetAlias() + { + return AZStd::string(); + } + /** * Override to conduct per-component or per-slice validation logic during slice asset processing. * @param sliceEntities All entities that belong to the slice that the entity with this component is on. diff --git a/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp b/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp index 0cddae99af22..7704fa9e24b8 100644 --- a/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp @@ -89,6 +89,7 @@ namespace AZ if (component && (component->GetUnderlyingComponentType() != genericComponentWrapperTypeId)) { entityInstance->m_components.emplace_back(component); + component->SetAlias(componentKey); } } diff --git a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/AdapterBuilder.cpp b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/AdapterBuilder.cpp index fd436e533949..404f83778f2c 100644 --- a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/AdapterBuilder.cpp +++ b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/AdapterBuilder.cpp @@ -165,6 +165,8 @@ namespace AZ::DocumentPropertyEditor { BeginLabel(); Attribute(Nodes::Label::Value, text); + Attribute(Nodes::PropertyEditor::SharePriorColumn, true); + Attribute(Nodes::PropertyEditor::Alignment, Nodes::PropertyEditor::Align::AlignLeft); EndLabel(); } } // namespace AZ::DocumentPropertyEditor diff --git a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.cpp b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.cpp index a9ffecd22e16..0c022a52fd60 100644 --- a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.cpp +++ b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.cpp @@ -96,6 +96,8 @@ namespace AZ::DocumentPropertyEditor::Nodes system->RegisterNodeAttribute(ContainerActionButton::Action); system->RegisterNodeAttribute(ContainerActionButton::OnActivate); + system->RegisterPropertyEditor(); + system->RegisterPropertyEditor(); system->RegisterPropertyEditor(); system->RegisterPropertyEditor(); diff --git a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h index 7c9dc1cce506..5e096c6b2bd7 100644 --- a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h +++ b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h @@ -204,6 +204,11 @@ namespace AZ::DocumentPropertyEditor::Nodes static constexpr auto OnActivate = CallbackAttributeDefinition("OnActivate"); }; + struct OverrideIcon : PropertyEditorDefinition + { + static constexpr AZStd::string_view Name = "OverrideIcon"; + }; + struct CheckBox : PropertyEditorDefinition { static constexpr AZStd::string_view Name = "CheckBox"; diff --git a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.cpp b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.cpp index 7cb01e8c3a16..1ab8008d075b 100644 --- a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.cpp +++ b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.cpp @@ -228,7 +228,7 @@ namespace AZ::Reflection nullptr); const StackEntry& nodeData = m_stack.back(); - m_serializeContext->EnumerateInstance(&context, nodeData.m_instance, nodeData.m_typeId, nullptr, nullptr); + m_serializeContext->EnumerateInstance(&context, nodeData.m_instance, nodeData.m_typeId, nodeData.m_classData, nodeData.m_classElement); } bool BeginNode( @@ -254,7 +254,7 @@ namespace AZ::Reflection else if (classElement) { AZStd::string_view elementName = classElement->m_name; - if (!elementName.empty()) + if (!elementName.empty() && classElement->m_editData) { path.append("/"); path.append(elementName); diff --git a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.cpp b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.cpp index 3ff3b81b2d33..377296d0f8aa 100644 --- a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.cpp +++ b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.cpp @@ -6,11 +6,12 @@ * */ +#pragma optimize("", off) #include +#include #include #include #include -#include #include #include #include @@ -355,6 +356,7 @@ namespace AZ::DocumentPropertyEditor if (!parentContainer->IsFixedSize()) { + [[maybe_unused]] auto serializedPathAttribute = attributes.Find(AZ::Reflection::DescriptorAttributes::SerializedPath); m_builder.BeginPropertyEditor(); m_builder.Attribute(Nodes::PropertyEditor::SharePriorColumn, true); m_builder.Attribute(Nodes::PropertyEditor::UseMinimumWidth, true); @@ -384,6 +386,10 @@ namespace AZ::DocumentPropertyEditor m_builder.BeginRow(); + AZ::Reflection::AttributeDataType serializedPathAttribute = + attributes.Find(AZ::Reflection::DescriptorAttributes::SerializedPath); + m_adapter->AddIconIfPropertyOverride(&m_builder, serializedPathAttribute.GetString()); + for (const auto& attribute : Nodes::Row::RowAttributes) { auto attributeValue = attributes.Find(attribute->GetName()); @@ -473,47 +479,90 @@ namespace AZ::DocumentPropertyEditor m_builder.Label(labelAttribute.data()); } - AZ::Dom::Value instancePointerValue = AZ::Dom::Utils::MarshalTypedPointerToValue(access.Get(), access.GetType()); - bool hashValue = false; - const AZ::Name PointerTypeFieldName = AZ::Dom::Utils::PointerTypeFieldName; - if (instancePointerValue.IsOpaqueValue() || instancePointerValue.FindMember(PointerTypeFieldName)) + if (serializedPathAttribute.GetString().empty()) { - hashValue = true; + AZ::Dom::Value instancePointerValue = AZ::Dom::Utils::MarshalTypedPointerToValue(access.Get(), access.GetType()); + VisitValue( + instancePointerValue, + access.Get(), + attributes, + // this needs to write the value back into the reflected object via Json serialization + [valuePointer = access.Get(), valueType = access.GetType(), this](const Dom::Value& newValue) + { + // marshal this new value into a pointer for use by the Json serializer + auto marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, valueType); + + rapidjson::Document buffer; + JsonSerializerSettings serializeSettings; + JsonDeserializerSettings deserializeSettings; + serializeSettings.m_serializeContext = m_serializeContext; + deserializeSettings.m_serializeContext = m_serializeContext; + + // serialize the new value to Json, using the original valuePointer as a reference object to generate a minimal diff + JsonSerialization::Store(buffer, buffer.GetAllocator(), marshalledPointer, valuePointer, valueType, serializeSettings); + + // now deserialize that value into the original location + JsonSerialization::Load(valuePointer, valueType, buffer, deserializeSettings); + + // NB: the returned value for serialized pointer values is instancePointerValue, but since this is passed by pointer, + // it will not actually detect a changed dom value. Since we are already writing directly to the DOM before this step, + // it won't affect the calling DPE, however, other DPEs pointed at the same adapter would be unaware of the change, + // and wouldn't update their UI. + // In future, to properly support multiple DPEs on one adapter, we will need to solve this. One way would be to store + // the json serialized value (which is mostly human-readable text) as an attribute, so any change to the Json would + // trigger an update. This would have the advantage of allowing opaque and pointer types to be searchable by the + // string-based Filter adapter. Without this, things like Vector3 will not have searchable values by text. These + // advantages would have to be measured against the size changes in the DOM and the time taken to populate and parse them. return newValue; + return newValue; + }, + false, + false); } - VisitValue( - instancePointerValue, - access.Get(), - attributes, - // this needs to write the value back into the reflected object via Json serialization - [valuePointer = access.Get(), valueType = access.GetType(), this](const Dom::Value& newValue) - { - // marshal this new value into a pointer for use by the Json serializer - auto marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, valueType); - - rapidjson::Document buffer; - JsonSerializerSettings serializeSettings; - JsonDeserializerSettings deserializeSettings; - serializeSettings.m_serializeContext = m_serializeContext; - deserializeSettings.m_serializeContext = m_serializeContext; - - // serialize the new value to Json, using the original valuePointer as a reference object to generate a minimal diff - JsonSerialization::Store(buffer, buffer.GetAllocator(), marshalledPointer, valuePointer, valueType, serializeSettings); - - // now deserialize that value into the original location - JsonSerialization::Load(valuePointer, valueType, buffer, deserializeSettings); - - // NB: the returned value for serialized pointer values is instancePointerValue, but since this is passed by pointer, - // it will not actually detect a changed dom value. Since we are already writing directly to the DOM before this step, - // it won't affect the calling DPE, however, other DPEs pointed at the same adapter would be unaware of the change, - // and wouldn't update their UI. - // In future, to properly support multiple DPEs on one adapter, we will need to solve this. One way would be to store - // the json serialized value (which is mostly human-readable text) as an attribute, so any change to the Json would - // trigger an update. This would have the advantage of allowing opaque and pointer types to be searchable by the - // string-based Filter adapter. Without this, things like Vector3 will not have searchable values by text. These - // advantages would have to be measured against the size changes in the DOM and the time taken to populate and parse them. - return newValue; - }, - false, hashValue); + else + { + const AZ::TypeId opaqueType = access.GetType(); + [[maybe_unused]] void* pointerFromAccess = access.Get(); + AZ::Dom::Value instancePointerValue1 = AZ::Dom::Utils::MarshalTypedPointerToValue(access.Get(), access.GetType()); + void* marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(instancePointerValue1, opaqueType); + // TODO: The below 2 lines can be removed. serialize context somehow gets picked up even if you don't explicitly pass it + // here. + JsonSerializerSettings serializeSettings; + serializeSettings.m_serializeContext = m_serializeContext; + + rapidjson::Document serializedValue; + auto result = JsonSerialization::Store( + serializedValue, serializedValue.GetAllocator(), marshalledPointer, nullptr, opaqueType, serializeSettings); + + AZ::Dom::Value instancePointerValue; + auto outputWriter = instancePointerValue.GetWriteHandler(); + auto convertToAzDomResult = + AZ::Dom::Json::VisitRapidJsonValue(serializedValue, *outputWriter, AZ::Dom::Lifetime::Temporary); + VisitValue( + instancePointerValue, + access.Get(), + attributes, + [valuePointer = access.Get(), opaqueType, this](const Dom::Value& newValue) + { + void* marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, opaqueType); + rapidjson::Document serializedValue; + auto result = JsonSerialization::Store( + serializedValue, serializedValue.GetAllocator(), marshalledPointer, nullptr, opaqueType); + + JsonDeserializerSettings deserializeSettings; + deserializeSettings.m_serializeContext = m_serializeContext; + // now deserialize that value into the original location + JsonSerialization::Load(valuePointer, opaqueType, serializedValue, deserializeSettings); + + AZ::Dom::Value newInstancePointerValue; + auto outputWriter = newInstancePointerValue.GetWriteHandler(); + auto convertToAzDomResult = + AZ::Dom::Json::VisitRapidJsonValue(serializedValue, *outputWriter, AZ::Dom::Lifetime::Temporary); + return newInstancePointerValue; + }, + false, + false); + } + } } @@ -864,3 +913,4 @@ namespace AZ::DocumentPropertyEditor ); } } // namespace AZ::DocumentPropertyEditor +#pragma optimize("", on) diff --git a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.h b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.h index 07fdd53860c8..17e030482d8c 100644 --- a/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.h +++ b/Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.h @@ -8,8 +8,10 @@ #pragma once +#include #include #include +#include namespace AZ::DocumentPropertyEditor { @@ -60,6 +62,10 @@ namespace AZ::DocumentPropertyEditor //! property editor instances has altered its value. void NotifyPropertyChanged(const PropertyChangeInfo& changeInfo); + virtual void AddIconIfPropertyOverride(AdapterBuilder*, const AZStd::string_view&) + { + } + void* GetInstance() { return m_instance; } const void* GetInstance() const { return m_instance; } AZ::TypeId GetTypeId() const { return m_typeId; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.cpp index 24ba43a7a56c..064d19306df1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.cpp @@ -51,11 +51,15 @@ namespace AzToolsFramework } } - bool PrefabOverridePublicHandler::AreOverridesPresent(AZ::EntityId entityId) + bool PrefabOverridePublicHandler::AreOverridesPresent(AZ::EntityId entityId, const AZ::Dom::Path& prefix) { AZStd::pair pathAndLinkIdPair = GetPathAndLinkIdFromFocusedPrefab(entityId); if (!pathAndLinkIdPair.first.IsEmpty() && pathAndLinkIdPair.second != InvalidLinkId) { + if (!prefix.IsEmpty()) + { + pathAndLinkIdPair.first /= prefix; + } return m_prefabOverrideHandler.AreOverridesPresent(pathAndLinkIdPair.first, pathAndLinkIdPair.second); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.h index 16077aa6df69..9b50380e84db 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicHandler.h @@ -32,7 +32,7 @@ namespace AzToolsFramework //! this function specifically checks for overrides from the focused prefab. //! @param entityId The id of the entity to check for overrides. //! @return true if overrides are present on the given entity id from the focused prefab. - bool AreOverridesPresent(AZ::EntityId entityId) override; + bool AreOverridesPresent(AZ::EntityId entityId, const AZ::Dom::Path& prefix = {}) override; //! Gets the override type on the given entity id. Overrides can come from any ancestor prefab but //! this function specifically checks for overrides from the focused prefab. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicInterface.h index 052f7fb66622..2a746c6f56ed 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverridePublicInterface.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -25,7 +26,7 @@ namespace AzToolsFramework //! by the class implmenting this interface based on certain selections in the editor. eg: the prefab currently being edited. //! @param entityId The id of the entity to check for overrides. //! @return true if overrides are present on the given entity id. - virtual bool AreOverridesPresent(AZ::EntityId entityId) = 0; + virtual bool AreOverridesPresent(AZ::EntityId entityId, const AZ::Dom::Path& prefix = {}) = 0; //! Gets the override type on the given entity id. The prefab that creates the overrides is identified //! by the class implmenting this interface based on certain selections in the editor. eg: the prefab currently being edited. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.h index 5a37f925f6f0..7d99e67a5f05 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.h @@ -117,6 +117,16 @@ namespace AzToolsFramework virtual void Deactivate() override; ////////////////////////////////////////////////////////////////////////// + void SetAlias(const AZStd::string& alias) override + { + m_alias = alias; + } + + AZStd::string GetAlias() override + { + return m_alias; + } + /** * Gets the transform interface of the entity that the component * belongs to, if the entity has a transform component. @@ -191,6 +201,7 @@ namespace AzToolsFramework static void Reflect(AZ::ReflectContext* context); private: + AZStd::string m_alias; AZ::TransformInterface* m_transform; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.cpp index 515fc0970f2d..47b740c6cba3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.cpp @@ -6,16 +6,39 @@ * */ +#include #include - +#include +#include +#include #include - namespace AZ::DocumentPropertyEditor { - ComponentAdapter::ComponentAdapter() = default; + ComponentAdapter::ComponentAdapter() + { + m_propertyChangeHandler = ReflectionAdapter::PropertyChangeEvent::Handler( + [this](const ReflectionAdapter::PropertyChangeInfo& changeInfo) + { + this->GeneratePropertyEditPatch(changeInfo); + }); + ConnectPropertyChangeHandler(m_propertyChangeHandler); + m_prefabOverridePublicInterface = AZ::Interface::Get(); + if (m_prefabOverridePublicInterface == nullptr) + { + AZ_Assert(false, "Could not get PrefabOverridePublicInterface on ComponentAdapter construction."); + return; + } + + } ComponentAdapter::ComponentAdapter(AZ::Component* componentInstace) { + m_propertyChangeHandler = ReflectionAdapter::PropertyChangeEvent::Handler( + [this](const ReflectionAdapter::PropertyChangeInfo& changeInfo) + { + this->GeneratePropertyEditPatch(changeInfo); + }); + ConnectPropertyChangeHandler(m_propertyChangeHandler); SetComponent(componentInstace); } @@ -76,6 +99,14 @@ namespace AZ::DocumentPropertyEditor AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::BusConnect(); AZ::Uuid instanceTypeId = azrtti_typeid(m_componentInstance); SetValue(m_componentInstance, instanceTypeId); + m_componentAlias = componentInstance->GetAlias(); + auto owningInstance = + AZ::Interface::Get()->FindOwningInstance(componentInstance->GetEntityId()); + AZ_Assert(owningInstance.has_value(), "Entity owning the component doesn't have an owning prefab instance."); + auto entityAlias = owningInstance->get().GetEntityAlias(componentInstance->GetEntityId()); + AZ_Assert(entityAlias.has_value(), "Owning entity of component doesn't have a valid entity alias in the owning prefab."); + m_entityAlias = entityAlias->get(); + m_entityId = m_componentInstance->GetEntityId(); } void ComponentAdapter::DoRefresh() @@ -111,9 +142,7 @@ namespace AZ::DocumentPropertyEditor else { AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult( - m_currentUndoNode, - &AzToolsFramework::ToolsApplicationRequests::BeginUndoBatch, - "Modify Entity Property"); + m_currentUndoNode, &AzToolsFramework::ToolsApplicationRequests::BeginUndoBatch, "Modify Entity Property"); } AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast( @@ -138,4 +167,82 @@ namespace AZ::DocumentPropertyEditor return returnValue; } + void ComponentAdapter::AddIconIfPropertyOverride(AdapterBuilder* adapterBuilder, const AZStd::string_view& serializedPath) + { + AZ::Dom::Path prefabPatchPath(AzToolsFramework::Prefab::PrefabDomUtils::ComponentsName); + prefabPatchPath /= m_componentAlias; + prefabPatchPath /= AZ::Dom::Path(serializedPath); + if (!serializedPath.empty() && m_prefabOverridePublicInterface->AreOverridesPresent(m_entityId, prefabPatchPath)) + { + adapterBuilder->BeginPropertyEditor(); + adapterBuilder->Attribute(Nodes::PropertyEditor::SharePriorColumn, true); + adapterBuilder->Attribute(Nodes::PropertyEditor::UseMinimumWidth, true); + adapterBuilder->Attribute(Nodes::PropertyEditor::Alignment, Nodes::PropertyEditor::Align::AlignLeft); + adapterBuilder->EndPropertyEditor(); + } + } + + void ComponentAdapter::GeneratePropertyEditPatch(const ReflectionAdapter::PropertyChangeInfo& propertyChangeInfo) + { + if (propertyChangeInfo.changeType == Nodes::ValueChangeType::FinishedEdit) + { + AZ::Dom::Value domValue = GetContents(); + AZ::Dom::Path serializedPath = propertyChangeInfo.path / Reflection::DescriptorAttributes::SerializedPath; + + AZ::Dom::Path prefabPatchPath(AzToolsFramework::Prefab::PrefabDomUtils::EntitiesName); + prefabPatchPath /= m_entityAlias; + prefabPatchPath /= AzToolsFramework::Prefab::PrefabDomUtils::ComponentsName; + prefabPatchPath /= m_componentAlias; + prefabPatchPath /= serializedPath; + + + AzToolsFramework::Prefab::PrefabDom prefabPatch; + prefabPatch.SetObject(); + + AZStd::string patchPath = domValue[serializedPath].GetString(); + rapidjson::Value path = rapidjson::Value(patchPath.c_str(), + aznumeric_caster(patchPath.size()), + prefabPatch.GetAllocator()); + prefabPatch.AddMember(rapidjson::StringRef("op"), rapidjson::StringRef("replace"), prefabPatch.GetAllocator()) + .AddMember( + rapidjson::StringRef("path"), + rapidjson::Value(patchPath.c_str(), aznumeric_caster(patchPath.size())), + prefabPatch.GetAllocator()); + + + if (propertyChangeInfo.newValue.IsOpaqueValue()) + { + AZStd::any opaqueValue = propertyChangeInfo.newValue.GetOpaqueValue(); + void* marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(propertyChangeInfo.newValue, opaqueValue.type()); + rapidjson::Document patchValue; + auto result = + JsonSerialization::Store(patchValue, prefabPatch.GetAllocator(), marshalledPointer, nullptr, opaqueValue.type()); + prefabPatch.AddMember(rapidjson::StringRef("value"), AZStd::move(patchValue), prefabPatch.GetAllocator()); + } + else + { + auto convertToRapidJsonOutcome = AZ::Dom::Json::WriteToRapidJsonDocument( + [propertyChangeInfo](AZ::Dom::Visitor& visitor) + { + const bool copyStrings = false; + return propertyChangeInfo.newValue.Accept(visitor, copyStrings); + }); + + if (!convertToRapidJsonOutcome.IsSuccess()) + { + AZ_Assert(false, "PrefabDom value converted from AZ::Dom::Value."); + } + else + { + AzToolsFramework::Prefab::PrefabDom prefabPatchValue = convertToRapidJsonOutcome.TakeValue(); + prefabPatch.AddMember(rapidjson::StringRef("value"), AZStd::move(prefabPatchValue), prefabPatch.GetAllocator()); + AZ_Warning("Prefab", !prefabPatch.IsNull(), "Prefab patch generated from DPE is null"); + } + } + + auto* prefabFocusPublicInterface = AZ::Interface::Get(); + AZ_Assert(prefabFocusPublicInterface, "PrefabFocusPublicInterface cannot be fetched."); + } + } + } // namespace AZ::DocumentPropertyEditor diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.h index 6a354ea1a923..c60a7ca26d36 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DPEComponentAdapter.h @@ -10,7 +10,10 @@ #include #include +#include +#include #include +#include #include namespace AZ::DocumentPropertyEditor @@ -46,13 +49,25 @@ namespace AZ::DocumentPropertyEditor Dom::Value HandleMessage(const AdapterMessage& message) override; + void AddIconIfPropertyOverride(AdapterBuilder* adapterBuilder, const AZStd::string_view& serializedPath) override; + protected: + + void GeneratePropertyEditPatch(const ReflectionAdapter::PropertyChangeInfo& propertyChangeInfo); + + AZStd::string m_entityAlias; + AZStd::string m_componentAlias; + AZ::EntityId m_entityId; + + ReflectionAdapter::PropertyChangeEvent::Handler m_propertyChangeHandler; AZ::Component* m_componentInstance = nullptr; AzToolsFramework::UndoSystem::URSequencePoint* m_currentUndoNode = nullptr; enum AzToolsFramework::PropertyModificationRefreshLevel m_queuedRefreshLevel = AzToolsFramework::PropertyModificationRefreshLevel::Refresh_None; + + AzToolsFramework::Prefab::PrefabOverridePublicInterface* m_prefabOverridePublicInterface = nullptr; }; } // namespace AZ::DocumentPropertyEditor diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.cpp index d7dbca4b92ec..73f2e0dbb4b0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.cpp @@ -5,6 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#pragma optimize("", off) #include "DocumentPropertyEditor.h" #include @@ -1499,3 +1500,4 @@ namespace AzToolsFramework message.Match(AZ::DocumentPropertyEditor::Nodes::Adapter::QueryKey, showKeyQueryDialog); } } // namespace AzToolsFramework +#pragma optimize("", on) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/OverrideIconHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/OverrideIconHandler.cpp new file mode 100644 index 000000000000..8aecc0263921 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/OverrideIconHandler.cpp @@ -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 + * + */ + +#include + +namespace AzToolsFramework +{ + OverrideIconHandler::OverrideIconHandler() + { + setContextMenuPolicy(Qt::CustomContextMenu); + //qDebug() << connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); + qDebug() << connect(this, &OverrideIconHandler::customContextMenuRequested, this, &OverrideIconHandler::showContextMenu); + } + + void OverrideIconHandler::SetValueFromDom(const AZ::Dom::Value& node) + { + static QIcon s_overrideIcon(QStringLiteral(":/Entity/entity_modified_as_override.svg")); + + // Cache the node so we can query OnActivate on it when we're pressed. + m_node = node; + + setIcon(s_overrideIcon); + setIconSize(QSize(8, 8)); + } + + void OverrideIconHandler::showContextMenu(const QPoint& position) + { + QMenu contextMenu; + QAction* revertAction = contextMenu.addAction(tr("Revert Override")); + + QAction* selectedItem = contextMenu.exec(mapToGlobal(position)); + + if (selectedItem == revertAction) + { + AZ_Warning("Prefab", false, "Action is clicked"); + } + } +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/OverrideIconHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/OverrideIconHandler.h new file mode 100644 index 000000000000..7a9237b2e1c3 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/OverrideIconHandler.h @@ -0,0 +1,41 @@ +/* + * 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 +#include +#include +#include + + +namespace AzToolsFramework +{ + class OverrideIconHandler + : public PropertyHandlerWidget + { + public: + OverrideIconHandler(); + + void SetValueFromDom(const AZ::Dom::Value& node); + + static constexpr const AZStd::string_view GetHandlerName() + { + return AZ::DocumentPropertyEditor::Nodes::OverrideIcon::Name; + } + + public slots: + void showContextMenu(const QPoint&); + + private: + AZ::Dom::Value m_node; + }; +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/PropertyEditorToolsSystem.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/PropertyEditorToolsSystem.cpp index 7091dd4322b8..5358647f98ee 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/PropertyEditorToolsSystem.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/PropertyEditorToolsSystem.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace AzToolsFramework { @@ -27,6 +28,7 @@ namespace AzToolsFramework void PropertyEditorToolsSystem::RegisterDefaultHandlers() { PropertyEditorToolsSystemInterface::RegisterHandler(); + PropertyEditorToolsSystemInterface::RegisterHandler(); } PropertyEditorToolsSystem::PropertyHandlerId PropertyEditorToolsSystem::GetPropertyHandlerForNode(const AZ::Dom::Value node) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h index 84577a70ef4d..a87a0ae2f99e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h @@ -5,6 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #ifndef PROPERTYEDITORAPI_INTERNALS_H #define PROPERTYEDITORAPI_INTERNALS_H @@ -16,6 +17,7 @@ // and implement that interface, then register it with the property manager. #include +#include #include #include #include @@ -265,7 +267,11 @@ namespace AzToolsFramework auto value = AZ::DocumentPropertyEditor::Nodes::PropertyEditor::Value.ExtractFromDomNode(node); if (value.has_value()) { - m_proxyValue = AZ::Dom::Utils::ValueToType(value.value()).value_or(m_proxyValue); + AZ::JsonSerializationResult::ResultCode loadResult = AZ::Dom::Utils::LoadViaJsonSerialization(m_proxyValue, value.value()); + if (loadResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + m_proxyValue = AZ::Dom::Utils::ValueToType(value.value()).value_or(m_proxyValue); + } } m_rpeHandler.ConsumeAttributes_Internal(GetWidget(), &m_proxyNode); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 29382983eb50..13ef5f97bd89 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -396,6 +396,8 @@ set(FILES UI/DocumentPropertyEditor/FilteredDPE.cpp UI/DocumentPropertyEditor/FilteredDPE.h UI/DocumentPropertyEditor/FilteredDPE.ui + UI/DocumentPropertyEditor/OverrideIconHandler.cpp + UI/DocumentPropertyEditor/OverrideIconHandler.h UI/DocumentPropertyEditor/PropertyEditorToolsSystemInterface.h UI/DocumentPropertyEditor/PropertyEditorToolsSystem.cpp UI/DocumentPropertyEditor/PropertyEditorToolsSystem.h diff --git a/Registry/o3de.editor.setreg b/Registry/o3de.editor.setreg index dcf0862ec047..96392e36ab82 100644 --- a/Registry/o3de.editor.setreg +++ b/Registry/o3de.editor.setreg @@ -4,7 +4,9 @@ "ConsoleCommands": { "sys_PakWarnOnPakAccessFailures": 0, "cl_assetLoadWarningEnable": true, - "cl_assetLoadWarningMsThreshold": 100 + "cl_assetLoadWarningMsThreshold": 100, + "ed_enableDPE": true, + "ed_DebugDPE": false } } }