diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj
index afb0d5028..7ab16b561 100644
--- a/src/Artemis.Core/Artemis.Core.csproj
+++ b/src/Artemis.Core/Artemis.Core.csproj
@@ -70,4 +70,9 @@
..\..\..\RGB.NET\bin\netstandard2.0\RGB.NET.Groups.dll
+
+
+ PreserveNewest
+
+
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 6513c3d67..0eefa1da7 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Plugins.LayerEffect.Abstract;
+using Artemis.Core.Utilities;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using SkiaSharp;
@@ -61,9 +62,13 @@ namespace Artemis.Core.Models.Profile
internal FolderEntity FolderEntity { get; set; }
internal override RenderElementEntity RenderElementEntity => FolderEntity;
+ public bool IsRootFolder => Parent == Profile;
public override void Update(double deltaTime)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
if (!Enabled)
return;
@@ -89,6 +94,9 @@ namespace Artemis.Core.Models.Profile
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
if (!Enabled)
return;
@@ -125,6 +133,9 @@ namespace Artemis.Core.Models.Profile
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
if (Path == null || !Enabled || !Children.Any(c => c.Enabled))
return;
@@ -173,6 +184,13 @@ namespace Artemis.Core.Models.Profile
folderCanvas.Restore();
}
+ // If required, apply the opacity override of the module to the root folder
+ if (IsRootFolder && Profile.Module.OpacityOverride < 1)
+ {
+ var multiplier = Easings.SineEaseInOut(Profile.Module.OpacityOverride);
+ folderPaint.Color = folderPaint.Color.WithAlpha((byte) (folderPaint.Color.Alpha * multiplier));
+ }
+
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint);
canvas.DrawBitmap(_folderBitmap, targetLocation, folderPaint);
@@ -187,6 +205,9 @@ namespace Artemis.Core.Models.Profile
///
public Folder AddFolder(string name)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
var folder = new Folder(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1};
AddChild(folder);
return folder;
@@ -195,6 +216,9 @@ namespace Artemis.Core.Models.Profile
///
public override void AddChild(ProfileElement child, int? order = null)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
base.AddChild(child, order);
CalculateRenderProperties();
}
@@ -202,6 +226,9 @@ namespace Artemis.Core.Models.Profile
///
public override void RemoveChild(ProfileElement child)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
base.RemoveChild(child);
CalculateRenderProperties();
}
@@ -213,6 +240,9 @@ namespace Artemis.Core.Models.Profile
public void CalculateRenderProperties()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
var path = new SKPath {FillType = SKPathFillType.Winding};
foreach (var child in Children)
{
@@ -229,9 +259,32 @@ namespace Artemis.Core.Models.Profile
OnRenderPropertiesUpdated();
}
+ protected override void Dispose(bool disposing)
+ {
+ if (!disposing)
+ return;
+
+ foreach (var baseLayerEffect in LayerEffects)
+ baseLayerEffect.Dispose();
+ _layerEffects.Clear();
+
+ foreach (var profileElement in Children)
+ profileElement.Dispose();
+ ChildrenList.Clear();
+
+ _folderBitmap?.Dispose();
+ _folderBitmap = null;
+
+ Profile = null;
+ _disposed = true;
+ }
+
internal override void ApplyToEntity()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Folder");
+
FolderEntity.Id = EntityId;
FolderEntity.ParentId = Parent?.EntityId ?? new Guid();
@@ -250,16 +303,6 @@ namespace Artemis.Core.Models.Profile
DisplayConditionGroup?.ApplyToEntity();
}
- internal void Deactivate()
- {
- _folderBitmap?.Dispose();
- _folderBitmap = null;
-
- var layerEffects = new List(LayerEffects);
- foreach (var baseLayerEffect in layerEffects)
- DeactivateLayerEffect(baseLayerEffect);
- }
-
#region Events
public event EventHandler RenderPropertiesUpdated;
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 6f93f36c7..fdd212c4f 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -126,6 +126,9 @@ namespace Artemis.Core.Models.Profile
///
public override List GetAllKeyframes()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
var keyframes = base.GetAllKeyframes();
foreach (var baseLayerProperty in General.GetAllLayerProperties())
@@ -141,10 +144,38 @@ namespace Artemis.Core.Models.Profile
return keyframes;
}
+ protected override void Dispose(bool disposing)
+ {
+ if (!disposing)
+ return;
+
+ _disposed = true;
+
+ // Brush first in case it depends on any of the other disposables during it's own disposal
+ _layerBrush?.Dispose();
+ _layerBrush = null;
+
+ foreach (var baseLayerEffect in LayerEffects)
+ baseLayerEffect.Dispose();
+ _layerEffects.Clear();
+
+ _general?.Dispose();
+ _general = null;
+ _layerBitmap?.Dispose();
+ _layerBitmap = null;
+ _transform?.Dispose();
+ _transform = null;
+
+ Profile = null;
+ }
+
#region Storage
internal override void ApplyToEntity()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
// Properties
LayerEntity.Id = EntityId;
LayerEntity.ParentId = Parent?.EntityId ?? new Guid();
@@ -217,6 +248,9 @@ namespace Artemis.Core.Models.Profile
///
public override void Update(double deltaTime)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
return;
@@ -245,6 +279,9 @@ namespace Artemis.Core.Models.Profile
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
return;
@@ -254,9 +291,7 @@ namespace Artemis.Core.Models.Profile
{
if (!DisplayContinuously)
{
- var position = timeOverride + StartSegmentLength;
- if (position > StartSegmentLength + EndSegmentLength)
- TimelinePosition = StartSegmentLength + EndSegmentLength;
+ TimelinePosition = StartSegmentLength + timeOverride;
}
else
{
@@ -287,6 +322,9 @@ namespace Artemis.Core.Models.Profile
///
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
if (!Enabled || TimelinePosition > TimelineLength)
return;
@@ -328,9 +366,9 @@ namespace Artemis.Core.Models.Profile
if (!LayerBrush.SupportsTransformation)
SimpleRender(layerCanvas, _layerBitmap.Info, layerPaint, layerPath);
- else if (General.FillType.CurrentValue == LayerFillType.Stretch)
+ else if (General.ResizeMode.CurrentValue == LayerResizeMode.Normal)
StretchRender(layerCanvas, _layerBitmap.Info, layerPaint, layerPath);
- else if (General.FillType.CurrentValue == LayerFillType.Clip)
+ else if (General.ResizeMode.CurrentValue == LayerResizeMode.Clip)
ClipRender(layerCanvas, _layerBitmap.Info, layerPaint, layerPath);
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
@@ -340,7 +378,7 @@ namespace Artemis.Core.Models.Profile
if (Parent is Folder parentFolder)
targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location;
-
+
canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint);
}
@@ -410,6 +448,9 @@ namespace Artemis.Core.Models.Profile
internal void CalculateRenderProperties()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
if (!Leds.Any())
Path = new SKPath();
else
@@ -434,6 +475,9 @@ namespace Artemis.Core.Models.Profile
internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool zeroBased = false)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
var positionProperty = Transform.Position.CurrentValue;
// Start at the center of the shape
@@ -455,6 +499,9 @@ namespace Artemis.Core.Models.Profile
///
public void IncludePathInTranslation(SKPath path, bool zeroBased)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
@@ -465,10 +512,10 @@ namespace Artemis.Core.Models.Profile
var x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
var y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
- if (General.FillType == LayerFillType.Stretch)
+ if (General.ResizeMode == LayerResizeMode.Normal)
{
path.Transform(SKMatrix.MakeTranslation(x, y));
- path.Transform(SKMatrix.MakeScale((sizeProperty.Width / 100f), (sizeProperty.Height / 100f), anchorPosition.X, anchorPosition.Y));
+ path.Transform(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y));
path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y));
}
else
@@ -484,6 +531,9 @@ namespace Artemis.Core.Models.Profile
///
public void ExcludePathFromTranslation(SKPath path, bool zeroBased)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
@@ -497,7 +547,7 @@ namespace Artemis.Core.Models.Profile
var reversedXScale = 1f / (sizeProperty.Width / 100f);
var reversedYScale = 1f / (sizeProperty.Height / 100f);
- if (General.FillType == LayerFillType.Stretch)
+ if (General.ResizeMode == LayerResizeMode.Normal)
{
path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
path.Transform(SKMatrix.MakeScale(reversedXScale, reversedYScale, anchorPosition.X, anchorPosition.Y));
@@ -517,6 +567,9 @@ namespace Artemis.Core.Models.Profile
/// The number of transformations applied
public int ExcludeCanvasFromTranslation(SKCanvas canvas, bool zeroBased)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
@@ -530,7 +583,7 @@ namespace Artemis.Core.Models.Profile
var reversedXScale = 1f / (sizeProperty.Width / 100f);
var reversedYScale = 1f / (sizeProperty.Height / 100f);
- if (General.FillType == LayerFillType.Stretch)
+ if (General.ResizeMode == LayerResizeMode.Normal)
{
canvas.Translate(x * -1, y * -1);
canvas.Scale(reversedXScale, reversedYScale, anchorPosition.X, anchorPosition.Y);
@@ -554,6 +607,9 @@ namespace Artemis.Core.Models.Profile
/// The LED to add
public void AddLed(ArtemisLed led)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
_leds.Add(led);
CalculateRenderProperties();
}
@@ -564,6 +620,9 @@ namespace Artemis.Core.Models.Profile
/// The LEDs to add
public void AddLeds(IEnumerable leds)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
_leds.AddRange(leds);
CalculateRenderProperties();
}
@@ -574,6 +633,9 @@ namespace Artemis.Core.Models.Profile
/// The LED to remove
public void RemoveLed(ArtemisLed led)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
_leds.Remove(led);
CalculateRenderProperties();
}
@@ -583,12 +645,18 @@ namespace Artemis.Core.Models.Profile
///
public void ClearLeds()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
_leds.Clear();
CalculateRenderProperties();
}
internal void PopulateLeds(ArtemisSurface surface)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Layer");
+
var leds = new List();
// Get the surface LEDs for this layer
@@ -609,17 +677,6 @@ namespace Artemis.Core.Models.Profile
#region Activation
- internal void Deactivate()
- {
- _layerBitmap?.Dispose();
- _layerBitmap = null;
-
- DeactivateLayerBrush();
- var layerEffects = new List(LayerEffects);
- foreach (var baseLayerEffect in layerEffects)
- DeactivateLayerEffect(baseLayerEffect);
- }
-
internal void DeactivateLayerBrush()
{
if (LayerBrush == null)
@@ -666,9 +723,9 @@ namespace Artemis.Core.Models.Profile
Rectangle
}
- public enum LayerFillType
+ public enum LayerResizeMode
{
- Stretch,
+ Normal,
Clip
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs b/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
index e0a2b46d4..6ce5fa92f 100644
--- a/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
+++ b/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
@@ -9,8 +9,8 @@ namespace Artemis.Core.Models.Profile
[PropertyDescription(Name = "Shape type", Description = "The type of shape to draw in this layer")]
public EnumLayerProperty ShapeType { get; set; }
- [PropertyDescription(Name = "Fill type", Description = "How to make the shape adjust to scale changes")]
- public EnumLayerProperty FillType { get; set; }
+ [PropertyDescription(Name = "Resize mode", Description = "How to make the shape adjust to scale changes")]
+ public EnumLayerProperty ResizeMode { get; set; }
[PropertyDescription(Name = "Blend mode", Description = "How to blend this layer into the resulting image")]
public EnumLayerProperty BlendMode { get; set; }
@@ -21,11 +21,15 @@ namespace Artemis.Core.Models.Profile
protected override void PopulateDefaults()
{
ShapeType.DefaultValue = LayerShapeType.Rectangle;
- FillType.DefaultValue = LayerFillType.Stretch;
+ ResizeMode.DefaultValue = LayerResizeMode.Normal;
BlendMode.DefaultValue = SKBlendMode.SrcOver;
}
- protected override void OnPropertiesInitialized()
+ protected override void EnableProperties()
+ {
+ }
+
+ protected override void DisableProperties()
{
}
}
diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
index 04a1d56b6..3c29ea526 100644
--- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
+++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
@@ -15,7 +15,7 @@ using Artemis.Storage.Entities.Profile;
namespace Artemis.Core.Models.Profile
{
- public abstract class LayerPropertyGroup
+ public abstract class LayerPropertyGroup : IDisposable
{
private readonly List _layerProperties;
private readonly List _layerPropertyGroups;
@@ -78,11 +78,6 @@ namespace Artemis.Core.Models.Profile
}
}
- ///
- /// Gets or sets whether the group is expanded in the UI
- ///
- public bool IsExpanded { get; set; }
-
///
/// A list of all layer properties in this group
///
@@ -112,17 +107,28 @@ namespace Artemis.Core.Models.Profile
return _allLayerProperties;
}
+ public void Dispose()
+ {
+ DisableProperties();
+ foreach (var layerPropertyGroup in _layerPropertyGroups)
+ layerPropertyGroup.Dispose();
+ }
+
///
- /// Called before properties are fully initialized to allow you to populate
- /// on the properties you want
+ /// Called before property group is activated to allow you to populate on
+ /// the properties you want
///
protected abstract void PopulateDefaults();
///
- /// Called when all layer properties in this property group have been initialized, you may access all properties on the
- /// group here
+ /// Called when the property group is deactivated
///
- protected abstract void OnPropertiesInitialized();
+ protected abstract void EnableProperties();
+
+ ///
+ /// Called when the property group is deactivated (either the profile unloaded or the related brush/effect was removed)
+ ///
+ protected abstract void DisableProperties();
protected virtual void OnPropertyGroupInitialized()
{
@@ -195,7 +201,7 @@ namespace Artemis.Core.Models.Profile
foreach (var layerProperty in _layerProperties.Where(p => !p.IsLoadedFromStorage))
layerProperty.ApplyDefaultValue();
- OnPropertiesInitialized();
+ EnableProperties();
PropertiesInitialized = true;
OnPropertyGroupInitialized();
}
diff --git a/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs b/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
index 81b35a838..8a6afca51 100644
--- a/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
+++ b/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
@@ -27,7 +27,11 @@ namespace Artemis.Core.Models.Profile
Opacity.DefaultValue = 100;
}
- protected override void OnPropertiesInitialized()
+ protected override void EnableProperties()
+ {
+ }
+
+ protected override void DisableProperties()
{
}
}
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index df31ed9e2..b130da1bd 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Exceptions;
using Artemis.Core.Models.Surface;
+using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Models;
using Artemis.Storage.Entities.Profile;
using SkiaSharp;
@@ -13,12 +14,12 @@ namespace Artemis.Core.Models.Profile
{
private bool _isActivated;
- internal Profile(PluginInfo pluginInfo, string name)
+ internal Profile(ProfileModule module, string name)
{
ProfileEntity = new ProfileEntity();
EntityId = Guid.NewGuid();
- PluginInfo = pluginInfo;
+ Module = module;
Name = name;
UndoStack = new Stack();
RedoStack = new Stack();
@@ -27,19 +28,19 @@ namespace Artemis.Core.Models.Profile
ApplyToEntity();
}
- internal Profile(PluginInfo pluginInfo, ProfileEntity profileEntity)
+ internal Profile(ProfileModule module, ProfileEntity profileEntity)
{
ProfileEntity = profileEntity;
EntityId = profileEntity.Id;
- PluginInfo = pluginInfo;
+ Module = module;
UndoStack = new Stack();
RedoStack = new Stack();
ApplyToProfile();
}
- public PluginInfo PluginInfo { get; }
+ public ProfileModule Module { get; }
public bool IsActivated
{
@@ -56,6 +57,8 @@ namespace Artemis.Core.Models.Profile
{
lock (this)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Profile");
if (!IsActivated)
throw new ArtemisCoreException($"Cannot update inactive profile: {this}");
@@ -68,6 +71,8 @@ namespace Artemis.Core.Models.Profile
{
lock (this)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Profile");
if (!IsActivated)
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
@@ -78,21 +83,26 @@ namespace Artemis.Core.Models.Profile
public Folder GetRootFolder()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Profile");
+
return (Folder) Children.Single();
}
public void ApplyToProfile()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Profile");
+
Name = ProfileEntity.Name;
lock (ChildrenList)
{
- foreach (var folder in GetAllFolders())
- folder.Deactivate();
- foreach (var layer in GetAllLayers())
- layer.Deactivate();
-
+ // Remove the old profile tree
+ foreach (var profileElement in Children)
+ profileElement.Dispose();
ChildrenList.Clear();
+
// Populate the profile starting at the root, the rest is populated recursively
var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
if (rootFolder == null)
@@ -104,13 +114,31 @@ namespace Artemis.Core.Models.Profile
public override string ToString()
{
- return $"[Profile] {nameof(Name)}: {Name}, {nameof(IsActivated)}: {IsActivated}, {nameof(PluginInfo)}: {PluginInfo}";
+ return $"[Profile] {nameof(Name)}: {Name}, {nameof(IsActivated)}: {IsActivated}, {nameof(Module)}: {Module}";
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (!disposing)
+ return;
+
+ OnDeactivating();
+
+ foreach (var profileElement in Children)
+ profileElement.Dispose();
+ ChildrenList.Clear();
+
+ IsActivated = false;
+ _disposed = true;
}
internal override void ApplyToEntity()
{
+ if (_disposed)
+ throw new ObjectDisposedException("Profile");
+
ProfileEntity.Id = EntityId;
- ProfileEntity.PluginGuid = PluginInfo.Guid;
+ ProfileEntity.PluginGuid = Module.PluginInfo.Guid;
ProfileEntity.Name = Name;
ProfileEntity.IsActive = IsActivated;
@@ -128,6 +156,8 @@ namespace Artemis.Core.Models.Profile
{
lock (this)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Profile");
if (IsActivated)
return;
@@ -137,25 +167,11 @@ namespace Artemis.Core.Models.Profile
}
}
- internal void Deactivate()
- {
- lock (this)
- {
- if (!IsActivated)
- return;
-
- foreach (var folder in GetAllFolders())
- folder.Deactivate();
- foreach (var layer in GetAllLayers())
- layer.Deactivate();
-
- IsActivated = false;
- OnDeactivated();
- }
- }
-
internal void PopulateLeds(ArtemisSurface surface)
{
+ if (_disposed)
+ throw new ObjectDisposedException("Profile");
+
foreach (var layer in GetAllLayers())
layer.PopulateLeds(surface);
}
@@ -163,7 +179,7 @@ namespace Artemis.Core.Models.Profile
#region Events
///
- /// Occurs when the profile is being activated.
+ /// Occurs when the profile has been activated.
///
public event EventHandler Activated;
@@ -177,7 +193,7 @@ namespace Artemis.Core.Models.Profile
Activated?.Invoke(this, EventArgs.Empty);
}
- private void OnDeactivated()
+ private void OnDeactivating()
{
Deactivated?.Invoke(this, EventArgs.Empty);
}
diff --git a/src/Artemis.Core/Models/Profile/ProfileDescriptor.cs b/src/Artemis.Core/Models/Profile/ProfileDescriptor.cs
new file mode 100644
index 000000000..2ff49ab28
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/ProfileDescriptor.cs
@@ -0,0 +1,24 @@
+using System;
+using Artemis.Core.Plugins.Abstract;
+using Artemis.Storage.Entities.Profile;
+
+namespace Artemis.Core.Models.Profile
+{
+ public class ProfileDescriptor
+ {
+ internal ProfileDescriptor(ProfileModule profileModule, ProfileEntity profileEntity)
+ {
+ ProfileModule = profileModule;
+
+ Id = profileEntity.Id;
+ Name = profileEntity.Name;
+ IsLastActiveProfile = profileEntity.IsActive;
+ }
+
+ public bool IsLastActiveProfile { get; set; }
+
+ public Guid Id { get; }
+ public ProfileModule ProfileModule { get; }
+ public string Name { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/ProfileElement.cs b/src/Artemis.Core/Models/Profile/ProfileElement.cs
index 79588e4ae..5316a4b69 100644
--- a/src/Artemis.Core/Models/Profile/ProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/ProfileElement.cs
@@ -7,8 +7,22 @@ using Stylet;
namespace Artemis.Core.Models.Profile
{
- public abstract class ProfileElement : PropertyChangedBase
+ public abstract class ProfileElement : PropertyChangedBase, IDisposable
{
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected bool _disposed;
private bool _enabled;
private Guid _entityId;
private string _name;
@@ -85,6 +99,9 @@ namespace Artemis.Core.Models.Profile
public List GetAllFolders()
{
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().Name);
+
var folders = new List();
foreach (var childFolder in Children.Where(c => c is Folder).Cast())
{
@@ -99,6 +116,9 @@ namespace Artemis.Core.Models.Profile
public List GetAllLayers()
{
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().Name);
+
var layers = new List();
// Add all layers in this element
@@ -118,6 +138,9 @@ namespace Artemis.Core.Models.Profile
/// The order where to place the child (1-based), defaults to the end of the collection
public virtual void AddChild(ProfileElement child, int? order = null)
{
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().Name);
+
lock (ChildrenList)
{
// Add to the end of the list
@@ -156,6 +179,9 @@ namespace Artemis.Core.Models.Profile
/// The profile element to remove
public virtual void RemoveChild(ProfileElement child)
{
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().Name);
+
lock (ChildrenList)
{
ChildrenList.Remove(child);
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index 77d2b356d..8408a6b31 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -203,8 +203,6 @@ namespace Artemis.Core.Models.Profile
// If we are at the end of the main timeline, wrap around back to the beginning
if (DisplayContinuously && TimelinePosition >= mainSegmentEnd)
TimelinePosition = StartSegmentLength + (mainSegmentEnd - TimelinePosition);
- else if (TimelinePosition >= TimelineLength)
- TimelinePosition = TimelineLength;
}
else
{
diff --git a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
index 76dd755fb..55965ca63 100644
--- a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
+++ b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
+using System.Threading.Tasks;
using Artemis.Core.Exceptions;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Surface;
@@ -94,6 +95,11 @@ namespace Artemis.Core.Plugins.Abstract
///
public abstract class ProfileModule : Module
{
+ protected ProfileModule()
+ {
+ OpacityOverride = 1;
+ }
+
protected readonly List HiddenPropertiesList = new List();
///
@@ -113,6 +119,10 @@ namespace Artemis.Core.Plugins.Abstract
{
lock (this)
{
+ OpacityOverride = AnimatingProfileChange
+ ? Math.Max(0, OpacityOverride - 0.1)
+ : Math.Min(1, OpacityOverride + 0.1);
+
// Update the profile
if (!IsProfileUpdatingDisabled)
ActiveProfile?.Update(deltaTime);
@@ -129,16 +139,37 @@ namespace Artemis.Core.Plugins.Abstract
}
}
+ internal async Task ChangeActiveProfileAnimated(Profile profile, ArtemisSurface surface)
+ {
+ if (profile != null && profile.Module != this)
+ throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}.");
+
+
+ if (profile == ActiveProfile || AnimatingProfileChange)
+ return;
+
+ AnimatingProfileChange = true;
+
+ while (OpacityOverride > 0)
+ await Task.Delay(50);
+
+ ChangeActiveProfile(profile, surface);
+ AnimatingProfileChange = false;
+
+ while (OpacityOverride < 1)
+ await Task.Delay(50);
+ }
+
internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface)
{
- if (profile != null && profile.PluginInfo != PluginInfo)
- throw new ArtemisCoreException($"Cannot activate a profile of plugin {profile.PluginInfo} on a module of plugin {PluginInfo}.");
+ if (profile != null && profile.Module != this)
+ throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}.");
lock (this)
{
if (profile == ActiveProfile)
return;
- ActiveProfile?.Deactivate();
+ ActiveProfile?.Dispose();
ActiveProfile = profile;
ActiveProfile?.Activate(surface);
@@ -147,6 +178,16 @@ namespace Artemis.Core.Plugins.Abstract
OnActiveProfileChanged();
}
+ ///
+ /// Overrides the opacity of the root folder
+ ///
+ public double OpacityOverride { get; set; }
+
+ ///
+ /// Indicates whether or not a profile change is being animated
+ ///
+ public bool AnimatingProfileChange { get; private set; }
+
#region Events
public event EventHandler ActiveProfileChanged;
diff --git a/src/Artemis.Core/Plugins/LayerBrush/Abstract/BaseLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/Abstract/BaseLayerBrush.cs
index fbfa4ba5a..38bbd2c13 100644
--- a/src/Artemis.Core/Plugins/LayerBrush/Abstract/BaseLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrush/Abstract/BaseLayerBrush.cs
@@ -80,8 +80,9 @@ namespace Artemis.Core.Plugins.LayerBrush.Abstract
public void Dispose()
{
DisableLayerBrush();
+ BaseProperties.Dispose();
+
Dispose(true);
-
GC.SuppressFinalize(this);
}
diff --git a/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs
index 150a08c3d..758ba6d69 100644
--- a/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs
@@ -52,6 +52,8 @@ namespace Artemis.Core.Plugins.LayerBrush.Abstract
{
var artemisLed = Layer.Leds[index];
var renderPoint = points[index * 2 + 1];
+ if (!float.IsFinite(renderPoint.X) || !float.IsFinite(renderPoint.Y))
+ continue;
// Let the brush determine the color
ledPaint.Color = GetColor(artemisLed, renderPoint);
diff --git a/src/Artemis.Core/Plugins/LayerEffect/Abstract/BaseLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffect/Abstract/BaseLayerEffect.cs
index 212eb9368..6913defa4 100644
--- a/src/Artemis.Core/Plugins/LayerEffect/Abstract/BaseLayerEffect.cs
+++ b/src/Artemis.Core/Plugins/LayerEffect/Abstract/BaseLayerEffect.cs
@@ -106,6 +106,7 @@ namespace Artemis.Core.Plugins.LayerEffect.Abstract
public void Dispose()
{
DisableLayerEffect();
+ BaseProperties.Dispose();
}
///
diff --git a/src/Artemis.Core/Resources/intro-profile.json b/src/Artemis.Core/Resources/intro-profile.json
new file mode 100644
index 000000000..895a03515
--- /dev/null
+++ b/src/Artemis.Core/Resources/intro-profile.json
@@ -0,0 +1,1045 @@
+{
+ "$type": "Artemis.Storage.Entities.Profile.ProfileEntity, Artemis.Storage",
+ "Id": "eb4f487b-475b-408f-a84f-733412d41b44",
+ "PluginGuid": "0de2991a-d7b8-4f61-ae4e-6623849215b5",
+ "Name": "Intro animation",
+ "IsActive": true,
+ "Folders": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.FolderEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.FolderEntity, Artemis.Storage",
+ "Id": "cc21b67c-3485-4dc6-b2af-105fda42a915",
+ "ParentId": "eb4f487b-475b-408f-a84f-733412d41b44",
+ "Order": 1,
+ "Name": "Root folder",
+ "Enabled": true,
+ "Profile": null,
+ "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44",
+ "StartSegmentLength": "00:00:00",
+ "MainSegmentLength": "00:00:05",
+ "EndSegmentLength": "00:00:00",
+ "DisplayContinuously": true,
+ "AlwaysFinishTimeline": false,
+ "LayerEffects": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "PropertyEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "ExpandedPropertyGroups": {
+ "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
+ "$values": []
+ },
+ "RootDisplayCondition": {
+ "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage",
+ "BooleanOperator": 0,
+ "Children": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ }
+ ]
+ },
+ "Layers": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage",
+ "Id": "2464a52c-4cec-4f17-87ed-edfe4bfed6f6",
+ "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915",
+ "Order": 1,
+ "Name": "Teal layer",
+ "Enabled": true,
+ "Leds": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "Profile": null,
+ "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44",
+ "StartSegmentLength": "00:00:00",
+ "MainSegmentLength": "00:00:05",
+ "EndSegmentLength": "00:00:00",
+ "DisplayContinuously": false,
+ "AlwaysFinishTimeline": false,
+ "LayerEffects": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "PropertyEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ShapeType",
+ "Value": "1",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ResizeMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BlendMode",
+ "Value": "3",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BrushReference",
+ "Value": "{\"BrushPluginGuid\":\"61cbbf01-8d69-4ede-a972-f3f269da66d9\",\"BrushType\":\"NoiseBrush\"}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.AnchorPoint",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Position",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Scale",
+ "Value": "{\"IsEmpty\":false,\"Width\":500.0,\"Height\":500.0}",
+ "KeyframesEnabled": true,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:02.5000000",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":false,\"Width\":300.0,\"Height\":0.0}",
+ "EasingFunction": 3
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:03.7500000",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":false,\"Width\":300.0,\"Height\":400.0}",
+ "EasingFunction": 0
+ }
+ ]
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Rotation",
+ "Value": "-45.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Opacity",
+ "Value": "100.0",
+ "KeyframesEnabled": true,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:04",
+ "Timeline": 0,
+ "Value": "100.0",
+ "EasingFunction": 0
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:04.4000000",
+ "Timeline": 0,
+ "Value": "0.0",
+ "EasingFunction": 0
+ }
+ ]
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.ColorType",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.MainColor",
+ "Value": "\"#ff009688\"",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.SecondaryColor",
+ "Value": "\"#ff00ffe7\"",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.GradientColor",
+ "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}],\"Rotation\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.Scale",
+ "Value": "{\"IsEmpty\":false,\"Width\":100.0,\"Height\":100.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.Hardness",
+ "Value": "300.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.ScrollSpeed",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
+ "Path": "LayerBrush.AnimationSpeed",
+ "Value": "25.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ ]
+ },
+ "ExpandedPropertyGroups": {
+ "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
+ "$values": [
+ "Transform",
+ "General"
+ ]
+ },
+ "RootDisplayCondition": {
+ "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage",
+ "BooleanOperator": 0,
+ "Children": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage",
+ "Id": "8d5fa358-88ed-4501-8cb2-fd9c31a34274",
+ "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915",
+ "Order": 2,
+ "Name": "RGB layer",
+ "Enabled": true,
+ "Leds": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "Profile": null,
+ "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44",
+ "StartSegmentLength": "00:00:00",
+ "MainSegmentLength": "00:00:03.7500000",
+ "EndSegmentLength": "00:00:00",
+ "DisplayContinuously": false,
+ "AlwaysFinishTimeline": false,
+ "LayerEffects": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "PropertyEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ShapeType",
+ "Value": "1",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ResizeMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BlendMode",
+ "Value": "3",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BrushReference",
+ "Value": "{\"BrushPluginGuid\":\"92a9d6ba-6f7a-4937-94d5-c1d715b4141a\",\"BrushType\":\"ColorBrush\"}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.AnchorPoint",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Position",
+ "Value": "{\"IsEmpty\":false,\"Length\":0.5,\"LengthSquared\":0.25,\"X\":-0.5,\"Y\":-0.0}",
+ "KeyframesEnabled": true,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:01.2500000",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":false,\"Length\":0.25,\"LengthSquared\":0.0625,\"X\":-0.25,\"Y\":0.0}",
+ "EasingFunction": 0
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:03.7500000",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":false,\"Length\":0.25,\"LengthSquared\":0.0625,\"X\":0.25,\"Y\":0.0}",
+ "EasingFunction": 0
+ }
+ ]
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Scale",
+ "Value": "{\"IsEmpty\":false,\"Width\":200.0,\"Height\":0.0}",
+ "KeyframesEnabled": true,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:01.2500000",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":false,\"Width\":150.0,\"Height\":0.0}",
+ "EasingFunction": 5
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:02.2700000",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":false,\"Width\":150.0,\"Height\":100.0}",
+ "EasingFunction": 0
+ }
+ ]
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Rotation",
+ "Value": "0.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Opacity",
+ "Value": "100.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.GradientType",
+ "Value": "1",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.TileMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.Color",
+ "Value": "\"#ffff0000\"",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "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}],\"Rotation\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.ColorsMultiplier",
+ "Value": "2",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.LinearGradientRotation",
+ "Value": "0.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.RadialGradient.CenterOffset",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.RadialGradient.ResizeMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ ]
+ },
+ "ExpandedPropertyGroups": {
+ "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
+ "$values": [
+ "Transform",
+ "LayerBrush"
+ ]
+ },
+ "RootDisplayCondition": {
+ "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage",
+ "BooleanOperator": 0,
+ "Children": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage",
+ "Id": "72a7e56c-0649-4449-8dca-eb937161f228",
+ "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915",
+ "Order": 3,
+ "Name": "Exploison layer",
+ "Enabled": true,
+ "Leds": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "Profile": null,
+ "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44",
+ "StartSegmentLength": "00:00:00",
+ "MainSegmentLength": "00:00:02.2500000",
+ "EndSegmentLength": "00:00:00",
+ "DisplayContinuously": false,
+ "AlwaysFinishTimeline": false,
+ "LayerEffects": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "PropertyEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ShapeType",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ResizeMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BlendMode",
+ "Value": "3",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BrushReference",
+ "Value": "{\"BrushPluginGuid\":\"92a9d6ba-6f7a-4937-94d5-c1d715b4141a\",\"BrushType\":\"ColorBrush\"}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.AnchorPoint",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Position",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Scale",
+ "Value": "{\"IsEmpty\":false,\"Width\":110.03,\"Height\":340.37}",
+ "KeyframesEnabled": true,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:00",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":true,\"Width\":0.0,\"Height\":0.0}",
+ "EasingFunction": 6
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage",
+ "Position": "00:00:02",
+ "Timeline": 0,
+ "Value": "{\"IsEmpty\":false,\"Width\":200.0,\"Height\":200.0}",
+ "EasingFunction": 0
+ }
+ ]
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Rotation",
+ "Value": "0.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Opacity",
+ "Value": "100.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.GradientType",
+ "Value": "2",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.TileMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.Color",
+ "Value": "\"#ffff0000\"",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.Colors",
+ "Value": "{\"Stops\":[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.492},{\"Color\":\"#ffedff00\",\"Position\":0.905},{\"Color\":\"#00ff0000\",\"Position\":1.0}],\"Rotation\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.ColorsMultiplier",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.LinearGradientRotation",
+ "Value": "0.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.RadialGradient.CenterOffset",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.RadialGradient.ResizeMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ ]
+ },
+ "ExpandedPropertyGroups": {
+ "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
+ "$values": [
+ "LayerBrush"
+ ]
+ },
+ "RootDisplayCondition": {
+ "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage",
+ "BooleanOperator": 0,
+ "Children": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage",
+ "Id": "f046f56f-a236-4ed6-bbd9-b5a4731878cf",
+ "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915",
+ "Order": 4,
+ "Name": "Background",
+ "Enabled": true,
+ "Leds": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "Profile": null,
+ "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44",
+ "StartSegmentLength": "00:00:00",
+ "MainSegmentLength": "00:00:03.7500000",
+ "EndSegmentLength": "00:00:00",
+ "DisplayContinuously": false,
+ "AlwaysFinishTimeline": false,
+ "LayerEffects": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "PropertyEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": [
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ShapeType",
+ "Value": "1",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.ResizeMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BlendMode",
+ "Value": "3",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.BrushReference",
+ "Value": "{\"BrushPluginGuid\":\"92a9d6ba-6f7a-4937-94d5-c1d715b4141a\",\"BrushType\":\"ColorBrush\"}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.AnchorPoint",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Position",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Scale",
+ "Value": "{\"IsEmpty\":false,\"Width\":100.0,\"Height\":100.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Rotation",
+ "Value": "0.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "Transform.Opacity",
+ "Value": "100.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.GradientType",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.TileMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.Color",
+ "Value": "\"#ff000000\"",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "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}],\"Rotation\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.ColorsMultiplier",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.LinearGradientRotation",
+ "Value": "0.0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.RadialGradient.CenterOffset",
+ "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
+ "Path": "LayerBrush.RadialGradient.ResizeMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ ]
+ },
+ "ExpandedPropertyGroups": {
+ "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
+ "$values": [
+ "LayerBrush"
+ ]
+ },
+ "RootDisplayCondition": {
+ "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage",
+ "BooleanOperator": 0,
+ "Children": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index 3f7d154f1..ec0654995 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -3,14 +3,17 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
+using System.Threading.Tasks;
using Artemis.Core.Events;
using Artemis.Core.Exceptions;
using Artemis.Core.JsonConverters;
+using Artemis.Core.Models.Profile;
using Artemis.Core.Ninject;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage.Interfaces;
+using Artemis.Core.Utilities;
using Artemis.Storage;
using Newtonsoft.Json;
using RGB.NET.Core;
@@ -35,6 +38,7 @@ namespace Artemis.Core.Services
private readonly ISurfaceService _surfaceService;
private List _dataModelExpansions;
private List _modules;
+ private IntroAnimation _introAnimation;
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else
internal CoreService(ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService,
@@ -90,11 +94,38 @@ namespace Artemis.Core.Services
else
_logger.Information("Initialized without an active surface entity");
- _profileService.ActivateDefaultProfiles();
+ PlayIntroAnimation();
+ _profileService.ActivateLastActiveProfiles();
OnInitialized();
}
+ private void PlayIntroAnimation()
+ {
+ FrameRendering += DrawOverlay;
+ _introAnimation = new IntroAnimation(_logger, _profileService, _surfaceService);
+
+ // Draw a white overlay over the device
+ void DrawOverlay(object sender, FrameRenderingEventArgs args)
+ {
+ if (_introAnimation == null)
+ args.Canvas.Clear(new SKColor(0, 0, 0));
+ else
+ _introAnimation.Render(args.DeltaTime, args.Canvas, _rgbService.BitmapBrush.Bitmap.Info);
+ }
+
+ var introLength = _introAnimation.AnimationProfile.GetAllLayers().Max(l => l.TimelineLength);
+
+ // Stop rendering after the profile finishes (take 1 second extra in case of slow updates)
+ Task.Run(async () =>
+ {
+ await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1)));
+ FrameRendering -= DrawOverlay;
+
+ _introAnimation.AnimationProfile?.Dispose();
+ _introAnimation = null;
+ });
+ }
public void SetMainWindowHandle(IntPtr handle)
{
diff --git a/src/Artemis.Core/Services/Interfaces/ICoreService.cs b/src/Artemis.Core/Services/Interfaces/ICoreService.cs
index 34a3969ee..f4e6b7750 100644
--- a/src/Artemis.Core/Services/Interfaces/ICoreService.cs
+++ b/src/Artemis.Core/Services/Interfaces/ICoreService.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
using Artemis.Core.Events;
namespace Artemis.Core.Services.Interfaces
diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs
index 9fe885d6b..31bd8efcb 100644
--- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs
@@ -1,38 +1,96 @@
using System.Collections.Generic;
+using System.Threading.Tasks;
using Artemis.Core.Models.Profile;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Services.Interfaces;
namespace Artemis.Core.Services.Storage.Interfaces
{
+ ///
+ /// Provides access to profile storage and is responsible for activating default profiles
+ ///
public interface IProfileService : IArtemisService
{
- void ActivateDefaultProfiles();
- Profile CreateProfile(ProfileModule module, string name);
- List GetProfiles(ProfileModule module);
- Profile GetActiveProfile(ProfileModule module);
+ ///
+ /// Activates the last profile for each module
+ ///
+ void ActivateLastActiveProfiles();
+
+ ///
+ /// Creates a new profile for the given module and returns a descriptor pointing to it
+ ///
+ /// The profile module to create the profile for
+ /// The name of the new profile
+ ///
+ ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name);
+
+ ///
+ /// Gets a descriptor for each profile stored for the given
+ ///
+ /// The module to return profile descriptors for
+ ///
+ List GetProfileDescriptors(ProfileModule module);
+
+ ///
+ /// Writes the profile to persistent storage
+ ///
+ ///
+ ///
void UpdateProfile(Profile profile, bool includeChildren);
+
+ ///
+ /// Disposes and permanently deletes the provided profile
+ ///
+ /// The profile to delete
void DeleteProfile(Profile profile);
///
- /// Activates the profile for the given with the currently active surface
+ /// Permanently deletes the profile described by the provided profile descriptor
///
- /// The module to activate the profile for
- /// The profile to activate
- void ActivateProfile(ProfileModule module, Profile profile);
+ /// The descriptor pointing to the profile to delete
+ void DeleteProfile(ProfileDescriptor profileDescriptor);
+
+ ///
+ /// Activates the profile described in the given with the currently active surface
+ ///
+ /// The descriptor describing the profile to activate
+ Profile ActivateProfile(ProfileDescriptor profileDescriptor);
+
+ ///
+ /// Asynchronously activates the profile described in the given with the currently
+ /// active surface using a fade animation
+ ///
+ /// The descriptor describing the profile to activate
+ Task ActivateProfileAnimated(ProfileDescriptor profileDescriptor);
+
+ ///
+ /// Clears the active profile on the given
+ ///
+ /// The profile module to deactivate the active profile on
+ void ClearActiveProfile(ProfileModule module);
+
+ ///
+ /// Asynchronously clears the active profile on the given using a fade animation
+ ///
+ /// The profile module to deactivate the active profile on
+ Task ClearActiveProfileAnimated(ProfileModule module);
///
/// Attempts to restore the profile to the state it had before the last call.
///
- ///
- ///
- bool UndoUpdateProfile(Profile selectedProfile, ProfileModule module);
+ ///
+ bool UndoUpdateProfile(Profile profile);
///
/// Attempts to restore the profile to the state it had before the last call.
///
- ///
- ///
- bool RedoUpdateProfile(Profile selectedProfile, ProfileModule module);
+ ///
+ bool RedoUpdateProfile(Profile profile);
+
+ ///
+ /// Prepares the profile for rendering. You should not need to call this, it is exposed for some niche usage in the core
+ ///
+ ///
+ void InstantiateProfile(Profile profile);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs
index d970852c7..9212eb87f 100644
--- a/src/Artemis.Core/Services/Storage/ProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/ProfileService.cs
@@ -1,5 +1,7 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using Artemis.Core.Events;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Surface;
@@ -14,9 +16,6 @@ using Serilog;
namespace Artemis.Core.Services.Storage
{
- ///
- /// Provides access to profile storage and is responsible for activating default profiles
- ///
public class ProfileService : IProfileService
{
private readonly ILogger _logger;
@@ -41,73 +40,82 @@ namespace Artemis.Core.Services.Storage
public JsonSerializerSettings MementoSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
- public void ActivateDefaultProfiles()
+ public void ActivateLastActiveProfiles()
{
foreach (var profileModule in _pluginService.GetPluginsOfType())
{
- var activeProfile = GetActiveProfile(profileModule);
- ActivateProfile(profileModule, activeProfile);
+ var activeProfile = GetLastActiveProfile(profileModule);
+ ActivateProfile(activeProfile);
}
}
- public List GetProfiles(ProfileModule module)
+ public List GetProfileDescriptors(ProfileModule module)
{
var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
- var profiles = new List();
- foreach (var profileEntity in profileEntities)
- {
- // If the profile entity matches the module's currently active profile, use that instead
- if (module.ActiveProfile != null && module.ActiveProfile.EntityId == profileEntity.Id)
- profiles.Add(module.ActiveProfile);
- else
- profiles.Add(new Profile(module.PluginInfo, profileEntity));
- }
-
- return profiles;
+ return profileEntities.Select(e => new ProfileDescriptor(module, e)).ToList();
}
- public Profile GetActiveProfile(ProfileModule module)
+ public ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name)
{
- if (module.ActiveProfile != null)
- return module.ActiveProfile;
-
- var moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
- var profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault();
- if (profileEntity == null)
- return null;
-
- return new Profile(module.PluginInfo, profileEntity);
+ var profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, PluginGuid = module.PluginInfo.Guid};
+ return new ProfileDescriptor(module, profileEntity);
}
- public Profile CreateProfile(ProfileModule module, string name)
+ public Profile ActivateProfile(ProfileDescriptor profileDescriptor)
{
- var profile = new Profile(module.PluginInfo, name);
- _profileRepository.Add(profile.ProfileEntity);
+ if (profileDescriptor.ProfileModule.ActiveProfile?.EntityId == profileDescriptor.Id)
+ return profileDescriptor.ProfileModule.ActiveProfile;
- if (_surfaceService.ActiveSurface != null)
- profile.PopulateLeds(_surfaceService.ActiveSurface);
+ var profileEntity = _profileRepository.Get(profileDescriptor.Id);
+ var profile = new Profile(profileDescriptor.ProfileModule, profileEntity);
+ InstantiateProfile(profile);
+ profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _surfaceService.ActiveSurface);
return profile;
}
-
- public void ActivateProfile(ProfileModule module, Profile profile)
+ public async Task ActivateProfileAnimated(ProfileDescriptor profileDescriptor)
{
- module.ChangeActiveProfile(profile, _surfaceService.ActiveSurface);
- if (profile != null)
- {
- InitializeLayerProperties(profile);
- InstantiateLayers(profile);
- InstantiateFolders(profile);
- }
+ if (profileDescriptor.ProfileModule.ActiveProfile?.EntityId == profileDescriptor.Id)
+ return profileDescriptor.ProfileModule.ActiveProfile;
+
+ var profileEntity = _profileRepository.Get(profileDescriptor.Id);
+ var profile = new Profile(profileDescriptor.ProfileModule, profileEntity);
+ InstantiateProfile(profile);
+
+ await profileDescriptor.ProfileModule.ChangeActiveProfileAnimated(profile, _surfaceService.ActiveSurface);
+ return profile;
+ }
+
+ public void ClearActiveProfile(ProfileModule module)
+ {
+ module.ChangeActiveProfile(null, _surfaceService.ActiveSurface);
+ }
+
+ public async Task ClearActiveProfileAnimated(ProfileModule module)
+ {
+ await module.ChangeActiveProfileAnimated(null, _surfaceService.ActiveSurface);
}
public void DeleteProfile(Profile profile)
{
_logger.Debug("Removing profile " + profile);
+
+ // If the given profile is currently active, disable it first (this also disposes it)
+ if (profile.Module.ActiveProfile == profile)
+ profile.Module.ChangeActiveProfile(null, _surfaceService.ActiveSurface);
+ else
+ profile.Dispose();
+
_profileRepository.Remove(profile.ProfileEntity);
}
+ public void DeleteProfile(ProfileDescriptor profileDescriptor)
+ {
+ var profileEntity = _profileRepository.Get(profileDescriptor.Id);
+ _profileRepository.Remove(profileEntity);
+ }
+
public void UpdateProfile(Profile profile, bool includeChildren)
{
_logger.Debug("Updating profile " + profile);
@@ -127,46 +135,73 @@ namespace Artemis.Core.Services.Storage
_profileRepository.Save(profile.ProfileEntity);
}
- public bool UndoUpdateProfile(Profile profile, ProfileModule module)
+ public bool UndoUpdateProfile(Profile profile)
{
- if (!profile.UndoStack.Any())
+ // Keep the profile from being rendered by locking it
+ lock (profile)
{
- _logger.Debug("Undo profile update - Failed, undo stack empty");
- return false;
- }
+ if (!profile.UndoStack.Any())
+ {
+ _logger.Debug("Undo profile update - Failed, undo stack empty");
+ return false;
+ }
- ActivateProfile(module, null);
- var top = profile.UndoStack.Pop();
- var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
- profile.RedoStack.Push(memento);
- profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings);
- profile.ApplyToProfile();
- ActivateProfile(module, profile);
+ var top = profile.UndoStack.Pop();
+ var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
+ profile.RedoStack.Push(memento);
+ profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings);
+
+ profile.ApplyToProfile();
+ InstantiateProfile(profile);
+ }
_logger.Debug("Undo profile update - Success");
return true;
}
- public bool RedoUpdateProfile(Profile profile, ProfileModule module)
+ public bool RedoUpdateProfile(Profile profile)
{
- if (!profile.RedoStack.Any())
+ // Keep the profile from being rendered by locking it
+ lock (profile)
{
- _logger.Debug("Redo profile update - Failed, redo empty");
- return false;
+ if (!profile.RedoStack.Any())
+ {
+ _logger.Debug("Redo profile update - Failed, redo empty");
+ return false;
+ }
+
+ var top = profile.RedoStack.Pop();
+ var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
+ profile.UndoStack.Push(memento);
+ profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings);
+
+ profile.ApplyToProfile();
+ InstantiateProfile(profile);
+
+ _logger.Debug("Redo profile update - Success");
+ return true;
}
-
- ActivateProfile(module, null);
- var top = profile.RedoStack.Pop();
- var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
- profile.UndoStack.Push(memento);
- profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings);
- profile.ApplyToProfile();
- ActivateProfile(module, profile);
-
- _logger.Debug("Redo profile update - Success");
- return true;
}
+ public ProfileDescriptor GetLastActiveProfile(ProfileModule module)
+ {
+ var moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
+ var profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault();
+ return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity);
+ }
+
+ public void InstantiateProfile(Profile profile)
+ {
+ profile.PopulateLeds(_surfaceService.ActiveSurface);
+ InitializeLayerProperties(profile);
+ InstantiateLayers(profile);
+ InstantiateFolders(profile);
+ }
+
+ ///
+ /// Initializes the properties on the layers of the given profile
+ ///
+ ///
private void InitializeLayerProperties(Profile profile)
{
foreach (var layer in profile.GetAllLayers())
@@ -178,6 +213,9 @@ namespace Artemis.Core.Services.Storage
}
}
+ ///
+ /// Instantiates all plugin-related classes on the folders of the given profile
+ ///
private void InstantiateFolders(Profile profile)
{
foreach (var folder in profile.GetAllFolders())
@@ -193,6 +231,9 @@ namespace Artemis.Core.Services.Storage
}
}
+ ///
+ /// Instantiates all plugin-related classes on the layers of the given profile
+ ///
private void InstantiateLayers(Profile profile)
{
foreach (var layer in profile.GetAllLayers())
@@ -215,6 +256,10 @@ namespace Artemis.Core.Services.Storage
}
}
+ ///
+ /// Populates all missing LEDs on all currently active profiles
+ ///
+ ///
private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
{
var profileModules = _pluginService.GetPluginsOfType();
@@ -222,6 +267,10 @@ namespace Artemis.Core.Services.Storage
profileModule.ActiveProfile.PopulateLeds(surface);
}
+
+ ///
+ /// Instantiates all missing plugin-related classes on the profile trees of all currently active profiles
+ ///
private void ActiveProfilesInstantiatePlugins()
{
var profileModules = _pluginService.GetPluginsOfType();
@@ -253,8 +302,8 @@ namespace Artemis.Core.Services.Storage
ActiveProfilesInstantiatePlugins();
else if (e.PluginInfo.Instance is ProfileModule profileModule)
{
- var activeProfile = GetActiveProfile(profileModule);
- ActivateProfile(profileModule, activeProfile);
+ var activeProfile = GetLastActiveProfile(profileModule);
+ ActivateProfile(activeProfile);
}
}
diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs
new file mode 100644
index 000000000..d44860799
--- /dev/null
+++ b/src/Artemis.Core/Utilities/IntroAnimation.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Artemis.Core.Extensions;
+using Artemis.Core.Models.Profile;
+using Artemis.Core.Models.Surface;
+using Artemis.Core.Plugins.Abstract;
+using Artemis.Core.Plugins.Abstract.ViewModels;
+using Artemis.Core.Plugins.LayerBrush;
+using Artemis.Core.Services.Interfaces;
+using Artemis.Core.Services.Storage.Interfaces;
+using Artemis.Storage.Entities.Profile;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Serilog;
+using SkiaSharp;
+
+namespace Artemis.Core.Utilities
+{
+ internal class IntroAnimation
+ {
+ private readonly ILogger _logger;
+ private readonly IProfileService _profileService;
+ private readonly ISurfaceService _surfaceService;
+
+ public IntroAnimation(ILogger logger, IProfileService profileService, ISurfaceService surfaceService)
+ {
+ _logger = logger;
+ _profileService = profileService;
+ _surfaceService = surfaceService;
+ CreateIntroProfile();
+ }
+
+ private void CreateIntroProfile()
+ {
+ try
+ {
+ // Load the intro profile from JSON into a ProfileEntity
+ var json = File.ReadAllText(Path.Combine(Constants.ApplicationFolder, "Resources", "intro-profile.json"));
+ var profileEntity = JsonConvert.DeserializeObject(json);
+ // Inject every LED on the surface into each layer
+ foreach (var profileEntityLayer in profileEntity.Layers)
+ {
+ profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity()
+ {
+ DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(),
+ LedName = l.RgbLed.Id.ToString(),
+ }));
+ }
+
+ var profile = new Profile(new DummyModule(), profileEntity);
+ profile.Activate(_surfaceService.ActiveSurface);
+
+ _profileService.InstantiateProfile(profile);
+ AnimationProfile = profile;
+ }
+ catch (Exception e)
+ {
+ _logger.Warning(e, "Failed to load intro profile");
+ }
+ }
+
+ public Profile AnimationProfile { get; set; }
+
+ public void Render(double deltaTime, SKCanvas canvas, SKImageInfo bitmapInfo)
+ {
+ if (AnimationProfile == null)
+ return;
+
+ AnimationProfile.Update(deltaTime);
+ AnimationProfile.Render(deltaTime, canvas, bitmapInfo);
+ }
+ }
+
+ internal class DummyModule : ProfileModule
+ {
+ public override void EnablePlugin()
+ {
+ }
+
+ public override void DisablePlugin()
+ {
+ }
+
+ public override IEnumerable GetViewModels()
+ {
+ return new List();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
index 67f315c49..4c2fa7b65 100644
--- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
@@ -24,9 +24,9 @@ namespace Artemis.UI.Shared.Services.Interfaces
void ChangeSelectedProfileElement(RenderProfileElement profileElement);
void UpdateSelectedProfileElement();
void UpdateProfilePreview();
- bool UndoUpdateProfile(ProfileModule module);
- bool RedoUpdateProfile(ProfileModule module);
- Module GetCurrentModule();
+ bool UndoUpdateProfile();
+ bool RedoUpdateProfile();
+ ProfileModule GetCurrentModule();
///
/// Occurs when a new profile is selected
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
index fb4c2bb02..ecbd0a28d 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
@@ -77,8 +77,9 @@ namespace Artemis.UI.Shared.Services
var profileElementEvent = new ProfileEventArgs(profile, SelectedProfile);
SelectedProfile = profile;
- UpdateProfilePreview();
+
OnSelectedProfileChanged(profileElementEvent);
+ UpdateProfilePreview();
}
}
@@ -88,8 +89,9 @@ namespace Artemis.UI.Shared.Services
{
_logger.Verbose("UpdateSelectedProfile {profile}", SelectedProfile);
_profileService.UpdateProfile(SelectedProfile, true);
- UpdateProfilePreview();
+
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile));
+ UpdateProfilePreview();
}
}
@@ -132,13 +134,19 @@ namespace Artemis.UI.Shared.Services
OnProfilePreviewUpdated();
}
- public bool UndoUpdateProfile(ProfileModule module)
+ public bool UndoUpdateProfile()
{
- var undid = _profileService.UndoUpdateProfile(SelectedProfile, module);
+ var undid = _profileService.UndoUpdateProfile(SelectedProfile);
if (!undid)
return false;
+ if (SelectedProfileElement is Folder folder)
+ SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId);
+ else if (SelectedProfileElement is Layer layer)
+ SelectedProfileElement = SelectedProfile.GetAllLayers().FirstOrDefault(l => l.EntityId == layer.EntityId);
+
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
+ OnSelectedProfileElementChanged(new RenderProfileElementEventArgs(SelectedProfileElement));
if (SelectedProfileElement != null)
{
@@ -152,9 +160,9 @@ namespace Artemis.UI.Shared.Services
return true;
}
- public bool RedoUpdateProfile(ProfileModule module)
+ public bool RedoUpdateProfile()
{
- var redid = _profileService.RedoUpdateProfile(SelectedProfile, module);
+ var redid = _profileService.RedoUpdateProfile(SelectedProfile);
if (!redid)
return false;
@@ -248,9 +256,9 @@ namespace Artemis.UI.Shared.Services
return time;
}
- public Module GetCurrentModule()
+ public ProfileModule GetCurrentModule()
{
- return (Module) SelectedProfile?.PluginInfo.Instance;
+ return SelectedProfile?.Module;
}
public event EventHandler ProfileSelected;
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml
index 69087f4ce..b8b30d465 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml
@@ -79,7 +79,7 @@
@@ -96,7 +96,7 @@
@@ -135,7 +135,7 @@
@@ -152,7 +152,7 @@
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs
index 8115186b5..b1195b782 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs
@@ -14,6 +14,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private RenderProfileElement _renderProfileElement;
private DisplayConditionGroupViewModel _rootGroup;
private int _transitionerIndex;
+ private bool _displayContinuously;
+ private bool _alwaysFinishTimeline;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
{
@@ -39,15 +41,22 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _renderProfileElement, value);
}
- public int ConditionBehaviourIndex
+ public bool DisplayContinuously
{
- get => RenderProfileElement != null && RenderProfileElement.AlwaysFinishTimeline ? 0 : 1;
+ get => _displayContinuously;
set
{
- if (RenderProfileElement == null)
- return;
+ if (!SetAndNotify(ref _displayContinuously, value)) return;
+ _profileEditorService.UpdateSelectedProfileElement();
+ }
+ }
- RenderProfileElement.AlwaysFinishTimeline = value == 0;
+ public bool AlwaysFinishTimeline
+ {
+ get => _alwaysFinishTimeline;
+ set
+ {
+ if (!SetAndNotify(ref _alwaysFinishTimeline, value)) return;
_profileEditorService.UpdateSelectedProfileElement();
}
}
@@ -70,9 +79,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{
RenderProfileElement = e.RenderProfileElement;
- NotifyOfPropertyChange(nameof(ConditionBehaviourIndex));
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
+ _displayContinuously = RenderProfileElement?.DisplayContinuously ?? false;
+ NotifyOfPropertyChange(nameof(DisplayContinuously));
+ _alwaysFinishTimeline = RenderProfileElement?.AlwaysFinishTimeline ?? false;
+ NotifyOfPropertyChange(nameof(AlwaysFinishTimeline));
+
if (e.RenderProfileElement == null)
{
RootGroup?.Dispose();
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
index 5f3e2e1f0..0a846301d 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
@@ -209,7 +209,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
public List GetAllLayerPropertyGroupViewModels()
{
var groups = LayerPropertyGroups.ToList();
- groups.AddRange(groups.SelectMany(g => g.Children).Where(g => g is LayerPropertyGroupViewModel).Cast());
+ var toAdd = groups.SelectMany(g => g.Children).Where(g => g is LayerPropertyGroupViewModel).Cast().ToList();
+ groups.AddRange(toAdd);
return groups;
}
@@ -284,7 +285,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
var hideRenderRelatedProperties = SelectedLayer?.LayerBrush != null && !SelectedLayer.LayerBrush.SupportsTransformation;
SelectedLayer.General.ShapeType.IsHidden = hideRenderRelatedProperties;
- SelectedLayer.General.FillType.IsHidden = hideRenderRelatedProperties;
+ SelectedLayer.General.ResizeMode.IsHidden = hideRenderRelatedProperties;
SelectedLayer.General.BlendMode.IsHidden = hideRenderRelatedProperties;
SelectedLayer.Transform.IsHidden = hideRenderRelatedProperties;
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs
index c3f85b376..506fb3b80 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs
@@ -9,6 +9,7 @@ using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree;
using Artemis.UI.Shared.Services.Interfaces;
+using Humanizer;
using Ninject;
using Ninject.Parameters;
@@ -42,6 +43,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
TreePropertyGroupViewModel = _layerPropertyVmFactory.TreePropertyGroupViewModel(this);
TimelinePropertyGroupViewModel = _layerPropertyVmFactory.TimelinePropertyGroupViewModel(this);
+ // Generate a fallback name if the description does not contain one
+ if (PropertyGroupDescription.Name == null)
+ {
+ var propertyInfo = LayerPropertyGroup.Parent?.GetType().GetProperties().FirstOrDefault(p => ReferenceEquals(p.GetValue(LayerPropertyGroup.Parent), LayerPropertyGroup));
+ if (propertyInfo != null)
+ PropertyGroupDescription.Name = propertyInfo.Name.Humanize();
+ else
+ PropertyGroupDescription.Name = "Unknown group";
+ }
+
LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
PopulateChildren();
DetermineType();
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml
index 55d53b651..6bd0b2e1f 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml
@@ -21,7 +21,8 @@
Text="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Name}"
ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}"
VerticalAlignment="Center"
- Margin="0 5">
+ HorizontalAlignment="Left"
+ Margin="3 5 0 5">