|
@@ -7,10 +7,11 @@
|
|
|
*/
|
|
|
|
|
|
#include <AzCore/Component/ComponentApplicationBus.h>
|
|
|
+#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
|
|
|
#include <AzCore/DOM/DomPrefixTree.h>
|
|
|
#include <AzCore/DOM/DomUtils.h>
|
|
|
+#include <AzCore/Settings/SettingsRegistry.h>
|
|
|
#include <AzCore/std/ranges/ranges_algorithm.h>
|
|
|
-#include <AzFramework/DocumentPropertyEditor/AdapterBuilder.h>
|
|
|
#include <AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h>
|
|
|
#include <AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.h>
|
|
|
#include <AzFramework/DocumentPropertyEditor/ReflectionAdapter.h>
|
|
@@ -25,6 +26,8 @@ namespace AZ::DocumentPropertyEditor
|
|
|
// Look-up table of onChanged callbacks for handling property changes
|
|
|
AZ::Dom::DomPrefixTree<AZStd::function<Dom::Value(const Dom::Value&)>> m_onChangedCallbacks;
|
|
|
|
|
|
+ static constexpr AZStd::string_view InspectorOverrideManagementKey = "/O3DE/Preferences/Prefabs/EnableInspectorOverrideManagement";
|
|
|
+
|
|
|
struct BoundContainer
|
|
|
{
|
|
|
AZ::SerializeContext::IDataContainer* m_container;
|
|
@@ -246,6 +249,54 @@ namespace AZ::DocumentPropertyEditor
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ void VisitValueWithSerializedPath(Reflection::IObjectAccess& access, const Reflection::IAttributes& attributes)
|
|
|
+ {
|
|
|
+ const AZ::TypeId valueType = access.GetType();
|
|
|
+ void* valuePointer = access.Get();
|
|
|
+
|
|
|
+ rapidjson::Document serializedValue;
|
|
|
+ JsonSerialization::Store(serializedValue, serializedValue.GetAllocator(), valuePointer, nullptr, valueType);
|
|
|
+
|
|
|
+ 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, valueType, this](const Dom::Value& newValue)
|
|
|
+ {
|
|
|
+ void* marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, valueType);
|
|
|
+ rapidjson::Document serializedValue;
|
|
|
+ JsonSerialization::Store(serializedValue, serializedValue.GetAllocator(), marshalledPointer, nullptr, valueType);
|
|
|
+
|
|
|
+ JsonDeserializerSettings deserializeSettings;
|
|
|
+ deserializeSettings.m_serializeContext = m_serializeContext;
|
|
|
+ // now deserialize that value into the original location
|
|
|
+ JsonSerialization::Load(valuePointer, valueType, 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool IsInspectorOverrideManagementEnabled()
|
|
|
+ {
|
|
|
+ bool isInspectorOverrideManagementEnabled = false;
|
|
|
+
|
|
|
+ if (auto* registry = AZ::SettingsRegistry::Get())
|
|
|
+ {
|
|
|
+ registry->Get(isInspectorOverrideManagementEnabled, InspectorOverrideManagementKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ return isInspectorOverrideManagementEnabled;
|
|
|
+ }
|
|
|
+
|
|
|
template<class T>
|
|
|
void VisitPrimitive(T& value, const Reflection::IAttributes& attributes)
|
|
|
{
|
|
@@ -384,6 +435,10 @@ namespace AZ::DocumentPropertyEditor
|
|
|
|
|
|
m_builder.BeginRow();
|
|
|
|
|
|
+ AZ::Reflection::AttributeDataType serializedPathAttribute =
|
|
|
+ attributes.Find(AZ::Reflection::DescriptorAttributes::SerializedPath);
|
|
|
+ m_adapter->OnBeginRow(&m_builder, serializedPathAttribute.GetString());
|
|
|
+
|
|
|
for (const auto& attribute : Nodes::Row::RowAttributes)
|
|
|
{
|
|
|
auto attributeValue = attributes.Find(attribute->GetName());
|
|
@@ -480,40 +535,55 @@ namespace AZ::DocumentPropertyEditor
|
|
|
{
|
|
|
hashValue = true;
|
|
|
}
|
|
|
- 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);
|
|
|
+
|
|
|
+ // The IsInspectorOverrideManagementEnabled() check is only temporary until the inspector override management feature set
|
|
|
+ // is fully developed. Since the original utils funtion is in AzToolsFramework and we can't access it from here, we are
|
|
|
+ // duplicating it in this class temporarily till we can do more testing and gain confidence about this new way of storing
|
|
|
+ // serialized values of opaque types directly in the DPE DOM.
|
|
|
+ if (IsInspectorOverrideManagementEnabled() && !serializedPathAttribute.GetString().empty())
|
|
|
+ {
|
|
|
+ VisitValueWithSerializedPath(access, attributes);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -625,6 +695,10 @@ namespace AZ::DocumentPropertyEditor
|
|
|
m_propertyChangeEvent.Signal(changeInfo);
|
|
|
}
|
|
|
|
|
|
+ void ReflectionAdapter::OnBeginRow(AdapterBuilder*, AZStd::string_view)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
Dom::Value ReflectionAdapter::GenerateContents()
|
|
|
{
|
|
|
m_impl->m_builder.BeginAdapter();
|