mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-01 09:37:03 +02:00
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
167 lines
6.2 KiB
C#
167 lines
6.2 KiB
C#
using System;
|
|
using Robust.Client.UserInterface.Controls;
|
|
|
|
namespace Robust.Client.UserInterface
|
|
{
|
|
/// <param name="owner">Control on which the property was changed.</param>
|
|
/// <param name="eventArgs"></param>
|
|
public delegate void AttachedPropertyChangedCallback(Control owner, AttachedPropertyChangedEventArgs eventArgs);
|
|
|
|
public delegate void AttachedPropertyChangedCallback<T>(Control owner,
|
|
AttachedPropertyChangedEventArgs<T> eventArgs);
|
|
|
|
/// <summary>
|
|
/// An attached property is a property that can be assigned to any control,
|
|
/// without having to modify the base <see cref="Control" /> class to add it.
|
|
/// This is useful for storing data for specific controls like <see cref="LayoutContainer" />
|
|
/// </summary>
|
|
[Virtual]
|
|
public class AttachedProperty
|
|
{
|
|
/// <summary>
|
|
/// The name of the property.
|
|
/// </summary>
|
|
public string Name { get; }
|
|
|
|
/// <summary>
|
|
/// The type that defines the attached property.
|
|
/// </summary>
|
|
public Type OwningType { get; }
|
|
|
|
/// <summary>
|
|
/// The type of the value stored in the property.
|
|
/// </summary>
|
|
public Type PropertyType { get; }
|
|
|
|
/// <summary>
|
|
/// The default value of the property.
|
|
/// This is returned if no value is set and <see cref="Control.GetValue"/> is called.
|
|
/// </summary>
|
|
public object? DefaultValue { get; }
|
|
|
|
/// <summary>
|
|
/// An optional validation function.
|
|
/// If the value to <see cref="Control.SetValue"/> fails this check, an exception will be thrown.
|
|
/// </summary>
|
|
public Func<object?, bool>? Validate { get; }
|
|
|
|
/// <summary>
|
|
/// A callback to run whenever this property changes on a control.
|
|
/// </summary>
|
|
public AttachedPropertyChangedCallback? Changed { get; }
|
|
|
|
internal AttachedProperty(string name, Type owningType, Type propertyType,
|
|
object? defaultValue = null,
|
|
Func<object?, bool>? validate = null,
|
|
AttachedPropertyChangedCallback? changed = null)
|
|
{
|
|
Name = name;
|
|
OwningType = owningType;
|
|
PropertyType = propertyType;
|
|
DefaultValue = defaultValue;
|
|
Validate = validate;
|
|
Changed = changed;
|
|
}
|
|
|
|
/// <remarks>
|
|
/// Parameters correspond to properties on this class.
|
|
/// </remarks>
|
|
public static AttachedProperty Create(
|
|
string name, Type owningType, Type propertyType,
|
|
object? defaultValue = null,
|
|
Func<object?, bool>? validate = null,
|
|
AttachedPropertyChangedCallback? changed = null)
|
|
{
|
|
if (propertyType.IsValueType && defaultValue == null)
|
|
{
|
|
// Use activator to create uninitialized version of value type.
|
|
defaultValue = Activator.CreateInstance(propertyType)!;
|
|
}
|
|
|
|
return new AttachedProperty(name, owningType, propertyType, defaultValue, validate, changed);
|
|
}
|
|
}
|
|
|
|
public sealed class AttachedProperty<T> : AttachedProperty
|
|
{
|
|
public new Func<T, bool>? Validate { get; }
|
|
|
|
public new AttachedPropertyChangedCallback<T>? Changed { get; }
|
|
|
|
public new T DefaultValue { get; }
|
|
|
|
internal AttachedProperty(string name, Type owningType, T defaultValue,
|
|
Func<T, bool>? validate = null, AttachedPropertyChangedCallback<T>? changed = null)
|
|
: base(name, owningType, typeof(T), defaultValue,
|
|
validate != null ? o => validate!((T) o!) : null,
|
|
changed != null
|
|
? (o, ev) => changed!(o, new AttachedPropertyChangedEventArgs<T>((T) ev.NewValue!, (T) ev.OldValue!))
|
|
: null)
|
|
{
|
|
Validate = validate;
|
|
Changed = changed;
|
|
DefaultValue = defaultValue;
|
|
}
|
|
|
|
public static AttachedProperty<T> Create(string name, Type owningType,
|
|
T defaultValue = default!,
|
|
Func<T, bool>? validate = null,
|
|
AttachedPropertyChangedCallback<T>? changed = null)
|
|
{
|
|
if (!typeof(T).IsValueType && defaultValue == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(defaultValue),
|
|
"Got defaultValue that is null for reference type." +
|
|
"If this is a non-nullable reference type," +
|
|
"make sure to fill in a default value with the parameter." +
|
|
"If this is intended to be nullable," +
|
|
"use the CreateNull() overload (and make sure to set the type nullability correctly!).");
|
|
}
|
|
|
|
return new AttachedProperty<T>(name, owningType, defaultValue, validate, changed);
|
|
}
|
|
|
|
// TODO: C# 9: use nullable T on the returned attached property here.
|
|
public static AttachedProperty<T> CreateNull(string name, Type owningType,
|
|
T defaultValue = default!,
|
|
Func<T, bool>? validate = null,
|
|
AttachedPropertyChangedCallback<T>? changed = null)
|
|
{
|
|
if (typeof(T).IsValueType)
|
|
{
|
|
throw new ArgumentException("Type must not be a value type. Use regular create for that" +
|
|
" (yes, even for nullable value types).");
|
|
}
|
|
return new AttachedProperty<T>(name, owningType, defaultValue, validate, changed);
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event args for when an attached property on a control changes.
|
|
/// </summary>
|
|
public readonly struct AttachedPropertyChangedEventArgs
|
|
{
|
|
public AttachedPropertyChangedEventArgs(object? newValue, object? oldValue)
|
|
{
|
|
NewValue = newValue;
|
|
OldValue = oldValue;
|
|
}
|
|
|
|
public object? NewValue { get; }
|
|
public object? OldValue { get; }
|
|
}
|
|
|
|
public readonly struct AttachedPropertyChangedEventArgs<T>
|
|
{
|
|
public T NewValue { get; }
|
|
public T OldValue { get; }
|
|
|
|
public AttachedPropertyChangedEventArgs(T newValue, T oldValue)
|
|
{
|
|
NewValue = newValue;
|
|
OldValue = oldValue;
|
|
}
|
|
}
|
|
}
|