Files
sbox-public/engine/Sandbox.System/SerializedObject/MultiSerializedObject.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

186 lines
5.3 KiB
C#

namespace Sandbox;
/// <summary>
/// An object (or data) that can be accessed as an object
/// </summary>
public class MultiSerializedObject : SerializedObject
{
List<SerializedObject> children = new List<SerializedObject>();
private static TResult SelectDistincSingleOrFallback<TSource, TResult>( IEnumerable<TSource> source, Func<TSource, TResult> selector, TResult fallback )
{
// Last 2 elements is all we need to check if there is more than 2 we want to return fallback
var distinct = source.Select( selector ).Distinct().Take( 2 );
var isSingle = distinct.Count() == 1;
return isSingle ? distinct.FirstOrDefault() : fallback;
}
public override string TypeIcon => SelectDistincSingleOrFallback( children, x => x.TypeIcon, "multiple-icon" );
public override string TypeName => SelectDistincSingleOrFallback( children, x => x.TypeName, "MultipleTypes" );
public override string TypeTitle => SelectDistincSingleOrFallback( children, x => x.TypeTitle, "Multiple Objects" );
public override bool IsValid => children.Where( x => x.IsValid() ).Any();
public MultiSerializedObject()
{
PropertyList = new List<SerializedProperty>();
}
/// <summary>
/// Add an object. Don't forget to rebuild after editing!
/// </summary>
public void Add( SerializedObject obj )
{
obj.OnPropertyChanged += p => OnPropertyChanged?.Invoke( p );
obj.OnPropertyPreChange += p => OnPropertyPreChange?.Invoke( p );
obj.OnPropertyStartEdit += p => OnPropertyStartEdit?.Invoke( p );
obj.OnPropertyFinishEdit += p => OnPropertyFinishEdit?.Invoke( p );
children.Add( obj );
}
/// <summary>
/// Rebuild the object after modifying. This updates PropertyList.
/// </summary>
public void Rebuild()
{
PropertyList.Clear();
SerializedProperty[] childProperties = children.SelectMany( x => x ).ToArray();
var groupedProperties = childProperties.GroupBy( x => x.Name ).ToArray();
foreach ( var group in groupedProperties )
{
if ( group.Count() == 1 )
{
PropertyList.Add( group.First() );
continue;
}
var msp = new MultiSerializedProperty( this, group.Select( x => x ) );
PropertyList.Add( msp );
}
}
/// <summary>
/// True if the target is multiple objects
/// </summary>
public override bool IsMultipleTargets => children.Count > 1;
/// <summary>
/// A list of actual target objects - if applicable
/// </summary>
public override IEnumerable<object> Targets => children.Where( x => x.IsValid() ).SelectMany( x => x.Targets );
}
class MultiSerializedProperty : SerializedProperty
{
readonly List<SerializedProperty> properties;
readonly MultiSerializedObject parent;
public override SerializedObject Parent => parent;
public override string Name => properties.First().Name;
public override string DisplayName => properties.First().DisplayName;
public override string Description => properties.First().Description;
public override string GroupName => properties.First().GroupName;
public override int Order => properties.First().Order;
public override bool IsEditable => properties.First().IsEditable;
public override bool IsPublic => properties.First().IsPublic;
public override Type PropertyType => properties.First().PropertyType;
public override string SourceFile => properties.First().SourceFile;
public override int SourceLine => properties.First().SourceLine;
public override bool HasChanges => properties.First().HasChanges;
public override bool IsProperty => properties.First().IsProperty;
public override bool IsField => properties.First().IsField;
public override bool IsMethod => properties.First().IsMethod;
public override IEnumerable<Attribute> GetAttributes()
{
// TODO: maybe this should be attributes that all targets share?
return properties?.FirstOrDefault()?.GetAttributes() ?? base.GetAttributes();
}
public override bool TryGetAsObject( out SerializedObject obj )
{
var mso = new MultiSerializedObject();
int count = 0;
foreach ( var e in properties )
{
if ( e.TryGetAsObject( out var o ) )
{
mso.Add( o );
count++;
}
}
mso.Rebuild();
obj = mso;
return count > 0;
}
public MultiSerializedProperty( MultiSerializedObject parent, IEnumerable<SerializedProperty> enumerable )
{
this.parent = parent;
properties = enumerable.ToList();
}
public override T GetValue<T>( T defaultValue = default )
{
return properties.First().GetValue<T>( defaultValue );
}
public override void SetValue<T>( T value )
{
NotePreChange();
foreach ( var prop in properties )
{
prop.SetValue( value );
}
NoteChanged();
}
protected override void NoteStartEdit()
{
foreach ( var prop in properties )
{
prop.NoteStartEdit( prop );
}
}
internal override void NoteStartEdit( SerializedProperty childProperty )
{
foreach ( var prop in properties )
{
prop.NoteStartEdit( prop );
}
}
protected override void NoteFinishEdit()
{
foreach ( var prop in properties )
{
prop.NoteFinishEdit( prop );
}
}
internal override void NoteFinishEdit( SerializedProperty childProperty )
{
foreach ( var prop in properties )
{
prop.NoteFinishEdit( prop );
}
}
public override bool IsMultipleValues => properties.Count > 1;
public override bool IsMultipleDifferentValues => properties.Select( x => x.GetValue<object>() ).Distinct().Count() > 1;
public override IEnumerable<SerializedProperty> MultipleProperties => properties;
}