using Facepunch.ActionGraphs;
using Sandbox.Internal;
using static Sandbox.SerializedObject;
namespace Sandbox;
public abstract class SerializedProperty : IValid
{
public virtual SerializedObject Parent { get; }
public virtual bool IsProperty => false;
public virtual bool IsField => false;
public virtual bool IsMethod => false;
public virtual string Name { get; }
public virtual string DisplayName { get; }
public virtual string Description { get; }
public virtual string GroupName { get; }
public virtual int Order { get; }
public virtual bool IsEditable { get; } = true;
public virtual bool IsPublic { get; } = true;
public virtual Type PropertyType { get; }
///
public virtual bool IsValid => true;
///
/// The source filename, if available
///
public virtual string SourceFile { get; }
///
/// The line in the source file, if available
///
public virtual int SourceLine { get; }
///
/// Returns true if the current set value differs from the actual value
///
public virtual bool HasChanges { get; }
///
/// Called when the property value is about to change.
///
public PropertyPreChangeDelegate OnPreChange { get; set; }
///
/// Called when the property value has changed.
///
public PropertyChangedDelegate OnChanged { get; set; }
///
/// Called when the property is about to be edited (eg. in a ControlWidget).
///
public PropertyStartEditDelegate OnStartEdit { get; set; }
///
/// Called when the property has finished being edited (eg. in a ControlWidget).
///
public PropertyFinishEditDelegate OnFinishEdit { get; set; }
public SerializedProperty()
{
_as.Property = this;
}
//
// Accessors
//
public abstract void SetValue( T value );
public virtual void SetValue( T value, SerializedProperty source ) => SetValue( value );
public abstract T GetValue( T defaultValue = default );
///
/// Get the default value of a specific property type.
///
///
public object GetDefault()
{
// DefaultValue codegen
if ( TryGetAttribute( out var defaultValue ) )
{
return defaultValue.Value;
}
var type = PropertyType;
if ( IsNullable )
{
type = NullableType;
}
if ( type == typeof( Color ) ) return Color.White;
if ( type == typeof( Color32 ) ) return Color32.White;
if ( type == typeof( ColorHsv ) ) return new ColorHsv( 0, 0, 1 );
if ( type.IsValueType )
{
return Activator.CreateInstance( type );
}
return null;
}
//
// Property Access
//
///
/// Return true if the property has this attribute
///
public bool HasAttribute() where T : Attribute
{
return GetAttributes().Any();
}
///
/// Return true if the property has this attribute
///
public bool HasAttribute( Type t )
{
return GetAttributes( t ).Any();
}
///
/// Try to get this attribute from the property. Return false on fail.
///
public bool TryGetAttribute( out T attribute ) where T : Attribute
{
attribute = GetAttributes().FirstOrDefault();
return attribute != null;
}
///
/// Get all of these attributes from the property.
///
public IEnumerable GetAttributes() where T : Attribute
{
return GetAttributes().OfType();
}
///
/// Get all of these attributes from the property.
///
public IEnumerable GetAttributes( Type t )
{
return GetAttributes().Where( t.IsInstanceOfType );
}
///
/// Get all attributes from the property.
///
public virtual IEnumerable GetAttributes()
{
return Enumerable.Empty();
}
///
/// Try to convert this property into a serialized object for further editing and exploration
///
///
///
public virtual bool TryGetAsObject( out SerializedObject obj )
{
obj = default;
return false;
}
AsAccessor _as;
public virtual ref AsAccessor As => ref _as;
public struct AsAccessor
{
internal SerializedProperty Property;
public string String
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public Vector2 Vector2
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public Vector3 Vector3
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public Rotation Rotation
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public Angles Angles
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public float Float
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public double Double
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public int Int
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public long Long
{
get => Property.GetValue();
set => Property.SetValue( value );
}
public bool Bool
{
get => Property.GetValue();
set => Property.SetValue( value );
}
}
///
/// True if this holds multiple values. That might all be the same.
///
public virtual bool IsMultipleValues => false;
///
/// True if this holds multiple values, and they're all different.
///
public virtual bool IsMultipleDifferentValues => false;
///
/// Get all properties if this holds multiple values
///
public virtual IEnumerable MultipleProperties
{
get
{
yield return this;
}
}
///
/// Our value has changed, maybe our parent would like to know
///
protected virtual void NoteChanged()
{
if ( OnChanged is not null )
{
OnChanged( this );
}
if ( Parent is not null )
{
Parent.NoteChanged( this );
}
}
internal virtual void NoteChanged( SerializedProperty childProperty )
{
if ( OnChanged is not null )
{
OnChanged( childProperty );
}
if ( Parent is not null )
{
Parent.NoteChanged( childProperty );
}
}
protected virtual void NotePreChange()
{
if ( OnPreChange is not null )
{
OnPreChange( this );
}
if ( Parent is not null )
{
Parent.NotePreChange( this );
}
}
internal virtual void NotePreChange( SerializedProperty childProperty )
{
if ( OnPreChange is not null )
{
OnPreChange( childProperty );
}
if ( Parent is not null )
{
Parent.NotePreChange( childProperty );
}
}
protected virtual void NoteStartEdit()
{
if ( OnStartEdit is not null )
{
OnStartEdit( this );
}
if ( Parent is not null )
{
Parent.NoteStartEdit( this );
}
}
internal virtual void NoteStartEdit( SerializedProperty childProperty )
{
if ( OnStartEdit is not null )
{
OnStartEdit( childProperty );
}
if ( Parent is not null )
{
Parent.NoteStartEdit( childProperty );
}
}
protected virtual void NoteFinishEdit()
{
if ( OnFinishEdit is not null )
{
OnFinishEdit( this );
}
if ( Parent is not null )
{
Parent.NoteFinishEdit( this );
}
}
internal virtual void NoteFinishEdit( SerializedProperty childProperty )
{
if ( OnFinishEdit is not null )
{
OnFinishEdit( childProperty );
}
if ( Parent is not null )
{
Parent.NoteFinishEdit( childProperty );
}
}
///
/// Convert an object value to a T type
///
protected T ValueToType( object value, T defaultValue = default )
{
try
{
if ( value is null )
return defaultValue;
if ( value.GetType().IsAssignableTo( typeof( T ) ) )
return (T)value;
if ( typeof( T ) == typeof( string ) )
return (T)(object)$"{value}";
if ( value.GetType() == typeof( string ) )
{
return JsonSerializer.Deserialize( (string)value );
}
// Convert.ChangeType doesn't support long to enum
if ( typeof( T ).IsEnum && value is IConvertible )
{
try
{
return (T)Enum.ToObject( typeof( T ), Convert.ToInt64( value ) );
}
catch
{
return defaultValue;
}
}
var converted = Convert.ChangeType( value, typeof( T ) );
if ( converted is not null )
return (T)converted;
var jsonElement = JsonSerializer.SerializeToElement( value );
return jsonElement.Deserialize();
}
catch ( System.Exception )
{
return defaultValue;
}
}
///
/// If this entry is a dictionary, we can get the key for it here
///
public virtual SerializedProperty GetKey() => null;
// Func _shouldShowCache;
///
/// Returns true if this property should be shown in the inspector
///
public bool ShouldShow()
{
if ( HasAttribute() ) return false;
if ( Parent is null ) return true;
var conditionals = GetAttributes();
if ( !conditionals.Any() ) return true;
return !conditionals.All( x => x.TestCondition( Parent ) );
}
///
/// Return true if this is a nullable value type
///
public bool IsNullable
{
get
{
return Nullable.GetUnderlyingType( PropertyType ) is not null;
}
}
///
/// If this is a nullable type, this will return the nullable target type
///
public Type NullableType
{
get
{
return Nullable.GetUnderlyingType( PropertyType );
}
}
///
/// True if the value is null
///
public bool IsNull
{
get
{
return GetValue