Merge branch 'Tryibion-control-reference'
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors;
|
||||
|
||||
/// <summary>
|
||||
/// The reference picker control used for UIControls using ControlReference.
|
||||
/// </summary>
|
||||
internal class UIControlRefPickerControl : FlaxObjectRefPickerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the control to pick.
|
||||
/// </summary>
|
||||
public Type ControlType;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsValid(Object obj)
|
||||
{
|
||||
return obj == null || (obj is UIControl control && control.Control.GetType() == ControlType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ControlReferenceEditor class.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ControlReference<>)), DefaultEditor]
|
||||
internal class ControlReferenceEditor : CustomEditor
|
||||
{
|
||||
private CustomElement<UIControlRefPickerControl> _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
if (!HasDifferentTypes)
|
||||
{
|
||||
_element = layout.Custom<UIControlRefPickerControl>();
|
||||
if (ValuesTypes == null || ValuesTypes[0] == null)
|
||||
{
|
||||
Editor.LogWarning("ControlReference needs to be assigned in code.");
|
||||
return;
|
||||
}
|
||||
Type genType = ValuesTypes[0].GetGenericArguments()[0];
|
||||
if (typeof(Control).IsAssignableFrom(genType))
|
||||
{
|
||||
_element.CustomControl.PresenterContext = Presenter.Owner;
|
||||
_element.CustomControl.ControlType = genType;
|
||||
_element.CustomControl.Type = new ScriptType(typeof(UIControl));
|
||||
}
|
||||
_element.CustomControl.ValueChanged += () =>
|
||||
{
|
||||
Type genericType = ValuesTypes[0].GetGenericArguments()[0];
|
||||
if (typeof(Control).IsAssignableFrom(genericType))
|
||||
{
|
||||
Type t = typeof(ControlReference<>);
|
||||
Type tw = t.MakeGenericType(new Type[] { genericType });
|
||||
var instance = Activator.CreateInstance(tw);
|
||||
((IControlReference)instance).UIControl = (UIControl)_element.CustomControl.Value;
|
||||
SetValue(instance);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
if (Values[0] is IControlReference cr)
|
||||
_element.CustomControl.Value = cr.UIControl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public IPresenterOwner PresenterContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed objects type (given type and all sub classes). Must be <see cref="Object"/> type of any subclass.
|
||||
/// Gets or sets the allowed objects type (given type and all subclasses). Must be <see cref="Object"/> type of any subclass.
|
||||
/// </summary>
|
||||
public ScriptType Type
|
||||
{
|
||||
@@ -61,7 +61,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
throw new ArgumentException(string.Format("Invalid type for FlaxObjectRefEditor. Input type: {0}", value != ScriptType.Null ? value.TypeName : "null"));
|
||||
|
||||
_type = value;
|
||||
_supportsPickDropDown = new ScriptType(typeof(Actor)).IsAssignableFrom(value) || new ScriptType(typeof(Script)).IsAssignableFrom(value);
|
||||
_supportsPickDropDown = new ScriptType(typeof(Actor)).IsAssignableFrom(value) ||
|
||||
new ScriptType(typeof(Script)).IsAssignableFrom(value);
|
||||
|
||||
// Deselect value if it's not valid now
|
||||
if (!IsValid(_value))
|
||||
@@ -80,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (_value == value)
|
||||
return;
|
||||
if (!IsValid(value))
|
||||
throw new ArgumentException("Invalid object type.");
|
||||
value = null;
|
||||
|
||||
// Special case for missing objects (eg. referenced actor in script that is deleted in editor)
|
||||
if (value != null && (Object.GetUnmanagedPtr(value) == IntPtr.Zero || value.ID == Guid.Empty))
|
||||
@@ -91,27 +92,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
// Get name to display
|
||||
if (_value is Script script)
|
||||
{
|
||||
_valueName = script.Actor ? $"{type.Name} ({script.Actor.Name})" : type.Name;
|
||||
}
|
||||
else if (_value != null)
|
||||
{
|
||||
_valueName = _value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
_valueName = string.Empty;
|
||||
}
|
||||
|
||||
// Update tooltip
|
||||
if (_value is SceneObject sceneObject)
|
||||
{
|
||||
TooltipText = Utilities.Utils.GetTooltip(sceneObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
TooltipText = string.Empty;
|
||||
}
|
||||
|
||||
OnValueChanged();
|
||||
}
|
||||
@@ -150,7 +141,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_type = ScriptType.Object;
|
||||
}
|
||||
|
||||
private bool IsValid(Object obj)
|
||||
/// <summary>
|
||||
/// Object validation check routine.
|
||||
/// </summary>
|
||||
/// <param name="obj">Input object to check.</param>
|
||||
/// <returns>True if it can be assigned, otherwise false.</returns>
|
||||
protected virtual bool IsValid(Object obj)
|
||||
{
|
||||
var type = TypeUtils.GetObjectType(obj);
|
||||
return obj == null || _type.IsAssignableFrom(type) && (CheckValid == null || CheckValid(obj, type));
|
||||
@@ -168,6 +164,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
Focus();
|
||||
}, PresenterContext);
|
||||
}
|
||||
else if (new ScriptType(typeof(Control)).IsAssignableFrom(_type))
|
||||
{
|
||||
ActorSearchPopup.Show(this, new Float2(0, Height), IsValid, actor =>
|
||||
{
|
||||
Value = actor as UIControl;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
}, PresenterContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptSearchPopup.Show(this, new Float2(0, Height), IsValid, script =>
|
||||
|
||||
@@ -442,7 +442,7 @@ namespace FlaxEngine.Json
|
||||
/// <inheritdoc />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<UIControl>(ref id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType.Name.StartsWith("ControlReference", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Serialize Guid values using `N` format
|
||||
|
||||
@@ -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());
|
||||
|
||||
183
Source/Engine/UI/ControlReference.cs
Normal file
183
Source/Engine/UI/ControlReference.cs
Normal file
@@ -0,0 +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
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for control references access.
|
||||
/// </summary>
|
||||
public interface IControlReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the reference to <see cref="FlaxEngine.UIControl"/> actor.
|
||||
/// </summary>
|
||||
public UIControl UIControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the control the interface uses.
|
||||
/// </summary>
|
||||
public Type ControlType { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI Control reference utility. References UI Control actor with a typed control type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the UI control object.</typeparam>
|
||||
#if FLAX_EDITOR
|
||||
[TypeConverter(typeof(TypeConverters.ControlReferenceConverter))]
|
||||
#endif
|
||||
public struct ControlReference<T> : IControlReference, IComparable, IComparable<ControlReference<T>>, IEquatable<ControlReference<T>> where T : Control
|
||||
{
|
||||
private UIControl _uiControl;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the typed UI control object owned by the referenced <see cref="FlaxEngine.UIControl"/> actor.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type ControlType => typeof(T);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _uiControl?.ToString() ?? "null";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _uiControl?.GetHashCode() ?? 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is IControlReference other)
|
||||
return CompareTo(other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(ControlReference<T> other)
|
||||
{
|
||||
return _uiControl == other._uiControl ? 0 : 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(ControlReference<T> other)
|
||||
{
|
||||
return _uiControl == other._uiControl;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ControlReference<T> other && _uiControl == other._uiControl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The implicit operator for the Control.
|
||||
/// </summary>
|
||||
/// <param name="reference">The control reference.</param>
|
||||
/// <returns>The control object.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator T(ControlReference<T> reference) => reference.Control;
|
||||
|
||||
/// <summary>
|
||||
/// The implicit operator for the UIControl.
|
||||
/// </summary>
|
||||
/// <param name="reference">The control reference.</param>
|
||||
/// <returns>The control actor.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator UIControl(ControlReference<T> reference) => reference.UIControl;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the object exists (reference is not null and the unmanaged object pointer is valid).
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to check.</param>
|
||||
/// <returns>True if object is valid, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator bool(ControlReference<T> obj) => obj._uiControl;
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two objects are equal.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(ControlReference<T> left, ControlReference<T> right) => left._uiControl == right._uiControl;
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two objects are not equal.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(ControlReference<T> left, ControlReference<T> right) => left._uiControl != right._uiControl;
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class ControlReferenceConverter : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
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<UIControl>(ref id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType.Name.StartsWith("ControlReference", StringComparison.Ordinal))
|
||||
return true;
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user