Files
RobustToolbox/Robust.Client/ViewVariables/ViewVariableControlFactory.cs
T
d8aefe5118 feat: now view-variable controls can be registered from content, or even dynamically added (#6077)
* feat: now view-variable controls can be registered from content, or even dynamically added

* refactor: whitespaces and xml-doc

* refactor: added changelog entry

* refactor: added methods for adding condition at start and at the end

* refactor: merged start/end methods, for IViewVariableControlFactory, improved changelog message

* refactor: replaced bool insertLast with InsertPosition enum

* refactor: reverse order of checks registration in ViewVariableControlFactory c-tor

---------

Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru>
2025-07-22 19:48:12 +02:00

143 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Numerics;
using Robust.Client.ViewVariables.Editors;
using Robust.Shared.Audio;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Robust.Client.ViewVariables;
internal sealed class ViewVariableControlFactory : IViewVariableControlFactory
{
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IResourceManager _resManager = default!;
[Dependency] private readonly IDependencyCollection _dependencyManager = default!;
private readonly Dictionary<Type, Func<Type, VVPropEditor>> _factoriesByType = new();
private readonly List<ConditionalViewVariableFactoryMethodContainer> _factoriesWithCondition = new();
public ViewVariableControlFactory()
{
RegisterForType<sbyte>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.SByte));
RegisterForType<byte>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Byte));
RegisterForType<ushort>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.UShort));
RegisterForType<short>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Short));
RegisterForType<uint>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.UInt));
RegisterForType<int>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Int));
RegisterForType<ulong>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.ULong));
RegisterForType<long>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Long));
RegisterForType<float>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Float));
RegisterForType<float>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Float));
RegisterForType<double>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Double));
RegisterForType<decimal>(_ => new VVPropEditorNumeric(VVPropEditorNumeric.NumberType.Decimal));
RegisterForType<string>(_ => new VVPropEditorString());
RegisterForType<EntProtoId?>(_ => new VVPropEditorNullableEntProtoId());
RegisterForType<EntProtoId>(_ => new VVPropEditorEntProtoId());
RegisterForType<Vector2>(_ => new VVPropEditorVector2(intVec: false));
RegisterForType<Vector2i>(_ => new VVPropEditorVector2(intVec: true));
RegisterForType<bool>(_ => new VVPropEditorBoolean());
RegisterForType<Angle>(_ => new VVPropEditorAngle());
RegisterForType<Box2>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2));
RegisterForType<Box2i>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.Box2i));
RegisterForType<UIBox2>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2));
RegisterForType<UIBox2i>(_ => new VVPropEditorUIBox2(VVPropEditorUIBox2.BoxType.UIBox2i));
RegisterForType<EntityCoordinates>(_ => new VVPropEditorEntityCoordinates());
RegisterForType<EntityUid>(_ => new VVPropEditorEntityUid());
RegisterForType<NetEntity>(_ => new VVPropEditorNetEntity());
RegisterForType<Color>(_ => new VVPropEditorColor());
RegisterForType<TimeSpan>(_ => new VVPropEditorTimeSpan());
RegisterWithCondition(
type => type != typeof(ViewVariablesBlobMembers.ServerValueTypeToken) && !type.IsValueType,
_ => new VVPropEditorReference()
);
RegisterWithCondition(
type => type == typeof(ViewVariablesBlobMembers.ServerKeyValuePairToken)
|| type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>),
_ => new VVPropEditorKeyValuePair()
);
RegisterForAssignableFrom<SoundSpecifier>(_ => new VVPropEditorSoundSpecifier(_protoManager, _resManager));
RegisterForAssignableFrom<ISelfSerialize>(type => CreateGenericEditor(type, typeof(VVPropEditorISelfSerializable<>)));
RegisterForAssignableFrom<ViewVariablesBlobMembers.PrototypeReferenceToken>(type => CreateGenericEditor(type, typeof(VVPropEditorIPrototype<>)));
RegisterForAssignableFrom<IPrototype>(type => CreateGenericEditor(type, typeof(VVPropEditorIPrototype<>)));
RegisterWithCondition(
type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ProtoId<>),
type =>
{
var typeArgumentType = type.GenericTypeArguments[0];
var editor = CreateGenericEditor(typeArgumentType, typeof(VVPropEditorProtoId<>));
_dependencyManager.InjectDependencies(editor);
return editor;
}
);
RegisterWithCondition(type => type.IsEnum, _ => new VVPropEditorEnum());
}
/// <inheritdoc />
public void RegisterForType<T>(Func<Type, VVPropEditor> factoryMethod)
{
_factoriesByType[typeof(T)] = factoryMethod;
}
/// <inheritdoc />
public void RegisterForAssignableFrom<T>(Func<Type, VVPropEditor> factoryMethod, InsertPosition insertPosition = InsertPosition.First)
{
int insertIndex = insertPosition == InsertPosition.Last && _factoriesWithCondition.Count > 0
? _factoriesWithCondition.Count - 1
: 0;
_factoriesWithCondition.Insert(insertIndex, new(type => typeof(T).IsAssignableFrom(type), factoryMethod));
}
/// <inheritdoc />
public void RegisterWithCondition(Func<Type, bool> condition, Func<Type, VVPropEditor> factory, InsertPosition insertPosition = InsertPosition.First)
{
int insertIndex = insertPosition == InsertPosition.Last && _factoriesWithCondition.Count > 0
? _factoriesWithCondition.Count - 1
: 0;
_factoriesWithCondition.Insert(insertIndex, new(condition, factory));
}
/// <inheritdoc />
public VVPropEditor CreateFor(Type? type)
{
if (type == null)
{
return new VVPropEditorDummy();
}
if (_factoriesByType.TryGetValue(type, out var factory))
{
return factory(type);
}
foreach (var factoryWithCondition in _factoriesWithCondition)
{
if (factoryWithCondition.CanExecute(type))
{
return factoryWithCondition.Create(type);
}
}
return new VVPropEditorDummy();
}
private static VVPropEditor CreateGenericEditor(Type typeArgumentType, Type genericConrolType)
{
var typeToCreate = genericConrolType.MakeGenericType(typeArgumentType);
return (VVPropEditor)Activator.CreateInstance(typeToCreate)!;
}
private sealed record ConditionalViewVariableFactoryMethodContainer(
Func<Type, bool> CanExecute,
Func<Type, VVPropEditor> Create
);
}