1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Layer property - Removed generic events in favor of regular ones

Layer property - Added API for easy hiding/showing properties depending on the state of another property 
Layer property - Made properties on property groups opt-out instead of opt-in, you can opt out with [LayerPropertyIgnore] attribute
This commit is contained in:
Robert 2021-01-19 19:32:31 +01:00
parent 52906218dc
commit 52a9253846
12 changed files with 209 additions and 92 deletions

View File

@ -7,7 +7,8 @@
{
KeyframesSupported = false;
DataBindingsSupported = false;
DefaultValue = new ColorGradient();
CurrentValueSet += OnCurrentValueSet;
}
@ -25,11 +26,25 @@
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
}
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs<ColorGradient> e)
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs e)
{
// Don't allow color gradients to be null
if (BaseValue == null)
BaseValue = DefaultValue ?? new ColorGradient();
}
#region Overrides of LayerProperty<ColorGradient>
/// <inheritdoc />
protected override void OnInitialize()
{
// Don't allow color gradients to be null
if (BaseValue == null)
BaseValue = DefaultValue ?? new ColorGradient();
base.OnInitialize();
}
#endregion
}
}

View File

@ -22,7 +22,7 @@
);
}
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs<FloatRange> e)
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs e)
{
// Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new FloatRange(0, 0);

View File

@ -22,7 +22,7 @@
);
}
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs<IntRange> e)
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs e)
{
// Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new IntRange(0, 0);

View File

@ -2,22 +2,6 @@
namespace Artemis.Core
{
/// <summary>
/// Provides strongly typed data for layer property events of type <typeparamref name="T" />.
/// </summary>
public class LayerPropertyEventArgs<T> : EventArgs
{
internal LayerPropertyEventArgs(LayerProperty<T> layerProperty)
{
LayerProperty = layerProperty;
}
/// <summary>
/// Gets the layer property this event is related to
/// </summary>
public LayerProperty<T> LayerProperty { get; }
}
/// <summary>
/// Provides data for layer property events.
/// </summary>

View File

@ -0,0 +1,11 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Represents an attribute that marks a layer property to be ignored
/// </summary>
public class LayerPropertyIgnoreAttribute : Attribute
{
}
}

View File

@ -29,9 +29,9 @@ namespace Artemis.Core
string Path { get; }
/// <summary>
/// Gets the type of the property
/// Gets the type of the property
/// </summary>
Type PropertyType { get; }
Type PropertyType { get; }
/// <summary>
/// Initializes the layer property
@ -59,5 +59,54 @@ namespace Artemis.Core
/// </summary>
/// <param name="timeline">The timeline to apply to the property</param>
void Update(Timeline timeline);
#region Events
/// <summary>
/// Occurs when the layer property is disposed
/// </summary>
public event EventHandler Disposed;
/// <summary>
/// Occurs once every frame when the layer property is updated
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? Updated;
/// <summary>
/// Occurs when the current value of the layer property was updated by some form of input
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? CurrentValueSet;
/// <summary>
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? VisibilityChanged;
/// <summary>
/// Occurs when keyframes are enabled/disabled
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? KeyframesToggled;
/// <summary>
/// Occurs when a new keyframe was added to the layer property
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? KeyframeAdded;
/// <summary>
/// Occurs when a keyframe was removed from the layer property
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
/// <summary>
/// Occurs when a data binding has been enabled
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? DataBindingEnabled;
/// <summary>
/// Occurs when a data binding has been disabled
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? DataBindingDisabled;
#endregion
}
}

View File

@ -79,6 +79,8 @@ namespace Artemis.Core
foreach (IDataBinding dataBinding in _dataBindings)
dataBinding.Dispose();
Disposed?.Invoke(this, EventArgs.Empty);
}
}
@ -442,7 +444,7 @@ namespace Artemis.Core
DataBinding<T, TProperty> dataBinding = new(dataBindingRegistration);
_dataBindings.Add(dataBinding);
OnDataBindingEnabled(new LayerPropertyEventArgs<T>(dataBinding.LayerProperty));
OnDataBindingEnabled(new LayerPropertyEventArgs(dataBinding.LayerProperty));
return dataBinding;
}
@ -460,7 +462,7 @@ namespace Artemis.Core
if (dataBinding.Registration != null)
dataBinding.Registration.DataBinding = null;
dataBinding.Dispose();
OnDataBindingDisabled(new LayerPropertyEventArgs<T>(dataBinding.LayerProperty));
OnDataBindingDisabled(new LayerPropertyEventArgs(dataBinding.LayerProperty));
}
private void UpdateDataBindings(Timeline timeline)
@ -474,6 +476,61 @@ namespace Artemis.Core
#endregion
#region Visbility
/// <summary>
/// Set up a condition to hide the provided layer property when the condition evaluates to <see langword="true" />
/// <para>Note: overrides previous calls to <c>IsHiddenWhen</c> and <c>IsVisibleWhen</c></para>
/// </summary>
/// <typeparam name="TP">The type of the target layer property</typeparam>
/// <param name="layerProperty">The target layer property</param>
/// <param name="condition">The condition to evaluate to determine whether to hide the current layer property</param>
public void IsHiddenWhen<TP>(TP layerProperty, Func<TP, bool> condition) where TP : ILayerProperty
{
IsHiddenWhen(layerProperty, condition, false);
}
/// <summary>
/// Set up a condition to show the provided layer property when the condition evaluates to <see langword="true" />
/// <para>Note: overrides previous calls to <c>IsHiddenWhen</c> and <c>IsVisibleWhen</c></para>
/// </summary>
/// <typeparam name="TP">The type of the target layer property</typeparam>
/// <param name="layerProperty">The target layer property</param>
/// <param name="condition">The condition to evaluate to determine whether to hide the current layer property</param>
public void IsVisibleWhen<TP>(TP layerProperty, Func<TP, bool> condition) where TP : ILayerProperty
{
IsHiddenWhen(layerProperty, condition, true);
}
private void IsHiddenWhen<TP>(TP layerProperty, Func<TP, bool> condition, bool inverse) where TP : ILayerProperty
{
layerProperty.VisibilityChanged += LayerPropertyChanged;
layerProperty.CurrentValueSet += LayerPropertyChanged;
layerProperty.Disposed += LayerPropertyOnDisposed;
void LayerPropertyChanged(object? sender, LayerPropertyEventArgs e)
{
if (inverse)
IsHidden = !condition(layerProperty);
else
IsHidden = condition(layerProperty);
}
void LayerPropertyOnDisposed(object? sender, EventArgs e)
{
layerProperty.VisibilityChanged -= LayerPropertyChanged;
layerProperty.CurrentValueSet -= LayerPropertyChanged;
layerProperty.Disposed -= LayerPropertyOnDisposed;
}
if (inverse)
IsHidden = !condition(layerProperty);
else
IsHidden = condition(layerProperty);
}
#endregion
#region Storage
private bool _isInitialized;
@ -502,6 +559,8 @@ namespace Artemis.Core
if (PropertyDescription.DisableKeyframes)
KeyframesSupported = false;
OnInitialize();
}
/// <inheritdoc />
@ -516,7 +575,6 @@ namespace Artemis.Core
if (!IsLoadedFromStorage)
ApplyDefaultValue(null);
else
{
try
{
if (Entity.Value != null)
@ -526,7 +584,6 @@ namespace Artemis.Core
{
// ignored for now
}
}
CurrentValue = BaseValue;
KeyframesEnabled = Entity.KeyframesEnabled;
@ -572,56 +629,50 @@ namespace Artemis.Core
dataBinding.Save();
}
/// <summary>
/// Called when the layer property has been initialized
/// </summary>
protected virtual void OnInitialize()
{
}
#endregion
#region Events
/// <summary>
/// Occurs once every frame when the layer property is updated
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? Updated;
/// <inheritdoc />
public event EventHandler? Disposed;
/// <summary>
/// Occurs when the current value of the layer property was updated by some form of input
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? CurrentValueSet;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? Updated;
/// <summary>
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? VisibilityChanged;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? CurrentValueSet;
/// <summary>
/// Occurs when keyframes are enabled/disabled
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? KeyframesToggled;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? VisibilityChanged;
/// <summary>
/// Occurs when a new keyframe was added to the layer property
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? KeyframeAdded;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? KeyframesToggled;
/// <summary>
/// Occurs when a keyframe was removed from the layer property
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? KeyframeRemoved;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? KeyframeAdded;
/// <summary>
/// Occurs when a data binding has been enabled
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? DataBindingEnabled;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
/// <summary>
/// Occurs when a data binding has been disabled
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? DataBindingDisabled;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? DataBindingEnabled;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? DataBindingDisabled;
/// <summary>
/// Invokes the <see cref="Updated" /> event
/// </summary>
protected virtual void OnUpdated()
{
Updated?.Invoke(this, new LayerPropertyEventArgs<T>(this));
Updated?.Invoke(this, new LayerPropertyEventArgs(this));
}
/// <summary>
@ -629,7 +680,7 @@ namespace Artemis.Core
/// </summary>
protected virtual void OnCurrentValueSet()
{
CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs<T>(this));
CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this));
LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this));
}
@ -638,7 +689,7 @@ namespace Artemis.Core
/// </summary>
protected virtual void OnVisibilityChanged()
{
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs<T>(this));
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
}
/// <summary>
@ -646,7 +697,7 @@ namespace Artemis.Core
/// </summary>
protected virtual void OnKeyframesToggled()
{
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs<T>(this));
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
}
/// <summary>
@ -654,7 +705,7 @@ namespace Artemis.Core
/// </summary>
protected virtual void OnKeyframeAdded()
{
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs<T>(this));
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
}
/// <summary>
@ -662,13 +713,13 @@ namespace Artemis.Core
/// </summary>
protected virtual void OnKeyframeRemoved()
{
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs<T>(this));
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
}
/// <summary>
/// Invokes the <see cref="DataBindingEnabled" /> event
/// </summary>
protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs<T> e)
protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e)
{
DataBindingEnabled?.Invoke(this, e);
}
@ -676,7 +727,7 @@ namespace Artemis.Core
/// <summary>
/// Invokes the <see cref="DataBindingDisabled" /> event
/// </summary>
protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs<T> e)
protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e)
{
DataBindingDisabled?.Invoke(this, e);
}

View File

@ -57,6 +57,7 @@ namespace Artemis.Core
/// <summary>
/// The parent group of this group
/// </summary>
[LayerPropertyIgnore]
public LayerPropertyGroup? Parent { get; internal set; }
/// <summary>
@ -127,7 +128,7 @@ namespace Artemis.Core
protected abstract void PopulateDefaults();
/// <summary>
/// Called when the property group is aactivated
/// Called when the property group is activated
/// </summary>
protected abstract void EnableProperties();
@ -156,19 +157,23 @@ namespace Artemis.Core
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
Path = path.TrimEnd('.');
// Get all properties with a PropertyDescriptionAttribute
// Get all properties implementing ILayerProperty or LayerPropertyGroup
foreach (PropertyInfo propertyInfo in GetType().GetProperties())
{
Attribute? propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
if (propertyDescription != null)
if (Attribute.IsDefined(propertyInfo, typeof(LayerPropertyIgnoreAttribute)))
continue;
if (typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
{
InitializeProperty(propertyInfo, (PropertyDescriptionAttribute) propertyDescription);
PropertyDescriptionAttribute? propertyDescription =
(PropertyDescriptionAttribute?) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
InitializeProperty(propertyInfo, propertyDescription ?? new PropertyDescriptionAttribute());
}
else
else if (typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
{
Attribute? propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
if (propertyGroupDescription != null)
InitializeChildGroup(propertyInfo, (PropertyGroupDescriptionAttribute) propertyGroupDescription);
PropertyGroupDescriptionAttribute? propertyGroupDescription =
(PropertyGroupDescriptionAttribute?) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
InitializeChildGroup(propertyInfo, propertyGroupDescription ?? new PropertyGroupDescriptionAttribute());
}
}

View File

@ -194,7 +194,7 @@ namespace Artemis.UI.Shared
UpdateInputValue();
}
private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs<T> e)
private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs e)
{
OnDataBindingsChanged();
}

View File

@ -152,20 +152,22 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
// The group has methods for getting this without reflection but then we lose the order of the properties as they are defined on the group
foreach (PropertyInfo propertyInfo in LayerPropertyGroup.GetType().GetProperties())
{
PropertyDescriptionAttribute propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
PropertyGroupDescriptionAttribute groupAttribute = (PropertyGroupDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
object value = propertyInfo.GetValue(LayerPropertyGroup);
if (Attribute.IsDefined(propertyInfo, typeof(LayerPropertyIgnoreAttribute)))
continue;
// Create VMs for properties on the group
if (propertyAttribute != null && value is ILayerProperty layerProperty)
if (typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
{
ILayerProperty value = (ILayerProperty) propertyInfo.GetValue(LayerPropertyGroup);
// Ensure a supported input VM was found, otherwise don't add it
if (_profileEditorService.CanCreatePropertyInputViewModel(layerProperty))
Items.Add(_layerPropertyVmFactory.LayerPropertyViewModel(layerProperty));
if (value != null && _profileEditorService.CanCreatePropertyInputViewModel(value))
Items.Add(_layerPropertyVmFactory.LayerPropertyViewModel(value));
}
else if (typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
{
LayerPropertyGroup value = (LayerPropertyGroup) propertyInfo.GetValue(LayerPropertyGroup);
if (value != null)
Items.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(value));
}
// Create VMs for child groups on this group, resulting in a nested structure
else if (groupAttribute != null && value is LayerPropertyGroup layerPropertyGroup)
Items.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerPropertyGroup));
}
}
}

View File

@ -77,17 +77,17 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
base.OnInitialActivate();
}
private void LayerPropertyOnKeyframesToggled(object sender, LayerPropertyEventArgs<T> e)
private void LayerPropertyOnKeyframesToggled(object sender, LayerPropertyEventArgs e)
{
UpdateKeyframes();
}
private void LayerPropertyOnKeyframeRemoved(object sender, LayerPropertyEventArgs<T> e)
private void LayerPropertyOnKeyframeRemoved(object sender, LayerPropertyEventArgs e)
{
UpdateKeyframes();
}
private void LayerPropertyOnKeyframeAdded(object sender, LayerPropertyEventArgs<T> e)
private void LayerPropertyOnKeyframeAdded(object sender, LayerPropertyEventArgs e)
{
UpdateKeyframes();
}

View File

@ -119,17 +119,17 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
LayerPropertyViewModel.IsHighlighted = _profileEditorService.SelectedDataBinding == LayerProperty;
}
private void LayerPropertyOnVisibilityChanged(object sender, LayerPropertyEventArgs<T> e)
private void LayerPropertyOnVisibilityChanged(object sender, LayerPropertyEventArgs e)
{
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
}
private void LayerPropertyOnDataBindingChange(object sender, LayerPropertyEventArgs<T> e)
private void LayerPropertyOnDataBindingChange(object sender, LayerPropertyEventArgs e)
{
NotifyOfPropertyChange(nameof(HasDataBinding));
}
private void LayerPropertyOnKeyframesToggled(object sender, LayerPropertyEventArgs<T> e)
private void LayerPropertyOnKeyframesToggled(object sender, LayerPropertyEventArgs e)
{
NotifyOfPropertyChange(nameof(KeyframesEnabled));
}