From bc6f4f50cbea86f2f2044460d1dd6080a545b844 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 7 Mar 2025 18:55:03 +0100 Subject: [PATCH] Fix serialization of `ControlReference` when used in prefabs #3262 --- Source/Engine/Serialization/JsonConverters.cs | 12 +++++- Source/Engine/Serialization/JsonSerializer.cs | 17 ++++++++ Source/Engine/UI/ControlReference.cs | 40 +++++++++++++------ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index f225c29d8..6120ba704 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -492,6 +492,16 @@ namespace FlaxEngine.Json writer.WriteValue(JsonSerializer.GetStringID(&id)); } + /// + public override void WriteJsonDiff(JsonWriter writer, object value, object other, Newtonsoft.Json.JsonSerializer serializer) + { + if (value is IControlReference valueRef && + other is IControlReference otherRef && + JsonSerializer.SceneObjectEquals(valueRef.UIControl, otherRef.UIControl)) + return; + base.WriteJsonDiff(writer, value, other, serializer); + } + /// public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) { @@ -499,7 +509,7 @@ namespace FlaxEngine.Json if (reader.TokenType == JsonToken.String && result is IControlReference controlReference) { JsonSerializer.ParseID((string)reader.Value, out var id); - controlReference.UIControl = Object.Find(ref id); + controlReference.Load(Object.Find(ref id)); } return result; } diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 59b13c549..a87eacb9f 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -228,6 +228,23 @@ namespace FlaxEngine.Json CacheManagedOnly.Dispose(); } + /// + /// The default implementation of the values comparision function used by the serialization system. + /// + /// The object a. + /// The object b. + /// True if both objects are equal, otherwise false. + public static bool SceneObjectEquals(SceneObject objA, SceneObject objB) + { + if (objA == objB) + return true; + if (objA == null || objB == null) + return false; + if (objA.HasPrefabLink && objB.HasPrefabLink) + return objA.PrefabObjectID == objB.PrefabObjectID; + return false; + } + /// /// The default implementation of the values comparision function used by the serialization system. /// diff --git a/Source/Engine/UI/ControlReference.cs b/Source/Engine/UI/ControlReference.cs index 68f50fa23..eee093909 100644 --- a/Source/Engine/UI/ControlReference.cs +++ b/Source/Engine/UI/ControlReference.cs @@ -22,6 +22,12 @@ namespace FlaxEngine /// Gets the type of the control the interface uses. /// public Type ControlType { get; } + + /// + /// Sets control ref by force - used during loading when is not loaded yet. + /// + /// The reference. + internal void Load(UIControl control); } /// @@ -43,11 +49,12 @@ namespace FlaxEngine { get { - if (_uiControl == null) + var control = _uiControl?.Control; + if (control == null) return null; - if (_uiControl.Control is T t) + if (control is T t) return t; - Debug.Write(LogType.Warning, "Trying to get Control from ControlReference but UIControl.Control is null, or the correct type."); + Debug.Write(LogType.Warning, $"Trying to get Control from ControlReference but UIControl.Control is not correct type. It should be {typeof(T)} but is {control.GetType()}."); return null; } } @@ -58,17 +65,18 @@ namespace FlaxEngine get => _uiControl; set { + var control = value?.Control; if (value == null) { _uiControl = null; } - else if (value.Control is T t) + else if (control is T) { _uiControl = value; } else { - Debug.Write(LogType.Warning, "Trying to set UIControl but UIControl.Control is null or not the correct type."); + Debug.Write(LogType.Warning, $"Trying to set UIControl but UIControl.Control is not the correct type. It should be {typeof(T)} but is {control.GetType()}."); } } } @@ -76,6 +84,12 @@ namespace FlaxEngine /// public Type ControlType => typeof(T); + /// + public void Load(UIControl value) + { + _uiControl = value; + } + /// public override string ToString() { @@ -92,26 +106,26 @@ namespace FlaxEngine public int CompareTo(object obj) { if (obj is IControlReference other) - return CompareTo(other); - return 0; + return Json.JsonSerializer.SceneObjectEquals(_uiControl, other.UIControl) ? 0 : 1; + return 1; } /// public int CompareTo(ControlReference other) { - return _uiControl == other._uiControl ? 0 : 1; + return Json.JsonSerializer.SceneObjectEquals(_uiControl, other._uiControl) ? 0 : 1; } /// public bool Equals(ControlReference other) { - return _uiControl == other._uiControl; + return Json.JsonSerializer.SceneObjectEquals(_uiControl, other._uiControl); } /// public override bool Equals(object obj) { - return obj is ControlReference other && _uiControl == other._uiControl; + return obj is ControlReference other && Json.JsonSerializer.SceneObjectEquals(_uiControl, other._uiControl); } /// @@ -142,13 +156,13 @@ namespace FlaxEngine /// Checks whether the two objects are equal. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(ControlReference left, ControlReference right) => left._uiControl == right._uiControl; + public static bool operator ==(ControlReference left, ControlReference right) => Json.JsonSerializer.SceneObjectEquals(left._uiControl, right._uiControl); /// /// Checks whether the two objects are not equal. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(ControlReference left, ControlReference right) => left._uiControl != right._uiControl; + public static bool operator !=(ControlReference left, ControlReference right) => !Json.JsonSerializer.SceneObjectEquals(left._uiControl, right._uiControl); } } @@ -166,7 +180,7 @@ namespace FlaxEngine.TypeConverters if (result is IControlReference control) { Json.JsonSerializer.ParseID(valueStr, out var id); - control.UIControl = Object.Find(ref id); + control.Load(Object.Find(ref id)); } return result; }