diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index e9f74c2d9..55f18200f 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -204,7 +204,7 @@ namespace Artemis.Core.Models.Profile
///
public override void Update(double deltaTime)
{
- if (LayerBrush == null || !LayerBrush.BaseProperties.PropertiesInitialized)
+ if (LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
return;
var properties = new List(General.GetAllLayerProperties().Where(p => p.BaseKeyframes.Any()));
@@ -269,6 +269,9 @@ namespace Artemis.Core.Models.Profile
private void StretchRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
{
+ if (LayerBrush == null || !LayerBrush.BaseProperties.PropertiesInitialized || LayerBrush.BrushType != LayerBrushType.Regular)
+ return;
+
// Apply transformations
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
@@ -285,12 +288,14 @@ namespace Artemis.Core.Models.Profile
canvas.Scale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y);
canvas.Translate(x, y);
- if (LayerBrush != null && LayerBrush.BaseProperties.PropertiesInitialized)
- LayerBrush.Render(canvas, canvasInfo, new SKPath(LayerShape.Path), paint);
+ LayerBrush.InternalRender(canvas, canvasInfo, new SKPath(LayerShape.Path), paint);
}
private void ClipRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
{
+ if (LayerBrush == null || !LayerBrush.BaseProperties.PropertiesInitialized || LayerBrush.BrushType != LayerBrushType.Regular)
+ return;
+
// Apply transformations
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
@@ -321,7 +326,8 @@ namespace Artemis.Core.Models.Profile
);
var renderPath = new SKPath();
renderPath.AddRect(boundsRect);
- LayerBrush?.Render(canvas, canvasInfo, renderPath, paint);
+
+ LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
}
internal void CalculateRenderProperties()
diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
index 5b0dc8a60..68d4bf8ae 100644
--- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
+++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
@@ -165,6 +165,9 @@ namespace Artemis.Core.Models.Profile
internal void ApplyToEntity()
{
+ if (!PropertiesInitialized)
+ return;
+
// Get all properties with a PropertyDescriptionAttribute
foreach (var propertyInfo in GetType().GetProperties())
{
diff --git a/src/Artemis.Core/Plugins/LayerBrush/BaseLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/BaseLayerBrush.cs
index 5a06297f8..7c3c41514 100644
--- a/src/Artemis.Core/Plugins/LayerBrush/BaseLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrush/BaseLayerBrush.cs
@@ -1,17 +1,25 @@
using System;
+using System.Linq;
using Artemis.Core.Models.Profile;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces;
+using RGB.NET.Core;
+using RGB.NET.Groups;
using SkiaSharp;
namespace Artemis.Core.Plugins.LayerBrush
{
///
- /// A basic layer brush that does not implement any layer property, to use properties with persistent storage,
- /// implement instead
+ /// For internal use only, please use or or instead
///
public abstract class BaseLayerBrush : IDisposable
{
+ protected BaseLayerBrush(Layer layer, LayerBrushDescriptor descriptor)
+ {
+ Layer = layer;
+ Descriptor = descriptor;
+ }
+
///
/// Gets the layer this brush is applied to
///
@@ -27,38 +35,43 @@ namespace Artemis.Core.Plugins.LayerBrush
///
public PluginInfo PluginInfo => Descriptor.LayerBrushProvider.PluginInfo;
- public virtual LayerPropertyGroup BaseProperties => null;
+ ///
+ /// Gets the type of layer brush
+ ///
+ public LayerBrushType BrushType { get; internal set; }
+ ///
+ /// Gets a reference to the layer property group without knowing it's type
+ ///
+ public virtual LayerPropertyGroup BaseProperties => null;
+
///
/// Called when the brush is being removed from the layer
///
- public virtual void Dispose()
- {
- }
+ public abstract void Dispose();
///
/// Called before rendering every frame, write your update logic here
///
///
- public virtual void Update(double deltaTime)
- {
- }
+ public abstract void Update(double deltaTime);
+
+ // Not only is this needed to initialize properties on the layer brushes, it also prevents implementing anything
+ // but LayerBrush and RgbNetLayerBrush outside the core
+ internal abstract void Initialize(ILayerService layerService);
+
+ internal abstract void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint);
///
- /// The main method of rendering anything to the layer. The provided is specific to the layer
- /// and matches it's width and height.
- /// Called during rendering or layer preview, in the order configured on the layer
+ /// Called when Artemis needs an instance of the RGB.NET brush you are implementing
///
- /// The layer canvas
- ///
- /// The path to be filled, represents the shape
- /// The paint to be used to fill the shape
- public virtual void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
- {
- }
+ /// Your RGB.NET brush
+ internal abstract IBrush InternalGetBrush();
+ }
- internal virtual void InitializeProperties(ILayerService layerService, string path)
- {
- }
+ public enum LayerBrushType
+ {
+ Regular,
+ RgbNet
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs
index 6c518d3c4..a466492ff 100644
--- a/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs
@@ -1,77 +1,52 @@
using System;
-using System.Collections.Generic;
using Artemis.Core.Models.Profile;
-using Artemis.Core.Models.Profile.LayerProperties;
-using Artemis.Core.Plugins.Exceptions;
using Artemis.Core.Services.Interfaces;
+using RGB.NET.Core;
+using SkiaSharp;
namespace Artemis.Core.Plugins.LayerBrush
{
- public abstract class LayerBrush : BaseLayerBrush where T : LayerPropertyGroup
+ public abstract class LayerBrush : PropertiesLayerBrush where T : LayerPropertyGroup
{
- private T _properties;
-
- protected LayerBrush(Layer layer, LayerBrushDescriptor descriptor)
+ protected LayerBrush(Layer layer, LayerBrushDescriptor descriptor) : base(layer, descriptor)
{
- Layer = layer;
- Descriptor = descriptor;
- }
-
- #region Properties
-
- ///
- /// Gets the properties of this brush.
- ///
- public T Properties
- {
- get
- {
- // I imagine a null reference here can be confusing, so lets throw an exception explaining what to do
- if (_properties == null)
- throw new ArtemisPluginException("Cannot access brush properties until OnPropertiesInitialized has been called");
- return _properties;
- }
- internal set => _properties = value;
+ BrushType = LayerBrushType.Regular;
}
///
- /// Gets whether all properties on this brush are initialized
+ /// The main method of rendering anything to the layer. The provided is specific to the layer
+ /// and matches it's width and height.
+ /// Called during rendering or layer preview, in the order configured on the layer
///
- public bool PropertiesInitialized { get; private set; }
+ /// The layer canvas
+ ///
+ /// The path to be filled, represents the shape
+ /// The paint to be used to fill the shape
+ public abstract void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint);
+
+ internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
+ {
+ Render(canvas, canvasInfo, path, paint);
+ }
- ///
- /// Called when all layer properties in this brush have been initialized
- ///
- protected virtual void OnPropertiesInitialized()
+ internal override IBrush InternalGetBrush()
+ {
+ throw new NotImplementedException("Regular layer brushes do not implement InternalGetBrush");
+ }
+
+ internal override void Initialize(ILayerService layerService)
+ {
+ InitializeProperties(layerService);
+ }
+
+ protected virtual void Dispose(bool disposing)
{
}
- ///
- public override LayerPropertyGroup BaseProperties => Properties;
-
- internal override void InitializeProperties(ILayerService layerService, string path)
+ public sealed override void Dispose()
{
- Properties = Activator.CreateInstance();
- Properties.InitializeProperties(layerService, Layer, path);
- OnPropertiesInitialized();
- PropertiesInitialized = true;
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
-
- internal virtual void ApplyToEntity()
- {
- Properties.ApplyToEntity();
- }
-
- internal virtual void OverrideProperties(TimeSpan overrideTime)
- {
- Properties.Override(overrideTime);
- }
-
- internal virtual IReadOnlyCollection GetAllLayerProperties()
- {
- return Properties.GetAllLayerProperties();
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerBrush/PropertiesLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/PropertiesLayerBrush.cs
new file mode 100644
index 000000000..ee7010c3c
--- /dev/null
+++ b/src/Artemis.Core/Plugins/LayerBrush/PropertiesLayerBrush.cs
@@ -0,0 +1,57 @@
+using System;
+using Artemis.Core.Models.Profile;
+using Artemis.Core.Plugins.Exceptions;
+using Artemis.Core.Services.Interfaces;
+
+namespace Artemis.Core.Plugins.LayerBrush
+{
+ ///
+ /// For internal use only, please use or or instead
+ ///
+ public abstract class PropertiesLayerBrush : BaseLayerBrush where T : LayerPropertyGroup
+ {
+ private T _properties;
+
+ protected PropertiesLayerBrush(Layer layer, LayerBrushDescriptor descriptor) : base(layer, descriptor)
+ {
+ }
+
+ ///
+ /// Gets whether all properties on this brush are initialized
+ ///
+ public bool PropertiesInitialized { get; internal set; }
+
+ ///
+ public override LayerPropertyGroup BaseProperties => Properties;
+
+ ///
+ /// Gets the properties of this brush.
+ ///
+ public T Properties
+ {
+ get
+ {
+ // I imagine a null reference here can be confusing, so lets throw an exception explaining what to do
+ if (_properties == null)
+ throw new ArtemisPluginException("Cannot access brush properties until OnPropertiesInitialized has been called");
+ return _properties;
+ }
+ internal set => _properties = value;
+ }
+
+ ///
+ /// Called when all layer properties in this brush have been initialized
+ ///
+ protected virtual void OnPropertiesInitialized()
+ {
+ }
+
+ internal void InitializeProperties(ILayerService layerService)
+ {
+ Properties = Activator.CreateInstance();
+ Properties.InitializeProperties(layerService, Layer, "LayerBrush.");
+ OnPropertiesInitialized();
+ PropertiesInitialized = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerBrush/RgbNetLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/RgbNetLayerBrush.cs
new file mode 100644
index 000000000..3cbf70fe3
--- /dev/null
+++ b/src/Artemis.Core/Plugins/LayerBrush/RgbNetLayerBrush.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Linq;
+using Artemis.Core.Models.Profile;
+using Artemis.Core.Services.Interfaces;
+using RGB.NET.Core;
+using RGB.NET.Groups;
+using SkiaSharp;
+
+namespace Artemis.Core.Plugins.LayerBrush
+{
+ public abstract class RgbNetLayerBrush : PropertiesLayerBrush where T : LayerPropertyGroup
+ {
+ protected RgbNetLayerBrush(Layer layer, LayerBrushDescriptor descriptor) : base(layer, descriptor)
+ {
+ BrushType = LayerBrushType.RgbNet;
+ LedGroup = new ListLedGroup();
+
+ Layer = layer;
+ Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
+ UpdateLedGroup();
+ }
+
+ ///
+ /// The LED group this layer brush is applied to
+ ///
+ public ListLedGroup LedGroup { get; internal set; }
+
+ ///
+ /// Called when Artemis needs an instance of the RGB.NET brush you are implementing
+ ///
+ /// Your RGB.NET brush
+ public abstract IBrush GetBrush();
+
+ public sealed override void Dispose()
+ {
+ Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated;
+ LedGroup.Detach();
+
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ internal void UpdateLedGroup()
+ {
+ // TODO: This simply renders it on top of the rest, get a ZIndex based on layer position
+ LedGroup.ZIndex = 1;
+
+ var missingLeds = Layer.Leds.Where(l => !LedGroup.ContainsLed(l.RgbLed)).Select(l => l.RgbLed).ToList();
+ var extraLeds = LedGroup.GetLeds().Where(l => Layer.Leds.All(layerLed => layerLed.RgbLed != l)).ToList();
+ LedGroup.AddLeds(missingLeds);
+ LedGroup.RemoveLeds(extraLeds);
+ LedGroup.Brush = GetBrush();
+ }
+
+ internal override void Initialize(ILayerService layerService)
+ {
+ InitializeProperties(layerService);
+ }
+
+ // Not used in this brush type
+ internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
+ {
+ throw new NotImplementedException("RGB.NET layer brushes do not implement InternalRender");
+ }
+
+ internal override IBrush InternalGetBrush()
+ {
+ return GetBrush();
+ }
+
+ private void LayerOnRenderPropertiesUpdated(object sender, EventArgs e)
+ {
+ UpdateLedGroup();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/LayerService.cs b/src/Artemis.Core/Services/LayerService.cs
index b080b8b5a..42e8e33e3 100644
--- a/src/Artemis.Core/Services/LayerService.cs
+++ b/src/Artemis.Core/Services/LayerService.cs
@@ -58,8 +58,8 @@ namespace Artemis.Core.Services
new ConstructorArgument("layer", layer),
new ConstructorArgument("descriptor", descriptor)
};
- layer.LayerBrush = (BaseLayerBrush)_kernel.Get(descriptor.LayerBrushType, arguments); ;
- layer.LayerBrush.InitializeProperties(this, "LayerBrush.");
+ layer.LayerBrush = (BaseLayerBrush) _kernel.Get(descriptor.LayerBrushType, arguments);
+ layer.LayerBrush.Initialize(this);
layer.OnLayerBrushUpdated();
return layer.LayerBrush;
}
diff --git a/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs b/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs
index a001c5c9d..198c77110 100644
--- a/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs
+++ b/src/Artemis.UI.Shared/Controls/GradientPicker.xaml.cs
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -8,6 +9,7 @@ using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Colors;
using Artemis.UI.Shared.Annotations;
using Artemis.UI.Shared.Services.Interfaces;
+using Stylet;
namespace Artemis.UI.Shared.Controls
{
@@ -19,6 +21,9 @@ namespace Artemis.UI.Shared.Controls
private static IGradientPickerService _gradientPickerService;
private bool _inCallback;
+ public event EventHandler DialogOpened;
+ public event EventHandler DialogClosed;
+
public GradientPicker()
{
InitializeComponent();
@@ -77,7 +82,12 @@ namespace Artemis.UI.Shared.Controls
private void UIElement_OnMouseUp(object sender, MouseButtonEventArgs e)
{
- GradientPickerService.ShowGradientPicker(ColorGradient, DialogHost);
+ Execute.OnUIThread(async () =>
+ {
+ OnDialogOpened();
+ await GradientPickerService.ShowGradientPicker(ColorGradient, DialogHost);
+ OnDialogClosed();
+ });
}
#region Static WPF fields
@@ -92,5 +102,15 @@ namespace Artemis.UI.Shared.Controls
EventManager.RegisterRoutedEvent(nameof(ColorGradient), RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(GradientPicker));
#endregion
+
+ protected virtual void OnDialogOpened()
+ {
+ DialogOpened?.Invoke(this, EventArgs.Empty);
+ }
+
+ protected virtual void OnDialogClosed()
+ {
+ DialogClosed?.Invoke(this, EventArgs.Empty);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/GradientPickerService.cs b/src/Artemis.UI.Shared/Services/GradientPickerService.cs
index f246c5dd4..608ea0401 100644
--- a/src/Artemis.UI.Shared/Services/GradientPickerService.cs
+++ b/src/Artemis.UI.Shared/Services/GradientPickerService.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Threading.Tasks;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Colors;
using Artemis.UI.Shared.Screens.GradientEditor;
@@ -15,12 +16,11 @@ namespace Artemis.UI.Shared.Services
_dialogService = dialogService;
}
- public void ShowGradientPicker(ColorGradient colorGradient, string dialogHost)
+ public Task