diff --git a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs
index 387c2d9ef..e811a4e0f 100644
--- a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs
+++ b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs
@@ -1,4 +1,5 @@
-using System.ComponentModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
using SkiaSharp;
namespace Artemis.Core
@@ -23,17 +24,16 @@ namespace Artemis.Core
if (CurrentValue == null)
return;
- for (int index = 0; index < CurrentValue.Stops.Count; index++)
+ for (int index = 0; index < CurrentValue.Count; index++)
{
int stopIndex = index;
void Setter(SKColor value)
{
- CurrentValue.Stops[stopIndex].Color = value;
- CurrentValue.OnColorValuesUpdated();
+ CurrentValue[stopIndex].Color = value;
}
- RegisterDataBindingProperty(() => CurrentValue.Stops[stopIndex].Color, Setter, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}");
+ RegisterDataBindingProperty(() => CurrentValue[stopIndex].Color, Setter, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}");
}
}
@@ -61,17 +61,17 @@ namespace Artemis.Core
if (_subscribedGradient != BaseValue)
{
if (_subscribedGradient != null)
- _subscribedGradient.PropertyChanged -= SubscribedGradientOnPropertyChanged;
+ _subscribedGradient.CollectionChanged -= SubscribedGradientOnPropertyChanged;
_subscribedGradient = BaseValue;
- _subscribedGradient.PropertyChanged += SubscribedGradientOnPropertyChanged;
+ _subscribedGradient.CollectionChanged += SubscribedGradientOnPropertyChanged;
}
CreateDataBindingRegistrations();
}
- private void SubscribedGradientOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ private void SubscribedGradientOnPropertyChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
- if (CurrentValue.Stops.Count != GetAllDataBindingRegistrations().Count)
+ if (CurrentValue.Count != GetAllDataBindingRegistrations().Count)
CreateDataBindingRegistrations();
}
diff --git a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs
index 4f630fcda..c621eb5c7 100644
--- a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs
+++ b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs
@@ -1,5 +1,8 @@
using System;
+using System.Collections;
using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
using System.Linq;
using SkiaSharp;
@@ -8,7 +11,7 @@ namespace Artemis.Core
///
/// A gradient containing a list of s
///
- public class ColorGradient : CorePropertyChanged
+ public class ColorGradient : IList, INotifyCollectionChanged
{
private static readonly SKColor[] FastLedRainbow =
{
@@ -23,19 +26,16 @@ namespace Artemis.Core
new(0xFFFF0000) // and back to Red
};
+ private readonly List _stops;
+
///
/// Creates a new instance of the class
///
public ColorGradient()
{
- Stops = new List();
+ _stops = new List();
}
- ///
- /// Gets a list of all the s in the gradient
- ///
- public List Stops { get; }
-
///
/// Gets all the colors in the color gradient
///
@@ -49,13 +49,16 @@ namespace Artemis.Core
{
List result = new();
if (timesToRepeat == 0)
- result = Stops.Select(c => c.Color).ToList();
+ {
+ result = this.Select(c => c.Color).ToList();
+ }
else
{
- List colors = Stops.Select(c => c.Color).ToList();
+ List colors = this.Select(c => c.Color).ToList();
for (int i = 0; i <= timesToRepeat; i++)
result.AddRange(colors);
}
+
if (seamless && !IsSeamless())
result.Add(result[0]);
@@ -77,11 +80,13 @@ namespace Artemis.Core
{
List result = new();
if (timesToRepeat == 0)
- result = Stops.Select(c => c.Position).ToList();
+ {
+ result = this.Select(c => c.Position).ToList();
+ }
else
{
// Create stops and a list of divided stops
- List stops = Stops.Select(c => c.Position / (timesToRepeat + 1)).ToList();
+ List stops = this.Select(c => c.Position / (timesToRepeat + 1)).ToList();
// For each repeat cycle, add the base stops to the end result
for (int i = 0; i <= timesToRepeat; i++)
@@ -104,25 +109,16 @@ namespace Artemis.Core
return result.ToArray();
}
- ///
- /// Triggers a property changed event of the collection
- ///
- public void OnColorValuesUpdated()
- {
- Stops.Sort((a, b) => a.Position.CompareTo(b.Position));
- OnPropertyChanged(nameof(Stops));
- }
-
///
/// Gets a color at any position between 0.0 and 1.0 using interpolation
///
/// A position between 0.0 and 1.0
public SKColor GetColor(float position)
{
- if (!Stops.Any())
+ if (!this.Any())
return SKColor.Empty;
- ColorGradientStop[] stops = Stops.ToArray();
+ ColorGradientStop[] stops = this.ToArray();
if (position <= 0) return stops[0].Color;
if (position >= 1) return stops[^1].Color;
ColorGradientStop left = stops[0];
@@ -160,7 +156,7 @@ namespace Artemis.Core
{
SKColor skColor = FastLedRainbow[index];
float position = 1f / (FastLedRainbow.Length - 1f) * index;
- gradient.Stops.Add(new ColorGradientStop(skColor, position));
+ gradient.Add(new ColorGradientStop(skColor, position));
}
return gradient;
@@ -172,7 +168,141 @@ namespace Artemis.Core
/// if the gradient is seamless; otherwise
public bool IsSeamless()
{
- return Stops.Count == 0 || Stops.First().Color.Equals(Stops.Last().Color);
+ return Count == 0 || this.First().Color.Equals(this.Last().Color);
}
+
+ internal void Sort()
+ {
+ _stops.Sort((a, b) => a.Position.CompareTo(b.Position));
+ }
+
+ private void ItemOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ Sort();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ #region Implementation of IEnumerable
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ return _stops.GetEnumerator();
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ #region Implementation of ICollection
+
+ ///
+ public void Add(ColorGradientStop item)
+ {
+ _stops.Add(item);
+ item.PropertyChanged += ItemOnPropertyChanged;
+ Sort();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _stops.IndexOf(item)));
+ }
+
+
+ ///
+ public void Clear()
+ {
+ foreach (ColorGradientStop item in _stops)
+ item.PropertyChanged -= ItemOnPropertyChanged;
+ _stops.Clear();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ ///
+ public bool Contains(ColorGradientStop item)
+ {
+ return _stops.Contains(item);
+ }
+
+ ///
+ public void CopyTo(ColorGradientStop[] array, int arrayIndex)
+ {
+ _stops.CopyTo(array, arrayIndex);
+ }
+
+ ///
+ public bool Remove(ColorGradientStop item)
+ {
+ item.PropertyChanged -= ItemOnPropertyChanged;
+ int index = _stops.IndexOf(item);
+ bool removed = _stops.Remove(item);
+ if (removed)
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
+
+ return removed;
+ }
+
+ ///
+ public int Count => _stops.Count;
+
+ ///
+ public bool IsReadOnly => false;
+
+ #endregion
+
+ #region Implementation of IList
+
+ ///
+ public int IndexOf(ColorGradientStop item)
+ {
+ return _stops.IndexOf(item);
+ }
+
+ ///
+ public void Insert(int index, ColorGradientStop item)
+ {
+ _stops.Insert(index, item);
+ item.PropertyChanged += ItemOnPropertyChanged;
+ Sort();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item)));
+ }
+
+ ///
+ public void RemoveAt(int index)
+ {
+ _stops[index].PropertyChanged -= ItemOnPropertyChanged;
+ _stops.RemoveAt(index);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, index));
+ }
+
+ ///
+ public ColorGradientStop this[int index]
+ {
+ get => _stops[index];
+ set
+ {
+ ColorGradientStop? oldValue = _stops[index];
+ oldValue.PropertyChanged -= ItemOnPropertyChanged;
+ _stops[index] = value;
+ _stops[index].PropertyChanged += ItemOnPropertyChanged;
+ Sort();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue));
+ }
+ }
+
+ #endregion
+
+ #region Implementation of INotifyCollectionChanged
+
+ ///
+ public event NotifyCollectionChangedEventHandler? CollectionChanged;
+
+ private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ CollectionChanged?.Invoke(this, e);
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 47a08ab8a..5e544e187 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -297,6 +297,9 @@ namespace Artemis.Core
private void ApplyTimeline(Timeline timeline)
{
+ if (timeline.Delta == TimeSpan.Zero)
+ return;
+
General.Update(timeline);
Transform.Update(timeline);
if (LayerBrush != null)
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs
index c42b1f74c..c9bdf5872 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs
@@ -33,6 +33,11 @@ namespace Artemis.Core
///
Type PropertyType { get; }
+ ///
+ /// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
+ ///
+ bool IsLoadedFromStorage { get; }
+
///
/// Initializes the layer property
///
@@ -54,6 +59,11 @@ namespace Artemis.Core
/// If succeeded the resulting keyframe, otherwise
ILayerPropertyKeyframe? AddKeyframeEntity(KeyframeEntity keyframeEntity);
+ ///
+ /// Overrides the property value with the default value
+ ///
+ void ApplyDefaultValue();
+
///
/// Updates the layer properties internal state
///
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
index c35bc401d..afe35710d 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
-using System.Linq.Expressions;
using Artemis.Storage.Entities.Profile;
using Newtonsoft.Json;
@@ -39,6 +38,107 @@ namespace Artemis.Core
_keyframes = new List>();
}
+ ///
+ /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
+ ///
+ ///
+ /// to release both managed and unmanaged resources;
+ /// to release only unmanaged resources.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _disposed = true;
+
+ foreach (IDataBinding dataBinding in _dataBindings)
+ dataBinding.Dispose();
+
+ Disposed?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnUpdated()
+ {
+ Updated?.Invoke(this, new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnCurrentValueSet()
+ {
+ CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this));
+ LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnVisibilityChanged()
+ {
+ VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnKeyframesToggled()
+ {
+ KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnKeyframeAdded()
+ {
+ KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnKeyframeRemoved()
+ {
+ KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnDataBindingPropertyRegistered()
+ {
+ DataBindingPropertyRegistered?.Invoke(this, new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnDataBindingPropertiesCleared()
+ {
+ DataBindingPropertiesCleared?.Invoke(this, new LayerPropertyEventArgs(this));
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e)
+ {
+ DataBindingEnabled?.Invoke(this, e);
+ }
+
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e)
+ {
+ DataBindingDisabled?.Invoke(this, e);
+ }
+
///
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
@@ -62,28 +162,6 @@ namespace Artemis.Core
OnUpdated();
}
- #region IDisposable
-
- ///
- /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
- ///
- ///
- /// to release both managed and unmanaged resources;
- /// to release only unmanaged resources.
- ///
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _disposed = true;
-
- foreach (IDataBinding dataBinding in _dataBindings)
- dataBinding.Dispose();
-
- Disposed?.Invoke(this, EventArgs.Empty);
- }
- }
-
///
public void Dispose()
{
@@ -91,7 +169,38 @@ namespace Artemis.Core
GC.SuppressFinalize(this);
}
- #endregion
+ ///
+ public event EventHandler? Disposed;
+
+ ///
+ public event EventHandler? Updated;
+
+ ///
+ public event EventHandler? CurrentValueSet;
+
+ ///
+ public event EventHandler? VisibilityChanged;
+
+ ///
+ public event EventHandler? KeyframesToggled;
+
+ ///
+ public event EventHandler? KeyframeAdded;
+
+ ///
+ public event EventHandler? KeyframeRemoved;
+
+ ///
+ public event EventHandler? DataBindingPropertyRegistered;
+
+ ///
+ public event EventHandler? DataBindingPropertiesCleared;
+
+ ///
+ public event EventHandler? DataBindingEnabled;
+
+ ///
+ public event EventHandler? DataBindingDisabled;
#region Hierarchy
@@ -195,22 +304,25 @@ namespace Artemis.Core
ReapplyUpdate();
}
- ///
- /// Overrides the property value with the default value
- ///
- public void ApplyDefaultValue(TimeSpan? time)
+ ///
+ public void ApplyDefaultValue()
{
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
string json = CoreJson.SerializeObject(DefaultValue, true);
- SetCurrentValue(CoreJson.DeserializeObject(json)!, time);
+ KeyframesEnabled = false;
+ SetCurrentValue(CoreJson.DeserializeObject(json)!, null);
}
private void ReapplyUpdate()
{
- ProfileElement.Timeline.ClearDelta();
- Update(ProfileElement.Timeline);
+ // Create a timeline with the same position but a delta of zero
+ Timeline temporaryTimeline = new();
+ temporaryTimeline.Override(ProfileElement.Timeline.Position, false);
+ temporaryTimeline.ClearDelta();
+
+ Update(temporaryTimeline);
OnCurrentValueSet();
}
@@ -440,10 +552,10 @@ namespace Artemis.Core
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
- foreach (IDataBindingRegistration dataBindingRegistration in _dataBindingRegistrations)
+ foreach (IDataBindingRegistration dataBindingRegistration in _dataBindingRegistrations)
dataBindingRegistration.ClearDataBinding();
_dataBindingRegistrations.Clear();
-
+
OnDataBindingPropertiesCleared();
}
@@ -593,7 +705,7 @@ namespace Artemis.Core
throw new ArtemisCoreException("Layer property is not yet initialized");
if (!IsLoadedFromStorage)
- ApplyDefaultValue(null);
+ ApplyDefaultValue();
else
try
{
@@ -657,123 +769,5 @@ namespace Artemis.Core
}
#endregion
-
- #region Events
-
- ///
- public event EventHandler? Disposed;
-
- ///
- public event EventHandler? Updated;
-
- ///
- public event EventHandler? CurrentValueSet;
-
- ///
- public event EventHandler? VisibilityChanged;
-
- ///
- public event EventHandler? KeyframesToggled;
-
- ///
- public event EventHandler? KeyframeAdded;
-
- ///
- public event EventHandler? KeyframeRemoved;
-
- ///
- public event EventHandler? DataBindingPropertyRegistered;
-
- ///
- public event EventHandler? DataBindingPropertiesCleared;
-
- ///
- public event EventHandler? DataBindingEnabled;
-
- ///
- public event EventHandler? DataBindingDisabled;
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnUpdated()
- {
- Updated?.Invoke(this, new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnCurrentValueSet()
- {
- CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this));
- LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnVisibilityChanged()
- {
- VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnKeyframesToggled()
- {
- KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnKeyframeAdded()
- {
- KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnKeyframeRemoved()
- {
- KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnDataBindingPropertyRegistered()
- {
- DataBindingPropertyRegistered?.Invoke(this, new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnDataBindingPropertiesCleared()
- {
- DataBindingPropertiesCleared?.Invoke(this, new LayerPropertyEventArgs(this));
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e)
- {
- DataBindingEnabled?.Invoke(this, e);
- }
-
- ///
- /// Invokes the event
- ///
- protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e)
- {
- DataBindingDisabled?.Invoke(this, e);
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
index dda5d03cb..7483733a9 100644
--- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
+++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
@@ -121,6 +121,31 @@ namespace Artemis.Core
return result.AsReadOnly();
}
+ ///
+ /// Applies the default value to all layer properties
+ ///
+ public void ResetAllLayerProperties()
+ {
+ foreach (ILayerProperty layerProperty in GetAllLayerProperties())
+ layerProperty.ApplyDefaultValue();
+ }
+
+ ///
+ /// Occurs when the property group has initialized all its children
+ ///
+ public event EventHandler? PropertyGroupInitialized;
+
+ ///
+ /// Occurs when one of the current value of one of the layer properties in this group changes by some form of input
+ /// Note: Will not trigger on properties in child groups
+ ///
+ public event EventHandler? LayerPropertyOnCurrentValueSet;
+
+ ///
+ /// Occurs when the value of the layer property was updated
+ ///
+ public event EventHandler? VisibilityChanged;
+
///
/// Called before property group is activated to allow you to populate on
/// the properties you want
@@ -145,6 +170,27 @@ namespace Artemis.Core
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
}
+ ///
+ /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
+ ///
+ ///
+ /// to release both managed and unmanaged resources;
+ /// to release only unmanaged resources.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _disposed = true;
+ DisableProperties();
+
+ foreach (ILayerProperty layerProperty in _layerProperties)
+ layerProperty.Dispose();
+ foreach (LayerPropertyGroup layerPropertyGroup in _layerPropertyGroups)
+ layerPropertyGroup.Dispose();
+ }
+ }
+
internal void Initialize(RenderProfileElement profileElement, string path, PluginFeature feature)
{
if (path == null) throw new ArgumentNullException(nameof(path));
@@ -209,6 +255,17 @@ namespace Artemis.Core
layerPropertyGroup.Update(timeline);
}
+ internal virtual void OnVisibilityChanged()
+ {
+ VisibilityChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal virtual void OnLayerPropertyOnCurrentValueSet(LayerPropertyEventArgs e)
+ {
+ Parent?.OnLayerPropertyOnCurrentValueSet(e);
+ LayerPropertyOnCurrentValueSet?.Invoke(this, e);
+ }
+
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
{
string path = $"{Path}.{propertyInfo.Name}";
@@ -266,67 +323,11 @@ namespace Artemis.Core
return entity;
}
- #region IDisposable
-
- ///
- /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
- ///
- ///
- /// to release both managed and unmanaged resources;
- /// to release only unmanaged resources.
- ///
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _disposed = true;
- DisableProperties();
-
- foreach (ILayerProperty layerProperty in _layerProperties)
- layerProperty.Dispose();
- foreach (LayerPropertyGroup layerPropertyGroup in _layerPropertyGroups)
- layerPropertyGroup.Dispose();
- }
- }
-
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
-
- #endregion
-
- #region Events
-
- ///
- /// Occurs when the property group has initialized all its children
- ///
- public event EventHandler? PropertyGroupInitialized;
-
- ///
- /// Occurs when one of the current value of one of the layer properties in this group changes by some form of input
- /// Note: Will not trigger on properties in child groups
- ///
- public event EventHandler? LayerPropertyOnCurrentValueSet;
-
- ///
- /// Occurs when the value of the layer property was updated
- ///
- public event EventHandler? VisibilityChanged;
-
- internal virtual void OnVisibilityChanged()
- {
- VisibilityChanged?.Invoke(this, EventArgs.Empty);
- }
-
- internal virtual void OnLayerPropertyOnCurrentValueSet(LayerPropertyEventArgs e)
- {
- Parent?.OnLayerPropertyOnCurrentValueSet(e);
- LayerPropertyOnCurrentValueSet?.Invoke(this, e);
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
index d28885e65..0baf7d834 100644
--- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
+++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
@@ -329,6 +329,13 @@ namespace Artemis.Core
/// A boolean indicating whether to remove excess LEDs present in the device but missing in the layout
internal void ApplyLayout(ArtemisLayout layout, bool createMissingLeds, bool removeExcessiveLeds)
{
+ if (createMissingLeds && !DeviceProvider.CreateMissingLedsSupported)
+ throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} " +
+ "set to true because the device provider does not support it");
+ if (removeExcessiveLeds && !DeviceProvider.RemoveExcessiveLedsSupported)
+ throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} " +
+ "set to true because the device provider does not support it");
+
if (layout.IsValid)
layout.RgbLayout!.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds);
diff --git a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
index 949065503..f41e9c145 100644
--- a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
+++ b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs
@@ -51,6 +51,18 @@ namespace Artemis.Core.DeviceProviders
///
public bool CanDetectLogicalLayout { get; protected set; }
+ ///
+ /// Gets or sets a boolean indicating whether adding missing LEDs defined in a layout but missing on the device is supported
+ /// Note: Defaults to .
+ ///
+ public bool CreateMissingLedsSupported { get; protected set; } = true;
+
+ ///
+ /// Gets or sets a boolean indicating whether removing excess LEDs present in the device but missing in the layout is supported
+ /// Note: Defaults to .
+ ///
+ public bool RemoveExcessiveLedsSupported { get; protected set; } = true;
+
///
/// Loads a layout for the specified device and wraps it in an
///
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/ILayerBrushPreset.cs b/src/Artemis.Core/Plugins/LayerBrushes/ILayerBrushPreset.cs
new file mode 100644
index 000000000..72fa2aa40
--- /dev/null
+++ b/src/Artemis.Core/Plugins/LayerBrushes/ILayerBrushPreset.cs
@@ -0,0 +1,28 @@
+namespace Artemis.Core.LayerBrushes
+{
+ ///
+ /// Represents a brush preset for a brush.
+ ///
+ public interface ILayerBrushPreset
+ {
+ ///
+ /// Gets the name of the preset
+ ///
+ string Name { get; }
+
+ ///
+ /// Gets the description of the preset
+ ///
+ string Description { get; }
+
+ ///
+ /// Gets the icon of the preset
+ ///
+ string Icon { get; }
+
+ ///
+ /// Applies the preset to the layer brush
+ ///
+ void Apply();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs
index c452384e3..2e1712250 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using SkiaSharp;
namespace Artemis.Core.LayerBrushes
@@ -70,6 +72,16 @@ namespace Artemis.Core.LayerBrushes
///
public virtual LayerPropertyGroup? BaseProperties => null;
+ ///
+ /// Gets a list of presets available to this layer brush
+ ///
+ public virtual List? Presets => null;
+
+ ///
+ /// Gets the default preset used for new instances of this layer brush
+ ///
+ public virtual ILayerBrushPreset? DefaultPreset => Presets?.FirstOrDefault();
+
///
/// Gets or sets whether the brush supports transformations
/// Note: RGB.NET brushes can never be transformed and setting this to true will throw an exception
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs
index 5b12849b8..9184e1c6b 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
namespace Artemis.Core.LayerBrushes
{
diff --git a/src/Artemis.Core/Resources/intro-profile.json b/src/Artemis.Core/Resources/intro-profile.json
index e95c17153..dad8392dc 100644
--- a/src/Artemis.Core/Resources/intro-profile.json
+++ b/src/Artemis.Core/Resources/intro-profile.json
@@ -292,7 +292,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"FeatureId": null,
"Path": "LayerBrush.GradientColor",
- "Value": "{\"Stops\":[{\"Color\":\"#ff0b4a40\",\"Position\":0.0},{\"Color\":\"#ff00897c\",\"Position\":0.242},{\"Color\":\"#ffffffff\",\"Position\":1.0},{\"Color\":\"#ff00ffe6\",\"Position\":0.67391306}]}",
+ "Value": "[{\"Color\":\"#ff0b4a40\",\"Position\":0.0},{\"Color\":\"#ff00897c\",\"Position\":0.242},{\"Color\":\"#ffffffff\",\"Position\":1.0},{\"Color\":\"#ff00ffe6\",\"Position\":0.67391306}]",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -592,7 +592,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"FeatureId": "Artemis.Plugins.LayerBrushes.Noise.NoiseBrushProvider-61cbbf01",
"Path": "LayerBrush.GradientColor",
- "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]}",
+ "Value": "[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -909,7 +909,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"FeatureId": null,
"Path": "LayerBrush.Colors",
- "Value": "{\"Stops\":[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffffdf00\",\"Position\":0.916},{\"Color\":\"#00ffcd00\",\"Position\":1.0},{\"Color\":\"#fff31900\",\"Position\":0.636},{\"Color\":\"#dbf41500\",\"Position\":0.5461956},{\"Color\":\"#00f60f00\",\"Position\":0.462},{\"Color\":\"#93f60d00\",\"Position\":0.3668478}]}",
+ "Value": "[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffffdf00\",\"Position\":0.916},{\"Color\":\"#00ffcd00\",\"Position\":1.0},{\"Color\":\"#fff31900\",\"Position\":0.636},{\"Color\":\"#dbf41500\",\"Position\":0.5461956},{\"Color\":\"#00f60f00\",\"Position\":0.462},{\"Color\":\"#93f60d00\",\"Position\":0.3668478}]",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -1164,7 +1164,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"FeatureId": "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
"Path": "LayerBrush.Colors",
- "Value": "{\"Stops\":[{\"Color\":\"#ffcaff00\",\"Position\":0.709},{\"Color\":\"#ffff0000\",\"Position\":0.902},{\"Color\":\"#ffffffff\",\"Position\":0.5842391},{\"Color\":\"#ffff4100\",\"Position\":0.95108694},{\"Color\":\"#00ff4100\",\"Position\":0.98641306}]}",
+ "Value": "[{\"Color\":\"#ffcaff00\",\"Position\":0.709},{\"Color\":\"#ffff0000\",\"Position\":0.902},{\"Color\":\"#ffffffff\",\"Position\":0.5842391},{\"Color\":\"#ffff4100\",\"Position\":0.95108694},{\"Color\":\"#00ff4100\",\"Position\":0.98641306}]",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -1496,7 +1496,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"FeatureId": null,
"Path": "LayerBrush.Colors",
- "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.125},{\"Color\":\"#ffedff00\",\"Position\":0.25},{\"Color\":\"#ff65ff00\",\"Position\":0.375},{\"Color\":\"#ff00ff22\",\"Position\":0.5},{\"Color\":\"#ff00ffaa\",\"Position\":0.625},{\"Color\":\"#ff00cbff\",\"Position\":0.75},{\"Color\":\"#ff0043ff\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]}",
+ "Value": "[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.125},{\"Color\":\"#ffedff00\",\"Position\":0.25},{\"Color\":\"#ff65ff00\",\"Position\":0.375},{\"Color\":\"#ff00ff22\",\"Position\":0.5},{\"Color\":\"#ff00ffaa\",\"Position\":0.625},{\"Color\":\"#ff00cbff\",\"Position\":0.75},{\"Color\":\"#ff0043ff\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -1751,7 +1751,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"FeatureId": "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
"Path": "LayerBrush.Colors",
- "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]}",
+ "Value": "[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index 079bb18e2..28f590376 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -251,20 +251,16 @@ namespace Artemis.Core.Services
// Draw a white overlay over the device
void DrawOverlay(object? sender, FrameRenderingEventArgs args)
{
+ if (intro.AnimationProfile.GetAllLayers().All(l => l.Timeline.IsFinished))
+ {
+ FrameRendering -= DrawOverlay;
+ intro.AnimationProfile.Dispose();
+ }
+
intro.Render(args.DeltaTime, args.Canvas);
}
FrameRendering += DrawOverlay;
-
- // Stop rendering after the profile finishes (take 1 second extra in case of slow updates)
- TimeSpan introLength = intro.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length)!;
- Task.Run(async () =>
- {
- await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1)));
- FrameRendering -= DrawOverlay;
-
- intro.AnimationProfile.Dispose();
- });
}
public event EventHandler? Initialized;
diff --git a/src/Artemis.Core/Services/Interfaces/IRgbService.cs b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
index 00c5f85d0..cfcb5ffb0 100644
--- a/src/Artemis.Core/Services/Interfaces/IRgbService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
@@ -90,9 +90,7 @@ namespace Artemis.Core.Services
///
///
///
- ///
- ///
- void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds);
+ void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout);
///
/// Attempts to retrieve the that corresponds the provided RGB.NET
diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs
index 4f812c886..5e4a5066a 100644
--- a/src/Artemis.Core/Services/RgbService.cs
+++ b/src/Artemis.Core/Services/RgbService.cs
@@ -312,7 +312,7 @@ namespace Artemis.Core.Services
layout = new ArtemisLayout(device.CustomLayoutPath, LayoutSource.Configured);
if (layout.IsValid)
{
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
}
@@ -321,7 +321,7 @@ namespace Artemis.Core.Services
layout = device.DeviceProvider.LoadUserLayout(device);
if (layout.IsValid)
{
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
@@ -329,13 +329,13 @@ namespace Artemis.Core.Services
layout = device.DeviceProvider.LoadLayout(device);
if (layout.IsValid)
{
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
// Finally fall back to a default layout
layout = LoadDefaultLayout(device);
- ApplyDeviceLayout(device, layout, true, true);
+ ApplyDeviceLayout(device, layout);
return layout;
}
@@ -344,9 +344,9 @@ namespace Artemis.Core.Services
return new("NYI", LayoutSource.Default);
}
- public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout, bool createMissingLeds, bool removeExessiveLeds)
+ public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout layout)
{
- device.ApplyLayout(layout, createMissingLeds, removeExessiveLeds);
+ device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
UpdateLedGroup();
}
diff --git a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
index d88e3da3b..8a20910db 100644
--- a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
+++ b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs
@@ -55,7 +55,8 @@ namespace Artemis.Core.Services
DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(Module module, string endPointName) where T : DataModel;
///
- /// Adds a new endpoint that directly maps received JSON to the data model of the provided .
+ /// Adds a new endpoint that directly maps received JSON to the data model of the provided
+ /// .
///
/// The data model type of the module
/// The module whose datamodel to apply the received JSON to
@@ -64,7 +65,8 @@ namespace Artemis.Core.Services
DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(ProfileModule profileModule, string endPointName) where T : DataModel;
///
- /// Adds a new endpoint that directly maps received JSON to the data model of the provided .
+ /// Adds a new endpoint that directly maps received JSON to the data model of the provided
+ /// .
///
/// The data model type of the module
/// The data model expansion whose datamodel to apply the received JSON to
@@ -114,7 +116,7 @@ namespace Artemis.Core.Services
/// Adds a new Web API controller and restarts the web server
///
/// The type of Web API controller to remove
- void AddController() where T : WebApiController;
+ void AddController(PluginFeature feature) where T : WebApiController;
///
/// Removes an existing Web API controller and restarts the web server
@@ -122,6 +124,18 @@ namespace Artemis.Core.Services
/// The type of Web API controller to remove
void RemoveController() where T : WebApiController;
+ ///
+ /// Adds a new EmbedIO module and restarts the web server
+ ///
+ /// The type of module to add
+ void AddModule(PluginFeature feature) where T : IWebModule;
+
+ ///
+ /// Removes a EmbedIO module and restarts the web server
+ ///
+ /// The type of module to remove
+ void RemoveModule() where T : IWebModule;
+
///
/// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules.
///
diff --git a/src/Artemis.Core/Services/WebServer/PluginsModule.cs b/src/Artemis.Core/Services/WebServer/PluginsModule.cs
index d7f9d49de..4ad697b66 100644
--- a/src/Artemis.Core/Services/WebServer/PluginsModule.cs
+++ b/src/Artemis.Core/Services/WebServer/PluginsModule.cs
@@ -48,13 +48,13 @@ namespace Artemis.Core.Services
protected override async Task OnRequestAsync(IHttpContext context)
{
if (context.Route.SubPath == null)
- throw HttpException.NotFound();
+ return;
// Split the sub path
string[] pathParts = context.Route.SubPath.Substring(1).Split('/');
// Expect a plugin ID and an endpoint
- if (pathParts == null || pathParts.Length != 2)
- throw HttpException.BadRequest("Path must contain a plugin ID and endpoint and nothing else.");
+ if (pathParts.Length != 2)
+ return;
// Find a matching plugin
if (!_pluginEndPoints.TryGetValue(pathParts[0], out Dictionary? endPoints))
@@ -78,7 +78,7 @@ namespace Artemis.Core.Services
}
///
- public override bool IsFinalHandler => true;
+ public override bool IsFinalHandler => false;
internal string? ServerUrl { get; set; }
diff --git a/src/Artemis.Core/Services/WebServer/WebApiControllerRegistration.cs b/src/Artemis.Core/Services/WebServer/WebApiControllerRegistration.cs
index ad5fb30dc..f12455c5c 100644
--- a/src/Artemis.Core/Services/WebServer/WebApiControllerRegistration.cs
+++ b/src/Artemis.Core/Services/WebServer/WebApiControllerRegistration.cs
@@ -6,9 +6,9 @@ namespace Artemis.Core.Services
{
internal class WebApiControllerRegistration : WebApiControllerRegistration where T : WebApiController
{
- public WebApiControllerRegistration(IKernel kernel) : base(typeof(T))
+ public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T))
{
- Factory = () => kernel.Get();
+ Factory = () => feature.Plugin.Kernel!.Get();
}
public Func Factory { get; set; }
@@ -17,12 +17,14 @@ namespace Artemis.Core.Services
internal abstract class WebApiControllerRegistration
{
- protected WebApiControllerRegistration(Type controllerType)
+ protected WebApiControllerRegistration(PluginFeature feature, Type controllerType)
{
+ Feature = feature;
ControllerType = controllerType;
}
public abstract object UntypedFactory { get; }
public Type ControllerType { get; set; }
+ public PluginFeature Feature { get; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/WebServer/WebModuleRegistration.cs b/src/Artemis.Core/Services/WebServer/WebModuleRegistration.cs
new file mode 100644
index 000000000..fb7f03d13
--- /dev/null
+++ b/src/Artemis.Core/Services/WebServer/WebModuleRegistration.cs
@@ -0,0 +1,20 @@
+using System;
+using EmbedIO;
+using Ninject;
+
+namespace Artemis.Core.Services
+{
+ internal class WebModuleRegistration
+ {
+ public PluginFeature Feature { get; }
+ public Type WebModuleType { get; }
+
+ public WebModuleRegistration(PluginFeature feature, Type webModuleType)
+ {
+ Feature = feature;
+ WebModuleType = webModuleType;
+ }
+
+ public IWebModule CreateInstance() => (IWebModule) Feature.Plugin.Kernel!.Get(WebModuleType);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/WebServer/WebServerService.cs b/src/Artemis.Core/Services/WebServer/WebServerService.cs
index 589004e29..a310fca63 100644
--- a/src/Artemis.Core/Services/WebServer/WebServerService.cs
+++ b/src/Artemis.Core/Services/WebServer/WebServerService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions;
@@ -8,7 +9,6 @@ using Artemis.Core.Modules;
using EmbedIO;
using EmbedIO.WebApi;
using Newtonsoft.Json;
-using Ninject;
using Serilog;
namespace Artemis.Core.Services
@@ -16,18 +16,19 @@ namespace Artemis.Core.Services
internal class WebServerService : IWebServerService, IDisposable
{
private readonly List _controllers;
- private readonly IKernel _kernel;
+ private readonly List _modules;
private readonly ILogger _logger;
private readonly PluginSetting _webServerPortSetting;
- public WebServerService(IKernel kernel, ILogger logger, ISettingsService settingsService)
+ public WebServerService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService)
{
- _kernel = kernel;
_logger = logger;
_controllers = new List();
+ _modules = new List();
_webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696);
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
+ pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
PluginsModule = new PluginsModule("/plugins");
StartWebServer();
@@ -43,12 +44,18 @@ namespace Artemis.Core.Services
Server?.Dispose();
Server = null;
- WebApiModule apiModule = new("/api/", JsonNetSerializer);
+ WebApiModule apiModule = new("/", JsonNetSerializer);
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
WebServer server = new WebServer(o => o.WithUrlPrefix($"http://*:{_webServerPortSetting.Value}/").WithMode(HttpListenerMode.EmbedIO))
.WithLocalSessionManager()
+ .WithModule(PluginsModule);
+
+ // Add registered modules
+ foreach (var webModule in _modules)
+ server = server.WithModule(webModule.CreateInstance());
+
+ server = server
.WithModule(apiModule)
- .WithModule(PluginsModule)
.HandleHttpException((context, exception) => HandleHttpExceptionJson(context, exception))
.HandleUnhandledException(JsonExceptionHandlerCallback);
@@ -166,9 +173,9 @@ namespace Artemis.Core.Services
#region Controller management
- public void AddController() where T : WebApiController
+ public void AddController(PluginFeature feature) where T : WebApiController
{
- _controllers.Add(new WebApiControllerRegistration(_kernel));
+ _controllers.Add(new WebApiControllerRegistration(feature));
StartWebServer();
}
@@ -180,6 +187,26 @@ namespace Artemis.Core.Services
#endregion
+ #region Module management
+
+ public void AddModule(PluginFeature feature) where T : IWebModule
+ {
+ if (feature == null) throw new ArgumentNullException(nameof(feature));
+ if (_modules.Any(r => r.WebModuleType == typeof(T)))
+ return;
+
+ _modules.Add(new WebModuleRegistration(feature, typeof(T)));
+ StartWebServer();
+ }
+
+ public void RemoveModule() where T : IWebModule
+ {
+ _modules.RemoveAll(r => r.WebModuleType == typeof(T));
+ StartWebServer();
+ }
+
+ #endregion
+
#region Handlers
private async Task JsonExceptionHandlerCallback(IHttpContext context, Exception exception)
@@ -235,6 +262,25 @@ namespace Artemis.Core.Services
StartWebServer();
}
+ private void PluginManagementServiceOnPluginFeatureDisabled(object? sender, PluginFeatureEventArgs e)
+ {
+ bool mustRestart = false;
+ if (_controllers.Any(c => c.Feature == e.PluginFeature))
+ {
+ mustRestart = true;
+ _controllers.RemoveAll(c => c.Feature == e.PluginFeature);
+ }
+
+ if (_modules.Any(m => m.Feature == e.PluginFeature))
+ {
+ mustRestart = true;
+ _modules.RemoveAll(m => m.Feature == e.PluginFeature);
+ }
+
+ if (mustRestart)
+ StartWebServer();
+ }
+
#endregion
#region IDisposable
diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs
index 9bb5456e4..9d295e499 100644
--- a/src/Artemis.Core/Utilities/IntroAnimation.cs
+++ b/src/Artemis.Core/Utilities/IntroAnimation.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using Artemis.Core.Modules;
diff --git a/src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs b/src/Artemis.Storage/Migrations/M0001AttributeBasedPropertiesMigration.cs
similarity index 86%
rename from src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs
rename to src/Artemis.Storage/Migrations/M0001AttributeBasedPropertiesMigration.cs
index b1697878a..4ac0e1979 100644
--- a/src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs
+++ b/src/Artemis.Storage/Migrations/M0001AttributeBasedPropertiesMigration.cs
@@ -3,7 +3,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M1AttributeBasedPropertiesMigration : IStorageMigration
+ public class M0001AttributeBasedPropertiesMigration : IStorageMigration
{
public int UserVersion => 1;
diff --git a/src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs b/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs
similarity index 94%
rename from src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs
rename to src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs
index e37a48776..409e2ad23 100644
--- a/src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs
+++ b/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs
@@ -5,7 +5,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M2ProfileEntitiesEnabledMigration : IStorageMigration
+ public class M0002ProfileEntitiesEnabledMigration : IStorageMigration
{
public int UserVersion => 2;
diff --git a/src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs b/src/Artemis.Storage/Migrations/M0003PluginEntitiesIndexChangesMigration.cs
similarity index 89%
rename from src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs
rename to src/Artemis.Storage/Migrations/M0003PluginEntitiesIndexChangesMigration.cs
index 0d38b175d..17560d181 100644
--- a/src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs
+++ b/src/Artemis.Storage/Migrations/M0003PluginEntitiesIndexChangesMigration.cs
@@ -3,7 +3,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M3PluginEntitiesIndexChangesMigration : IStorageMigration
+ public class M0003PluginEntitiesIndexChangesMigration : IStorageMigration
{
public int UserVersion => 3;
diff --git a/src/Artemis.Storage/Migrations/M4ProfileSegments.cs b/src/Artemis.Storage/Migrations/M0004ProfileSegments.cs
similarity index 96%
rename from src/Artemis.Storage/Migrations/M4ProfileSegments.cs
rename to src/Artemis.Storage/Migrations/M0004ProfileSegments.cs
index c54e3dc0f..74e1c4f48 100644
--- a/src/Artemis.Storage/Migrations/M4ProfileSegments.cs
+++ b/src/Artemis.Storage/Migrations/M0004ProfileSegments.cs
@@ -7,7 +7,7 @@ using System.Linq;
namespace Artemis.Storage.Migrations
{
- public class M4ProfileSegmentsMigration : IStorageMigration
+ public class M0004ProfileSegmentsMigration : IStorageMigration
{
public int UserVersion => 4;
diff --git a/src/Artemis.Storage/Migrations/M5DataBindingTypes.cs b/src/Artemis.Storage/Migrations/M0005DataBindingTypes.cs
similarity index 94%
rename from src/Artemis.Storage/Migrations/M5DataBindingTypes.cs
rename to src/Artemis.Storage/Migrations/M0005DataBindingTypes.cs
index 7bc4bcff6..5371de084 100644
--- a/src/Artemis.Storage/Migrations/M5DataBindingTypes.cs
+++ b/src/Artemis.Storage/Migrations/M0005DataBindingTypes.cs
@@ -3,7 +3,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M5DataBindingTypes : IStorageMigration
+ public class M0005DataBindingTypes : IStorageMigration
{
public int UserVersion => 5;
diff --git a/src/Artemis.Storage/Migrations/M6PredicateAbstraction.cs b/src/Artemis.Storage/Migrations/M0006PredicateAbstraction.cs
similarity index 96%
rename from src/Artemis.Storage/Migrations/M6PredicateAbstraction.cs
rename to src/Artemis.Storage/Migrations/M0006PredicateAbstraction.cs
index 339b42358..4e7567710 100644
--- a/src/Artemis.Storage/Migrations/M6PredicateAbstraction.cs
+++ b/src/Artemis.Storage/Migrations/M0006PredicateAbstraction.cs
@@ -4,7 +4,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M6PredicateAbstraction : IStorageMigration
+ public class M0006PredicateAbstraction : IStorageMigration
{
public int UserVersion => 6;
diff --git a/src/Artemis.Storage/Migrations/M8PluginFeatures.cs b/src/Artemis.Storage/Migrations/M0008PluginFeatures.cs
similarity index 99%
rename from src/Artemis.Storage/Migrations/M8PluginFeatures.cs
rename to src/Artemis.Storage/Migrations/M0008PluginFeatures.cs
index d782ff2bd..5dc088531 100644
--- a/src/Artemis.Storage/Migrations/M8PluginFeatures.cs
+++ b/src/Artemis.Storage/Migrations/M0008PluginFeatures.cs
@@ -5,7 +5,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M8PluginFeatures : IStorageMigration
+ public class M0008PluginFeatures : IStorageMigration
{
private void Migrate(BsonValue bsonValue, Dictionary pluginMap)
{
diff --git a/src/Artemis.Storage/Migrations/M9DeviceCalibration.cs b/src/Artemis.Storage/Migrations/M0009DeviceCalibration.cs
similarity index 92%
rename from src/Artemis.Storage/Migrations/M9DeviceCalibration.cs
rename to src/Artemis.Storage/Migrations/M0009DeviceCalibration.cs
index fdb45dd8b..7d6aef3b8 100644
--- a/src/Artemis.Storage/Migrations/M9DeviceCalibration.cs
+++ b/src/Artemis.Storage/Migrations/M0009DeviceCalibration.cs
@@ -3,7 +3,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M9DeviceCalibration : IStorageMigration
+ public class M0009DeviceCalibration : IStorageMigration
{
public int UserVersion => 9;
diff --git a/src/Artemis.Storage/Migrations/M10BetterDataBindings.cs b/src/Artemis.Storage/Migrations/M0010BetterDataBindings.cs
similarity index 97%
rename from src/Artemis.Storage/Migrations/M10BetterDataBindings.cs
rename to src/Artemis.Storage/Migrations/M0010BetterDataBindings.cs
index 4b7daf20f..ae33770c0 100644
--- a/src/Artemis.Storage/Migrations/M10BetterDataBindings.cs
+++ b/src/Artemis.Storage/Migrations/M0010BetterDataBindings.cs
@@ -3,7 +3,7 @@ using LiteDB;
namespace Artemis.Storage.Migrations
{
- public class M10BetterDataBindings : IStorageMigration
+ public class M0010BetterDataBindings : IStorageMigration
{
private void Migrate(BsonValue bsonValue)
{
diff --git a/src/Artemis.Storage/Migrations/M0011ColorGradients.cs b/src/Artemis.Storage/Migrations/M0011ColorGradients.cs
new file mode 100644
index 000000000..1b4f00bd2
--- /dev/null
+++ b/src/Artemis.Storage/Migrations/M0011ColorGradients.cs
@@ -0,0 +1,42 @@
+using Artemis.Storage.Migrations.Interfaces;
+using LiteDB;
+
+namespace Artemis.Storage.Migrations
+{
+ public class M0011ColorGradients : IStorageMigration
+ {
+ private void Migrate(BsonValue bsonValue)
+ {
+ if (!bsonValue.IsDocument || !bsonValue.AsDocument.TryGetValue("PropertyEntities", out BsonValue propertyEntities))
+ return;
+
+ foreach (BsonValue propertyEntity in propertyEntities.AsArray)
+ {
+ string valueString = propertyEntity["Value"].AsString;
+ if (!valueString.StartsWith("{\"Stops\":[{") || !valueString.EndsWith("}]}"))
+ continue;
+
+ valueString = valueString.Replace("{\"Stops\":[{", "[{");
+ valueString = valueString.Replace("}]}", "}]");
+ propertyEntity["Value"] = valueString;
+ }
+ }
+
+ public int UserVersion => 11;
+
+ public void Apply(LiteRepository repository)
+ {
+ ILiteCollection collection = repository.Database.GetCollection("ProfileEntity");
+ foreach (BsonDocument bsonDocument in collection.FindAll())
+ {
+ foreach (BsonValue bsonLayer in bsonDocument["Layers"].AsArray)
+ Migrate(bsonLayer);
+
+ foreach (BsonValue bsonLayer in bsonDocument["Folders"].AsArray)
+ Migrate(bsonLayer);
+
+ collection.Update(bsonDocument);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
index bc4b3648f..6dce06e42 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
@@ -10,7 +10,6 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core;
-using SkiaSharp;
using Stylet;
namespace Artemis.UI.Shared
@@ -88,6 +87,11 @@ namespace Artemis.UI.Shared
set => SetValue(HighlightedLedsProperty, value);
}
+ ///
+ /// Occurs when a LED of the device has been clicked
+ ///
+ public event EventHandler? LedClicked;
+
///
protected override void OnRender(DrawingContext drawingContext)
{
@@ -140,6 +144,27 @@ namespace Artemis.UI.Shared
return ResizeKeepAspect(deviceSize, availableSize.Width, availableSize.Height);
}
+ ///
+ /// Invokes the event
+ ///
+ ///
+ protected virtual void OnLedClicked(LedClickedEventArgs e)
+ {
+ LedClicked?.Invoke(this, e);
+ }
+
+ ///
+ /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
+ ///
+ ///
+ /// to release both managed and unmanaged resources;
+ /// to release only unmanaged resources.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing) _timer.Stop();
+ }
+
private static Size ResizeKeepAspect(Size src, double maxWidth, double maxHeight)
{
@@ -189,8 +214,8 @@ namespace Artemis.UI.Shared
return;
Point position = e.GetPosition(this);
- double x = (position.X / RenderSize.Width);
- double y = (position.Y / RenderSize.Height);
+ double x = position.X / RenderSize.Width;
+ double y = position.Y / RenderSize.Height;
Point scaledPosition = new(x * Device.Rectangle.Width, y * Device.Rectangle.Height);
DeviceVisualizerLed? deviceVisualizerLed = _deviceVisualizerLeds.FirstOrDefault(l => l.DisplayGeometry != null && l.LedRect.Contains(scaledPosition));
@@ -317,35 +342,6 @@ namespace Artemis.UI.Shared
drawingContext.Close();
}
- #region Events
-
- public event EventHandler? LedClicked;
-
- ///
- /// Invokes the event
- ///
- ///
- protected virtual void OnLedClicked(LedClickedEventArgs e)
- {
- LedClicked?.Invoke(this, e);
- }
-
- #endregion
-
- #region IDisposable
-
- ///
- /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
- ///
- ///
- /// to release both managed and unmanaged resources;
- /// to release only unmanaged resources.
- ///
- protected virtual void Dispose(bool disposing)
- {
- if (disposing) _timer.Stop();
- }
-
///
public void Dispose()
@@ -353,7 +349,5 @@ namespace Artemis.UI.Shared
Dispose(true);
GC.SuppressFinalize(this);
}
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Controls/GradientPicker.xaml b/src/Artemis.UI.Shared/Controls/GradientPicker.xaml
index e9f083456..c13770ec8 100644
--- a/src/Artemis.UI.Shared/Controls/GradientPicker.xaml
+++ b/src/Artemis.UI.Shared/Controls/GradientPicker.xaml
@@ -37,10 +37,7 @@
Background="{StaticResource Checkerboard}">
-
+
diff --git a/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs b/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs
index eaa6883d8..ad7992140 100644
--- a/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs
+++ b/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs
@@ -1,8 +1,10 @@
using System;
+using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
+using System.Windows.Media;
using Artemis.Core;
using Artemis.UI.Shared.Properties;
using Artemis.UI.Shared.Services;
@@ -17,12 +19,14 @@ namespace Artemis.UI.Shared
{
private static IColorPickerService? _colorPickerService;
private bool _inCallback;
+ private ColorGradientToGradientStopsConverter _gradientConverter;
///
/// Creates a new instance of the class
///
public GradientPicker()
{
+ _gradientConverter = new ColorGradientToGradientStopsConverter();
InitializeComponent();
}
@@ -99,10 +103,27 @@ namespace Artemis.UI.Shared
return;
gradientPicker._inCallback = true;
+
+ if (e.OldValue is ColorGradient oldGradient)
+ oldGradient.CollectionChanged -= gradientPicker.GradientChanged;
+ if (e.NewValue is ColorGradient newGradient)
+ newGradient.CollectionChanged += gradientPicker.GradientChanged;
+ gradientPicker.UpdateGradientStops();
gradientPicker.OnPropertyChanged(nameof(ColorGradient));
+
gradientPicker._inCallback = false;
}
+ private void GradientChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ Dispatcher.Invoke(UpdateGradientStops);
+ }
+
+ private void UpdateGradientStops()
+ {
+ GradientPreview.GradientStops = (GradientStopCollection)_gradientConverter.Convert(ColorGradient, null!, null!, null!);
+ }
+
private void UIElement_OnMouseUp(object sender, MouseButtonEventArgs e)
{
if (_colorPickerService == null)
diff --git a/src/Artemis.UI.Shared/Converters/ColorGradientToGradientStopsConverter.cs b/src/Artemis.UI.Shared/Converters/ColorGradientToGradientStopsConverter.cs
index ad4138311..2a1c9de0a 100644
--- a/src/Artemis.UI.Shared/Converters/ColorGradientToGradientStopsConverter.cs
+++ b/src/Artemis.UI.Shared/Converters/ColorGradientToGradientStopsConverter.cs
@@ -14,18 +14,18 @@ namespace Artemis.UI.Shared
/// Converts into a
/// .
///
- [ValueConversion(typeof(List), typeof(GradientStopCollection))]
+ [ValueConversion(typeof(ColorGradient), typeof(GradientStopCollection))]
public class ColorGradientToGradientStopsConverter : IValueConverter
{
///
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
- List colorGradients = (List) value;
+ ColorGradient? colorGradient = value as ColorGradient;
GradientStopCollection collection = new();
- if (colorGradients == null)
+ if (colorGradient == null)
return collection;
- foreach (ColorGradientStop c in colorGradients.OrderBy(s => s.Position))
+ foreach (ColorGradientStop c in colorGradient.OrderBy(s => s.Position))
collection.Add(new GradientStop(Color.FromArgb(c.Color.Alpha, c.Color.Red, c.Color.Green, c.Color.Blue), c.Position));
return collection;
}
@@ -33,8 +33,8 @@ namespace Artemis.UI.Shared
///
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
- GradientStopCollection collection = (GradientStopCollection) value;
- List colorGradients = new();
+ GradientStopCollection? collection = value as GradientStopCollection;
+ ColorGradient colorGradients = new();
if (collection == null)
return colorGradients;
diff --git a/src/Artemis.UI.Shared/Screens/GradientEditor/ColorStopViewModel.cs b/src/Artemis.UI.Shared/Screens/GradientEditor/ColorStopViewModel.cs
index 7d93d868a..7629a9899 100644
--- a/src/Artemis.UI.Shared/Screens/GradientEditor/ColorStopViewModel.cs
+++ b/src/Artemis.UI.Shared/Screens/GradientEditor/ColorStopViewModel.cs
@@ -20,7 +20,6 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
{
_gradientEditorViewModel = gradientEditorViewModel;
ColorStop = colorStop;
- ColorStop.PropertyChanged += ColorStopOnPropertyChanged;
}
public ColorGradientStop ColorStop { get; }
@@ -58,12 +57,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
get => _willRemoveColorStop;
set => SetAndNotify(ref _willRemoveColorStop, value);
}
-
- private void ColorStopOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
- {
- _gradientEditorViewModel.ColorGradient.OnColorValuesUpdated();
- }
-
+
#region Movement
public void StopMouseDown(object sender, MouseButtonEventArgs e)
@@ -102,7 +96,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
double minValue = 0.0;
double maxValue = _gradientEditorViewModel.PreviewWidth;
- List stops = _gradientEditorViewModel.ColorGradient.Stops.OrderBy(s => s.Position).ToList();
+ List stops = _gradientEditorViewModel.ColorGradient.ToList();
ColorGradientStop? previous = stops.IndexOf(ColorStop) >= 1 ? stops[stops.IndexOf(ColorStop) - 1] : null;
ColorGradientStop? next = stops.IndexOf(ColorStop) + 1 < stops.Count ? stops[stops.IndexOf(ColorStop) + 1] : null;
if (previous != null)
@@ -111,7 +105,6 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
maxValue = next.Position * _gradientEditorViewModel.PreviewWidth;
Offset = Math.Max(minValue, Math.Min(maxValue, position.X));
- _gradientEditorViewModel.ColorGradient.OnColorValuesUpdated();
}
#endregion
diff --git a/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorView.xaml b/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorView.xaml
index 49af71b72..64e084204 100644
--- a/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorView.xaml
+++ b/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorView.xaml
@@ -66,7 +66,7 @@
diff --git a/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs b/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs
index f53a099af..497d81963 100644
--- a/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs
+++ b/src/Artemis.UI.Shared/Screens/GradientEditor/GradientEditorViewModel.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
@@ -6,6 +7,7 @@ using System.Windows.Controls;
using System.Windows.Input;
using Artemis.Core;
using Artemis.UI.Shared.Services;
+using MaterialDesignThemes.Wpf;
using Stylet;
namespace Artemis.UI.Shared.Screens.GradientEditor
@@ -21,12 +23,24 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
ColorGradient = colorGradient;
ColorStopViewModels = new BindableCollection();
- _originalStops = ColorGradient.Stops.Select(s => new ColorGradientStop(s.Color, s.Position)).ToList();
+ _originalStops = ColorGradient.Select(s => new ColorGradientStop(s.Color, s.Position)).ToList();
PropertyChanged += UpdateColorStopViewModels;
+ ColorGradient.CollectionChanged += ColorGradientOnCollectionChanged;
}
- public BindableCollection ColorStopViewModels { get; set; }
+ #region Overrides of DialogViewModelBase
+
+ ///
+ public override void OnDialogClosed(object sender, DialogClosingEventArgs e)
+ {
+ ColorGradient.CollectionChanged -= ColorGradientOnCollectionChanged;
+ base.OnDialogClosed(sender, e);
+ }
+
+ #endregion
+
+ public BindableCollection ColorStopViewModels { get; }
public ColorStopViewModel? SelectedColorStopViewModel
{
@@ -48,15 +62,19 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
set => SetAndNotify(ref _previewWidth, value);
}
+ public ColorGradient Stops
+ {
+ get => ColorGradient;
+ }
+
public void AddColorStop(object sender, MouseEventArgs e)
{
Canvas? child = VisualTreeUtilities.FindChild