diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index 04a20fdb4..f225c29d8 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -442,7 +442,7 @@ namespace FlaxEngine.Json /// public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) { - var result = Activator.CreateInstance(objectType); + var result = existingValue ?? Activator.CreateInstance(objectType); if (reader.TokenType == JsonToken.String) { JsonSerializer.ParseID((string)reader.Value, out var id); @@ -483,6 +483,34 @@ namespace FlaxEngine.Json } } + internal class ControlReferenceConverter : JsonConverter + { + /// + public override unsafe void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) + { + var id = (value as IControlReference)?.UIControl?.ID ?? Guid.Empty; + writer.WriteValue(JsonSerializer.GetStringID(&id)); + } + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) + { + var result = existingValue ?? Activator.CreateInstance(objectType); + if (reader.TokenType == JsonToken.String && result is IControlReference controlReference) + { + JsonSerializer.ParseID((string)reader.Value, out var id); + controlReference.UIControl = Object.Find(ref id); + } + return result; + } + + /// + public override bool CanConvert(Type objectType) + { + return objectType.Name.StartsWith("ControlReference", StringComparison.Ordinal); + } + } + /* /// /// Serialize Guid values using `N` format diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index f13be1e8f..59b13c549 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -194,6 +194,7 @@ namespace FlaxEngine.Json settings.Converters.Add(new SoftObjectReferenceConverter()); settings.Converters.Add(new SoftTypeReferenceConverter()); settings.Converters.Add(new BehaviorKnowledgeSelectorAnyConverter()); + settings.Converters.Add(new ControlReferenceConverter()); settings.Converters.Add(new MarginConverter()); settings.Converters.Add(new VersionConverter()); settings.Converters.Add(new LocalizedStringConverter()); diff --git a/Source/Engine/UI/ControlReference.cs b/Source/Engine/UI/ControlReference.cs index dba3a3397..0bcd54374 100644 --- a/Source/Engine/UI/ControlReference.cs +++ b/Source/Engine/UI/ControlReference.cs @@ -1,84 +1,183 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.CompilerServices; using FlaxEngine.GUI; -namespace FlaxEngine; - -/// -/// Interface for control references access. -/// -public interface IControlReference +namespace FlaxEngine { /// - /// Gets or sets the reference to actor. + /// Interface for control references access. /// - public UIControl UIControl { get; set; } + public interface IControlReference + { + /// + /// Gets or sets the reference to actor. + /// + public UIControl UIControl { get; set; } + + /// + /// Gets the type of the control the interface uses. + /// + public Type ControlType { get; } + } /// - /// Gets the type of the control the interface uses. + /// UI Control reference utility. References UI Control actor with a typed control type. /// - public Type ControlType { get; } + /// Type of the UI control object. +#if FLAX_EDITOR + [TypeConverter(typeof(TypeConverters.ControlReferenceConverter))] +#endif + public struct ControlReference : IControlReference, IComparable, IComparable>, IEquatable> where T : Control + { + private UIControl _uiControl; + + /// + /// Gets the typed UI control object owned by the referenced actor. + /// + [HideInEditor] + public T Control + { + get + { + if (_uiControl != null && _uiControl.Control is T t) + return t; + Debug.Write(LogType.Warning, "Trying to get Control from ControlReference but UIControl is null, or UIControl.Control is null, or UIControl.Control is not the correct type."); + return null; + } + } + + /// + public UIControl UIControl + { + get => _uiControl; + set + { + if (value == null) + { + _uiControl = null; + } + else if (value.Control is T t) + { + _uiControl = value; + } + else + { + Debug.Write(LogType.Warning, "Trying to set ControlReference but UIControl.Control is null or UIControl.Control is not the correct type."); + } + } + } + + /// + public Type ControlType => typeof(T); + + /// + public override string ToString() + { + return _uiControl?.ToString() ?? "null"; + } + + /// + public override int GetHashCode() + { + return _uiControl?.GetHashCode() ?? 0; + } + + /// + public int CompareTo(object obj) + { + if (obj is IControlReference other) + return CompareTo(other); + return 0; + } + + /// + public int CompareTo(ControlReference other) + { + return _uiControl == other._uiControl ? 0 : 1; + } + + /// + public bool Equals(ControlReference other) + { + return _uiControl == other._uiControl; + } + + /// + public override bool Equals(object obj) + { + return obj is ControlReference other && _uiControl == other._uiControl; + } + + /// + /// The implicit operator for the Control. + /// + /// The control reference. + /// The control object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator T(ControlReference reference) => reference.Control; + + /// + /// The implicit operator for the UIControl. + /// + /// The control reference. + /// The control actor. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator UIControl(ControlReference reference) => reference.UIControl; + + /// + /// Checks if the object exists (reference is not null and the unmanaged object pointer is valid). + /// + /// The object to check. + /// True if object is valid, otherwise false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator bool(ControlReference obj) => obj._uiControl; + + /// + /// Checks whether the two objects are equal. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(ControlReference left, ControlReference right) => 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; + } } -/// -/// UI Control reference utility. References UI Control actor with a typed control type. -/// -[Serializable] -public struct ControlReference : IControlReference where T : Control +#if FLAX_EDITOR +namespace FlaxEngine.TypeConverters { - private UIControl _uiControl; - - /// - /// Gets the typed UI control object owned by the referenced actor. - /// - [HideInEditor] - public T Control + internal class ControlReferenceConverter : TypeConverter { - get + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { - if (_uiControl != null && _uiControl.Control is T t) - return t; - Debug.Write(LogType.Warning, "Trying to get Control from ControlReference but UIControl is null, or UIControl.Control is null, or UIControl.Control is not the correct type."); - return null; + if (value is string valueStr) + { + var result = Activator.CreateInstance(destinationType); + if (result is IControlReference control) + { + Json.JsonSerializer.ParseID(valueStr, out var id); + control.UIControl = Object.Find(ref id); + } + return result; + } + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if (destinationType.Name.StartsWith("ControlReference", StringComparison.Ordinal)) + return true; + return base.CanConvertTo(context, destinationType); } } - - /// - public UIControl UIControl - { - get => _uiControl; - set - { - if (value == null) - { - _uiControl = null; - } - else if (value.Control is T t) - { - _uiControl = value; - } - else - { - Debug.Write(LogType.Warning, "Trying to set ControlReference but UIControl.Control is null or UIControl.Control is not the correct type."); - } - } - } - - /// - public Type ControlType => typeof(T); - - /// - /// The implicit operator for the Control. - /// - /// The ControlReference - /// The Control. - public static implicit operator T(ControlReference reference) => reference.Control; - - /// - /// The implicit operator for the UIControl - /// - /// The ControlReference - /// The UIControl. - public static implicit operator UIControl(ControlReference reference) => reference.UIControl; } +#endif