mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
feat: Add VV editor for tuples (#6065)
* feat: Add VV editor for tuples * refactor: make tuple editor work in more cases * feat: support other arity tuples * fix: correct release notes entry * refactor: use a new index selector for tuples Also yank out silly unused code. * fix: make all non-ValueTuples readonly * refactor: spell out ValueTuple arities ,,,,,,,,,,,,,,,,,,,,,
This commit is contained in:
@@ -43,6 +43,7 @@ END TEMPLATE-->
|
||||
|
||||
* `RobustClientPackaging.WriteClientResources()` and `RobustServerPackaging.WriteServerResources()` now have an overload taking in a set of things to ignore in the content resources directory.
|
||||
* Added `IPrototypeManager.Resolve()`, which logs an error if the resolved prototype does not exist. This is effectively the previous (but not original) default behavior of `IPrototypeManager.TryIndex`.
|
||||
* There's now a ViewVariables property editor for tuples.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
|
||||
110
Robust.Client/ViewVariables/Editors/VVPropEditorTuple.cs
Normal file
110
Robust.Client/ViewVariables/Editors/VVPropEditorTuple.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using CS = System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Client.ViewVariables.Editors;
|
||||
|
||||
internal sealed class VVPropEditorTuple : VVPropEditor
|
||||
{
|
||||
[Dependency] private readonly IClientViewVariablesManagerInternal _viewVariables = default!;
|
||||
|
||||
private bool _readOnly;
|
||||
private readonly List<object?> _tuple = [];
|
||||
private readonly List<VVPropEditor> _editors = [];
|
||||
private Type? _actualType;
|
||||
|
||||
public VVPropEditorTuple()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override Control MakeUI(object? value)
|
||||
{
|
||||
var vBoxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
MinSize = new Vector2(240, 0),
|
||||
};
|
||||
|
||||
if (value is not CS.ITuple tuple)
|
||||
return vBoxContainer;
|
||||
|
||||
// Zero-tuples exist?? I'm just not going to bother with that.
|
||||
if (tuple.Length == 0)
|
||||
return vBoxContainer;
|
||||
|
||||
_actualType = value.GetType();
|
||||
|
||||
// We disallow editing tuples with arity more than 7 since they would
|
||||
// a pain to construct via reflection. And no one should have tuples
|
||||
// that large. (8 is bad because last element becomes a ValueTuple<>)
|
||||
_readOnly = ReadOnly
|
||||
|| tuple.Length >= 8
|
||||
|| !IsValueTuple(_actualType); // ToTuple only supports ValueTuples
|
||||
|
||||
for (var i = 0; i < tuple.Length; i++)
|
||||
{
|
||||
var editor = CreateBox(tuple[i], vBoxContainer);
|
||||
var index = i; // thanks C#
|
||||
editor.OnValueChanged += (o, reinterpret) => ValueChanged(ToTuple(o, index), reinterpret);
|
||||
|
||||
_tuple.Add(tuple[i]);
|
||||
_editors.Add(editor);
|
||||
}
|
||||
return vBoxContainer;
|
||||
}
|
||||
|
||||
private bool IsValueTuple(Type actualType)
|
||||
{
|
||||
if (!actualType.IsGenericType)
|
||||
return false;
|
||||
|
||||
Type[] valueTupleTypes =
|
||||
[
|
||||
typeof(ValueTuple<>), typeof(ValueTuple<,>), typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
|
||||
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>), typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
|
||||
];
|
||||
return valueTupleTypes.Contains(actualType.GetGenericTypeDefinition());
|
||||
}
|
||||
|
||||
private CS.ITuple ToTuple(object? changed, int index)
|
||||
{
|
||||
_tuple[index] = changed;
|
||||
|
||||
// I can't seem to make this work using .GetMethod.
|
||||
// If you know of a better way of doing this... please do.
|
||||
return (CS.ITuple)typeof(ValueTuple).GetMethods()
|
||||
.First(x => x is { Name: nameof(ValueTuple.Create), IsGenericMethod: true }
|
||||
&& x.GetParameters().Length == _tuple.Count)
|
||||
.MakeGenericMethod(_actualType!.GenericTypeArguments)
|
||||
.Invoke(null, _tuple.ToArray())!;
|
||||
}
|
||||
|
||||
private VVPropEditor CreateBox<T>(T? entry, BoxContainer parent)
|
||||
{
|
||||
var editor = _viewVariables.PropertyFor(entry?.GetType());
|
||||
// We disallow editing of serverside-only tuples because, uh, I don't
|
||||
// know how to make it work. Presumably it'd have to be something
|
||||
// similarly cursed to what I did in ToTuple above.
|
||||
parent.AddChild(editor.Initialize(entry, _readOnly));
|
||||
return editor;
|
||||
}
|
||||
|
||||
// Allow selecting, for example, dictionaries within the tuple.
|
||||
// Wait, why do you have a field with a tuple that holds a dictionary??
|
||||
public override void WireNetworkSelector(uint sessionId, object[] selectorChain)
|
||||
{
|
||||
for (var i = 0; i < _editors.Count; i++)
|
||||
{
|
||||
object[] chain = [..selectorChain, new ViewVariablesTupleIndexSelector(i)];
|
||||
_editors[i].WireNetworkSelector(sessionId, chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using CS = System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Client.ViewVariables;
|
||||
|
||||
@@ -63,6 +64,8 @@ internal sealed class ViewVariableControlFactory : IViewVariableControlFactory
|
||||
|| type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>),
|
||||
_ => new VVPropEditorKeyValuePair()
|
||||
);
|
||||
|
||||
RegisterForAssignableFrom<CS.ITuple>(_ => new VVPropEditorTuple());
|
||||
RegisterForAssignableFrom<SoundSpecifier>(_ => new VVPropEditorSoundSpecifier(_protoManager, _resManager));
|
||||
RegisterForAssignableFrom<ISelfSerialize>(type => CreateGenericEditor(type, typeof(VVPropEditorISelfSerializable<>)));
|
||||
RegisterForAssignableFrom<ViewVariablesBlobMembers.PrototypeReferenceToken>(type => CreateGenericEditor(type, typeof(VVPropEditorIPrototype<>)));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Server.ViewVariables.Traits;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
@@ -135,6 +136,10 @@ namespace Robust.Server.ViewVariables
|
||||
dynamic kv = value;
|
||||
value = kvPair.Key ? kv.Key : kv.Value;
|
||||
break;
|
||||
case ViewVariablesTupleIndexSelector indexSelector
|
||||
when value is ITuple tuple:
|
||||
value = indexSelector.Index <= tuple.Length - 1 ? tuple[indexSelector.Index] : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Server.ViewVariables.Traits;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -141,6 +142,19 @@ namespace Robust.Server.ViewVariables
|
||||
};
|
||||
}
|
||||
|
||||
// Handle ValueTuples
|
||||
if (typeof(ITuple).IsAssignableFrom(valType))
|
||||
{
|
||||
var tuple = (ITuple)value;
|
||||
var items = new object?[tuple.Length];
|
||||
for (var i = 0; i < tuple.Length; i++)
|
||||
{
|
||||
items[i] = MakeValueNetSafe(tuple[i]);
|
||||
}
|
||||
|
||||
return new ViewVariablesBlobMembers.ServerTupleToken { Items = items };
|
||||
}
|
||||
|
||||
// Can't send this value type over the wire.
|
||||
return new ViewVariablesBlobMembers.ServerValueTypeToken
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -132,6 +133,19 @@ namespace Robust.Shared.ViewVariables
|
||||
public object Value { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for a non-serializable value-type tuple.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ServerTupleToken : ITuple
|
||||
{
|
||||
public object[] Items { get; set; }
|
||||
|
||||
public object this[int index] => Items[index];
|
||||
|
||||
public int Length => Items.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data for a specific property.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.ViewVariables
|
||||
@@ -38,6 +39,17 @@ namespace Robust.Shared.ViewVariables
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used within <see cref="ViewVariablesSessionRelativeSelector.PropertyIndex"/>
|
||||
/// to refer to an index of a tuple. This should match what would be used
|
||||
/// with <see cref="ITuple.get_Item"/>.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ViewVariablesTupleIndexSelector(int index)
|
||||
{
|
||||
public int Index { get; set; } = index;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ViewVariablesSelectorKeyValuePair
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user