1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 01:42:02 +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; KeyframesSupported = false;
DataBindingsSupported = false; DataBindingsSupported = false;
DefaultValue = new ColorGradient();
CurrentValueSet += OnCurrentValueSet; CurrentValueSet += OnCurrentValueSet;
} }
@ -25,11 +26,25 @@
throw new ArtemisCoreException("Color Gradients do not support keyframes."); 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 // Don't allow color gradients to be null
if (BaseValue == null) if (BaseValue == null)
BaseValue = DefaultValue ?? new ColorGradient(); 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 // Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new FloatRange(0, 0); 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 // Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new IntRange(0, 0); BaseValue ??= DefaultValue ?? new IntRange(0, 0);

View File

@ -2,22 +2,6 @@
namespace Artemis.Core 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> /// <summary>
/// Provides data for layer property events. /// Provides data for layer property events.
/// </summary> /// </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; } string Path { get; }
/// <summary> /// <summary>
/// Gets the type of the property /// Gets the type of the property
/// </summary> /// </summary>
Type PropertyType { get; } Type PropertyType { get; }
/// <summary> /// <summary>
/// Initializes the layer property /// Initializes the layer property
@ -59,5 +59,54 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="timeline">The timeline to apply to the property</param> /// <param name="timeline">The timeline to apply to the property</param>
void Update(Timeline timeline); 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) foreach (IDataBinding dataBinding in _dataBindings)
dataBinding.Dispose(); dataBinding.Dispose();
Disposed?.Invoke(this, EventArgs.Empty);
} }
} }
@ -442,7 +444,7 @@ namespace Artemis.Core
DataBinding<T, TProperty> dataBinding = new(dataBindingRegistration); DataBinding<T, TProperty> dataBinding = new(dataBindingRegistration);
_dataBindings.Add(dataBinding); _dataBindings.Add(dataBinding);
OnDataBindingEnabled(new LayerPropertyEventArgs<T>(dataBinding.LayerProperty)); OnDataBindingEnabled(new LayerPropertyEventArgs(dataBinding.LayerProperty));
return dataBinding; return dataBinding;
} }
@ -460,7 +462,7 @@ namespace Artemis.Core
if (dataBinding.Registration != null) if (dataBinding.Registration != null)
dataBinding.Registration.DataBinding = null; dataBinding.Registration.DataBinding = null;
dataBinding.Dispose(); dataBinding.Dispose();
OnDataBindingDisabled(new LayerPropertyEventArgs<T>(dataBinding.LayerProperty)); OnDataBindingDisabled(new LayerPropertyEventArgs(dataBinding.LayerProperty));
} }
private void UpdateDataBindings(Timeline timeline) private void UpdateDataBindings(Timeline timeline)
@ -474,6 +476,61 @@ namespace Artemis.Core
#endregion #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 #region Storage
private bool _isInitialized; private bool _isInitialized;
@ -502,6 +559,8 @@ namespace Artemis.Core
if (PropertyDescription.DisableKeyframes) if (PropertyDescription.DisableKeyframes)
KeyframesSupported = false; KeyframesSupported = false;
OnInitialize();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -516,7 +575,6 @@ namespace Artemis.Core
if (!IsLoadedFromStorage) if (!IsLoadedFromStorage)
ApplyDefaultValue(null); ApplyDefaultValue(null);
else else
{
try try
{ {
if (Entity.Value != null) if (Entity.Value != null)
@ -526,7 +584,6 @@ namespace Artemis.Core
{ {
// ignored for now // ignored for now
} }
}
CurrentValue = BaseValue; CurrentValue = BaseValue;
KeyframesEnabled = Entity.KeyframesEnabled; KeyframesEnabled = Entity.KeyframesEnabled;
@ -572,56 +629,50 @@ namespace Artemis.Core
dataBinding.Save(); dataBinding.Save();
} }
/// <summary>
/// Called when the layer property has been initialized
/// </summary>
protected virtual void OnInitialize()
{
}
#endregion #endregion
#region Events #region Events
/// <summary> /// <inheritdoc />
/// Occurs once every frame when the layer property is updated public event EventHandler? Disposed;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? Updated;
/// <summary> /// <inheritdoc />
/// Occurs when the current value of the layer property was updated by some form of input public event EventHandler<LayerPropertyEventArgs>? Updated;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? CurrentValueSet;
/// <summary> /// <inheritdoc />
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated public event EventHandler<LayerPropertyEventArgs>? CurrentValueSet;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? VisibilityChanged;
/// <summary> /// <inheritdoc />
/// Occurs when keyframes are enabled/disabled public event EventHandler<LayerPropertyEventArgs>? VisibilityChanged;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? KeyframesToggled;
/// <summary> /// <inheritdoc />
/// Occurs when a new keyframe was added to the layer property public event EventHandler<LayerPropertyEventArgs>? KeyframesToggled;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? KeyframeAdded;
/// <summary> /// <inheritdoc />
/// Occurs when a keyframe was removed from the layer property public event EventHandler<LayerPropertyEventArgs>? KeyframeAdded;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? KeyframeRemoved;
/// <summary> /// <inheritdoc />
/// Occurs when a data binding has been enabled public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? DataBindingEnabled;
/// <summary> /// <inheritdoc />
/// Occurs when a data binding has been disabled public event EventHandler<LayerPropertyEventArgs>? DataBindingEnabled;
/// </summary>
public event EventHandler<LayerPropertyEventArgs<T>>? DataBindingDisabled; /// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? DataBindingDisabled;
/// <summary> /// <summary>
/// Invokes the <see cref="Updated" /> event /// Invokes the <see cref="Updated" /> event
/// </summary> /// </summary>
protected virtual void OnUpdated() protected virtual void OnUpdated()
{ {
Updated?.Invoke(this, new LayerPropertyEventArgs<T>(this)); Updated?.Invoke(this, new LayerPropertyEventArgs(this));
} }
/// <summary> /// <summary>
@ -629,7 +680,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
protected virtual void OnCurrentValueSet() protected virtual void OnCurrentValueSet()
{ {
CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs<T>(this)); CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this));
LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this)); LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this));
} }
@ -638,7 +689,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
protected virtual void OnVisibilityChanged() protected virtual void OnVisibilityChanged()
{ {
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs<T>(this)); VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
} }
/// <summary> /// <summary>
@ -646,7 +697,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
protected virtual void OnKeyframesToggled() protected virtual void OnKeyframesToggled()
{ {
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs<T>(this)); KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
} }
/// <summary> /// <summary>
@ -654,7 +705,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
protected virtual void OnKeyframeAdded() protected virtual void OnKeyframeAdded()
{ {
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs<T>(this)); KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
} }
/// <summary> /// <summary>
@ -662,13 +713,13 @@ namespace Artemis.Core
/// </summary> /// </summary>
protected virtual void OnKeyframeRemoved() protected virtual void OnKeyframeRemoved()
{ {
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs<T>(this)); KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
} }
/// <summary> /// <summary>
/// Invokes the <see cref="DataBindingEnabled" /> event /// Invokes the <see cref="DataBindingEnabled" /> event
/// </summary> /// </summary>
protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs<T> e) protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e)
{ {
DataBindingEnabled?.Invoke(this, e); DataBindingEnabled?.Invoke(this, e);
} }
@ -676,7 +727,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Invokes the <see cref="DataBindingDisabled" /> event /// Invokes the <see cref="DataBindingDisabled" /> event
/// </summary> /// </summary>
protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs<T> e) protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e)
{ {
DataBindingDisabled?.Invoke(this, e); DataBindingDisabled?.Invoke(this, e);
} }

View File

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

View File

@ -194,7 +194,7 @@ namespace Artemis.UI.Shared
UpdateInputValue(); UpdateInputValue();
} }
private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs<T> e) private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs e)
{ {
OnDataBindingsChanged(); 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 // 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()) foreach (PropertyInfo propertyInfo in LayerPropertyGroup.GetType().GetProperties())
{ {
PropertyDescriptionAttribute propertyAttribute = (PropertyDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute)); if (Attribute.IsDefined(propertyInfo, typeof(LayerPropertyIgnoreAttribute)))
PropertyGroupDescriptionAttribute groupAttribute = (PropertyGroupDescriptionAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute)); continue;
object value = propertyInfo.GetValue(LayerPropertyGroup);
// Create VMs for properties on the group if (typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
if (propertyAttribute != null && value is ILayerProperty layerProperty)
{ {
ILayerProperty value = (ILayerProperty) propertyInfo.GetValue(LayerPropertyGroup);
// Ensure a supported input VM was found, otherwise don't add it // Ensure a supported input VM was found, otherwise don't add it
if (_profileEditorService.CanCreatePropertyInputViewModel(layerProperty)) if (value != null && _profileEditorService.CanCreatePropertyInputViewModel(value))
Items.Add(_layerPropertyVmFactory.LayerPropertyViewModel(layerProperty)); 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(); base.OnInitialActivate();
} }
private void LayerPropertyOnKeyframesToggled(object sender, LayerPropertyEventArgs<T> e) private void LayerPropertyOnKeyframesToggled(object sender, LayerPropertyEventArgs e)
{ {
UpdateKeyframes(); UpdateKeyframes();
} }
private void LayerPropertyOnKeyframeRemoved(object sender, LayerPropertyEventArgs<T> e) private void LayerPropertyOnKeyframeRemoved(object sender, LayerPropertyEventArgs e)
{ {
UpdateKeyframes(); UpdateKeyframes();
} }
private void LayerPropertyOnKeyframeAdded(object sender, LayerPropertyEventArgs<T> e) private void LayerPropertyOnKeyframeAdded(object sender, LayerPropertyEventArgs e)
{ {
UpdateKeyframes(); UpdateKeyframes();
} }

View File

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