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

Profiles - Finished dispose implementation

Profiles - Added transition between active profiles
Core - Added startup animation
This commit is contained in:
Robert 2020-08-10 19:16:21 +02:00
parent c0bdd8cf26
commit d955bc8635
43 changed files with 1908 additions and 361 deletions

View File

@ -70,4 +70,9 @@
<HintPath>..\..\..\RGB.NET\bin\netstandard2.0\RGB.NET.Groups.dll</HintPath> <HintPath>..\..\..\RGB.NET\bin\netstandard2.0\RGB.NET.Groups.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="Resources\intro-profile.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.Plugins.LayerEffect.Abstract; using Artemis.Core.Plugins.LayerEffect.Abstract;
using Artemis.Core.Utilities;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract; using Artemis.Storage.Entities.Profile.Abstract;
using SkiaSharp; using SkiaSharp;
@ -61,9 +62,13 @@ namespace Artemis.Core.Models.Profile
internal FolderEntity FolderEntity { get; set; } internal FolderEntity FolderEntity { get; set; }
internal override RenderElementEntity RenderElementEntity => FolderEntity; internal override RenderElementEntity RenderElementEntity => FolderEntity;
public bool IsRootFolder => Parent == Profile;
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
if (_disposed)
throw new ObjectDisposedException("Folder");
if (!Enabled) if (!Enabled)
return; return;
@ -89,6 +94,9 @@ namespace Artemis.Core.Models.Profile
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment) public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
{ {
if (_disposed)
throw new ObjectDisposedException("Folder");
if (!Enabled) if (!Enabled)
return; return;
@ -125,6 +133,9 @@ namespace Artemis.Core.Models.Profile
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) 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)) if (Path == null || !Enabled || !Children.Any(c => c.Enabled))
return; return;
@ -172,6 +183,13 @@ namespace Artemis.Core.Models.Profile
profileElement.Render(deltaTime, folderCanvas, _folderBitmap.Info); profileElement.Render(deltaTime, folderCanvas, _folderBitmap.Info);
folderCanvas.Restore(); 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)) foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint); baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint);
@ -187,6 +205,9 @@ namespace Artemis.Core.Models.Profile
/// <returns></returns> /// <returns></returns>
public Folder AddFolder(string name) public Folder AddFolder(string name)
{ {
if (_disposed)
throw new ObjectDisposedException("Folder");
var folder = new Folder(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1}; var folder = new Folder(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1};
AddChild(folder); AddChild(folder);
return folder; return folder;
@ -195,6 +216,9 @@ namespace Artemis.Core.Models.Profile
/// <inheritdoc /> /// <inheritdoc />
public override void AddChild(ProfileElement child, int? order = null) public override void AddChild(ProfileElement child, int? order = null)
{ {
if (_disposed)
throw new ObjectDisposedException("Folder");
base.AddChild(child, order); base.AddChild(child, order);
CalculateRenderProperties(); CalculateRenderProperties();
} }
@ -202,6 +226,9 @@ namespace Artemis.Core.Models.Profile
/// <inheritdoc /> /// <inheritdoc />
public override void RemoveChild(ProfileElement child) public override void RemoveChild(ProfileElement child)
{ {
if (_disposed)
throw new ObjectDisposedException("Folder");
base.RemoveChild(child); base.RemoveChild(child);
CalculateRenderProperties(); CalculateRenderProperties();
} }
@ -213,6 +240,9 @@ namespace Artemis.Core.Models.Profile
public void CalculateRenderProperties() public void CalculateRenderProperties()
{ {
if (_disposed)
throw new ObjectDisposedException("Folder");
var path = new SKPath {FillType = SKPathFillType.Winding}; var path = new SKPath {FillType = SKPathFillType.Winding};
foreach (var child in Children) foreach (var child in Children)
{ {
@ -234,16 +264,27 @@ namespace Artemis.Core.Models.Profile
if (!disposing) if (!disposing)
return; return;
_folderBitmap?.Dispose();
foreach (var baseLayerEffect in LayerEffects) foreach (var baseLayerEffect in LayerEffects)
baseLayerEffect.Dispose(); baseLayerEffect.Dispose();
_layerEffects.Clear();
foreach (var profileElement in Children) foreach (var profileElement in Children)
profileElement.Dispose(); profileElement.Dispose();
ChildrenList.Clear();
_folderBitmap?.Dispose();
_folderBitmap = null;
Profile = null;
_disposed = true;
} }
internal override void ApplyToEntity() internal override void ApplyToEntity()
{ {
if (_disposed)
throw new ObjectDisposedException("Folder");
FolderEntity.Id = EntityId; FolderEntity.Id = EntityId;
FolderEntity.ParentId = Parent?.EntityId ?? new Guid(); FolderEntity.ParentId = Parent?.EntityId ?? new Guid();
@ -262,16 +303,6 @@ namespace Artemis.Core.Models.Profile
DisplayConditionGroup?.ApplyToEntity(); DisplayConditionGroup?.ApplyToEntity();
} }
internal void Deactivate()
{
_folderBitmap?.Dispose();
_folderBitmap = null;
var layerEffects = new List<BaseLayerEffect>(LayerEffects);
foreach (var baseLayerEffect in layerEffects)
DeactivateLayerEffect(baseLayerEffect);
}
#region Events #region Events
public event EventHandler RenderPropertiesUpdated; public event EventHandler RenderPropertiesUpdated;

View File

@ -126,6 +126,9 @@ namespace Artemis.Core.Models.Profile
/// <inheritdoc /> /// <inheritdoc />
public override List<BaseLayerPropertyKeyframe> GetAllKeyframes() public override List<BaseLayerPropertyKeyframe> GetAllKeyframes()
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
var keyframes = base.GetAllKeyframes(); var keyframes = base.GetAllKeyframes();
foreach (var baseLayerProperty in General.GetAllLayerProperties()) foreach (var baseLayerProperty in General.GetAllLayerProperties())
@ -143,22 +146,36 @@ namespace Artemis.Core.Models.Profile
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if (!disposing) if (!disposing)
return; return;
_general?.Dispose(); _disposed = true;
_layerBitmap?.Dispose();
// Brush first in case it depends on any of the other disposables during it's own disposal
_layerBrush?.Dispose(); _layerBrush?.Dispose();
_transform?.Dispose(); _layerBrush = null;
foreach (var baseLayerEffect in LayerEffects) foreach (var baseLayerEffect in LayerEffects)
baseLayerEffect.Dispose(); baseLayerEffect.Dispose();
_layerEffects.Clear();
_general?.Dispose();
_general = null;
_layerBitmap?.Dispose();
_layerBitmap = null;
_transform?.Dispose();
_transform = null;
Profile = null;
} }
#region Storage #region Storage
internal override void ApplyToEntity() internal override void ApplyToEntity()
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
// Properties // Properties
LayerEntity.Id = EntityId; LayerEntity.Id = EntityId;
LayerEntity.ParentId = Parent?.EntityId ?? new Guid(); LayerEntity.ParentId = Parent?.EntityId ?? new Guid();
@ -231,6 +248,9 @@ namespace Artemis.Core.Models.Profile
/// <inheritdoc /> /// <inheritdoc />
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized) if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
return; return;
@ -259,6 +279,9 @@ namespace Artemis.Core.Models.Profile
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment) public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized) if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
return; return;
@ -268,9 +291,7 @@ namespace Artemis.Core.Models.Profile
{ {
if (!DisplayContinuously) if (!DisplayContinuously)
{ {
var position = timeOverride + StartSegmentLength; TimelinePosition = StartSegmentLength + timeOverride;
if (position > StartSegmentLength + EndSegmentLength)
TimelinePosition = StartSegmentLength + EndSegmentLength;
} }
else else
{ {
@ -301,6 +322,9 @@ namespace Artemis.Core.Models.Profile
/// <inheritdoc /> /// <inheritdoc />
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
if (!Enabled || TimelinePosition > TimelineLength) if (!Enabled || TimelinePosition > TimelineLength)
return; return;
@ -424,6 +448,9 @@ namespace Artemis.Core.Models.Profile
internal void CalculateRenderProperties() internal void CalculateRenderProperties()
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
if (!Leds.Any()) if (!Leds.Any())
Path = new SKPath(); Path = new SKPath();
else else
@ -448,6 +475,9 @@ namespace Artemis.Core.Models.Profile
internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool zeroBased = false) internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool zeroBased = false)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
var positionProperty = Transform.Position.CurrentValue; var positionProperty = Transform.Position.CurrentValue;
// Start at the center of the shape // Start at the center of the shape
@ -469,6 +499,9 @@ namespace Artemis.Core.Models.Profile
/// <param name="path"></param> /// <param name="path"></param>
public void IncludePathInTranslation(SKPath path, bool zeroBased) public void IncludePathInTranslation(SKPath path, bool zeroBased)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
var sizeProperty = Transform.Scale.CurrentValue; var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue; var rotationProperty = Transform.Rotation.CurrentValue;
@ -498,6 +531,9 @@ namespace Artemis.Core.Models.Profile
/// </summary> /// </summary>
public void ExcludePathFromTranslation(SKPath path, bool zeroBased) public void ExcludePathFromTranslation(SKPath path, bool zeroBased)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
var sizeProperty = Transform.Scale.CurrentValue; var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue; var rotationProperty = Transform.Rotation.CurrentValue;
@ -531,6 +567,9 @@ namespace Artemis.Core.Models.Profile
/// <returns>The number of transformations applied</returns> /// <returns>The number of transformations applied</returns>
public int ExcludeCanvasFromTranslation(SKCanvas canvas, bool zeroBased) public int ExcludeCanvasFromTranslation(SKCanvas canvas, bool zeroBased)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
var sizeProperty = Transform.Scale.CurrentValue; var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue; var rotationProperty = Transform.Rotation.CurrentValue;
@ -568,6 +607,9 @@ namespace Artemis.Core.Models.Profile
/// <param name="led">The LED to add</param> /// <param name="led">The LED to add</param>
public void AddLed(ArtemisLed led) public void AddLed(ArtemisLed led)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
_leds.Add(led); _leds.Add(led);
CalculateRenderProperties(); CalculateRenderProperties();
} }
@ -578,6 +620,9 @@ namespace Artemis.Core.Models.Profile
/// <param name="leds">The LEDs to add</param> /// <param name="leds">The LEDs to add</param>
public void AddLeds(IEnumerable<ArtemisLed> leds) public void AddLeds(IEnumerable<ArtemisLed> leds)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
_leds.AddRange(leds); _leds.AddRange(leds);
CalculateRenderProperties(); CalculateRenderProperties();
} }
@ -588,6 +633,9 @@ namespace Artemis.Core.Models.Profile
/// <param name="led">The LED to remove</param> /// <param name="led">The LED to remove</param>
public void RemoveLed(ArtemisLed led) public void RemoveLed(ArtemisLed led)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
_leds.Remove(led); _leds.Remove(led);
CalculateRenderProperties(); CalculateRenderProperties();
} }
@ -597,12 +645,18 @@ namespace Artemis.Core.Models.Profile
/// </summary> /// </summary>
public void ClearLeds() public void ClearLeds()
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
_leds.Clear(); _leds.Clear();
CalculateRenderProperties(); CalculateRenderProperties();
} }
internal void PopulateLeds(ArtemisSurface surface) internal void PopulateLeds(ArtemisSurface surface)
{ {
if (_disposed)
throw new ObjectDisposedException("Layer");
var leds = new List<ArtemisLed>(); var leds = new List<ArtemisLed>();
// Get the surface LEDs for this layer // Get the surface LEDs for this layer
@ -623,17 +677,6 @@ namespace Artemis.Core.Models.Profile
#region Activation #region Activation
internal void Deactivate()
{
_layerBitmap?.Dispose();
_layerBitmap = null;
DeactivateLayerBrush();
var layerEffects = new List<BaseLayerEffect>(LayerEffects);
foreach (var baseLayerEffect in layerEffects)
DeactivateLayerEffect(baseLayerEffect);
}
internal void DeactivateLayerBrush() internal void DeactivateLayerBrush()
{ {
if (LayerBrush == null) if (LayerBrush == null)

View File

@ -28,5 +28,9 @@ namespace Artemis.Core.Models.Profile
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -110,6 +110,8 @@ namespace Artemis.Core.Models.Profile
public void Dispose() public void Dispose()
{ {
DisableProperties(); DisableProperties();
foreach (var layerPropertyGroup in _layerPropertyGroups)
layerPropertyGroup.Dispose();
} }
/// <summary> /// <summary>

View File

@ -30,5 +30,9 @@ namespace Artemis.Core.Models.Profile
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -57,6 +57,8 @@ namespace Artemis.Core.Models.Profile
{ {
lock (this) lock (this)
{ {
if (_disposed)
throw new ObjectDisposedException("Profile");
if (!IsActivated) if (!IsActivated)
throw new ArtemisCoreException($"Cannot update inactive profile: {this}"); throw new ArtemisCoreException($"Cannot update inactive profile: {this}");
@ -69,6 +71,8 @@ namespace Artemis.Core.Models.Profile
{ {
lock (this) lock (this)
{ {
if (_disposed)
throw new ObjectDisposedException("Profile");
if (!IsActivated) if (!IsActivated)
throw new ArtemisCoreException($"Cannot render inactive profile: {this}"); throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
@ -79,21 +83,26 @@ namespace Artemis.Core.Models.Profile
public Folder GetRootFolder() public Folder GetRootFolder()
{ {
if (_disposed)
throw new ObjectDisposedException("Profile");
return (Folder) Children.Single(); return (Folder) Children.Single();
} }
public void ApplyToProfile() public void ApplyToProfile()
{ {
if (_disposed)
throw new ObjectDisposedException("Profile");
Name = ProfileEntity.Name; Name = ProfileEntity.Name;
lock (ChildrenList) lock (ChildrenList)
{ {
foreach (var folder in GetAllFolders()) // Remove the old profile tree
folder.Deactivate(); foreach (var profileElement in Children)
foreach (var layer in GetAllLayers()) profileElement.Dispose();
layer.Deactivate();
ChildrenList.Clear(); ChildrenList.Clear();
// Populate the profile starting at the root, the rest is populated recursively // Populate the profile starting at the root, the rest is populated recursively
var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId); var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
if (rootFolder == null) if (rootFolder == null)
@ -113,13 +122,21 @@ namespace Artemis.Core.Models.Profile
if (!disposing) if (!disposing)
return; return;
Deactivate(); OnDeactivating();
foreach (var profileElement in Children) foreach (var profileElement in Children)
profileElement.Dispose(); profileElement.Dispose();
ChildrenList.Clear();
IsActivated = false;
_disposed = true;
} }
internal override void ApplyToEntity() internal override void ApplyToEntity()
{ {
if (_disposed)
throw new ObjectDisposedException("Profile");
ProfileEntity.Id = EntityId; ProfileEntity.Id = EntityId;
ProfileEntity.PluginGuid = Module.PluginInfo.Guid; ProfileEntity.PluginGuid = Module.PluginInfo.Guid;
ProfileEntity.Name = Name; ProfileEntity.Name = Name;
@ -139,6 +156,8 @@ namespace Artemis.Core.Models.Profile
{ {
lock (this) lock (this)
{ {
if (_disposed)
throw new ObjectDisposedException("Profile");
if (IsActivated) if (IsActivated)
return; return;
@ -148,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) internal void PopulateLeds(ArtemisSurface surface)
{ {
if (_disposed)
throw new ObjectDisposedException("Profile");
foreach (var layer in GetAllLayers()) foreach (var layer in GetAllLayers())
layer.PopulateLeds(surface); layer.PopulateLeds(surface);
} }
@ -174,7 +179,7 @@ namespace Artemis.Core.Models.Profile
#region Events #region Events
/// <summary> /// <summary>
/// Occurs when the profile is being activated. /// Occurs when the profile has been activated.
/// </summary> /// </summary>
public event EventHandler Activated; public event EventHandler Activated;
@ -188,7 +193,7 @@ namespace Artemis.Core.Models.Profile
Activated?.Invoke(this, EventArgs.Empty); Activated?.Invoke(this, EventArgs.Empty);
} }
private void OnDeactivated() private void OnDeactivating()
{ {
Deactivated?.Invoke(this, EventArgs.Empty); Deactivated?.Invoke(this, EventArgs.Empty);
} }

View File

@ -9,16 +9,16 @@ namespace Artemis.Core.Models.Profile
internal ProfileDescriptor(ProfileModule profileModule, ProfileEntity profileEntity) internal ProfileDescriptor(ProfileModule profileModule, ProfileEntity profileEntity)
{ {
ProfileModule = profileModule; ProfileModule = profileModule;
ProfileEntity = profileEntity;
Id = profileEntity.Id; Id = profileEntity.Id;
Name = profileEntity.Name; Name = profileEntity.Name;
IsLastActiveProfile = profileEntity.IsActive;
} }
public bool IsLastActiveProfile { get; set; }
public Guid Id { get; } public Guid Id { get; }
public ProfileModule ProfileModule { get; } public ProfileModule ProfileModule { get; }
public string Name { get; } public string Name { get; }
internal ProfileEntity ProfileEntity { get; }
} }
} }

View File

@ -22,6 +22,7 @@ namespace Artemis.Core.Models.Profile
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
protected bool _disposed;
private bool _enabled; private bool _enabled;
private Guid _entityId; private Guid _entityId;
private string _name; private string _name;
@ -98,6 +99,9 @@ namespace Artemis.Core.Models.Profile
public List<Folder> GetAllFolders() public List<Folder> GetAllFolders()
{ {
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
var folders = new List<Folder>(); var folders = new List<Folder>();
foreach (var childFolder in Children.Where(c => c is Folder).Cast<Folder>()) foreach (var childFolder in Children.Where(c => c is Folder).Cast<Folder>())
{ {
@ -112,6 +116,9 @@ namespace Artemis.Core.Models.Profile
public List<Layer> GetAllLayers() public List<Layer> GetAllLayers()
{ {
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
var layers = new List<Layer>(); var layers = new List<Layer>();
// Add all layers in this element // Add all layers in this element
@ -131,6 +138,9 @@ namespace Artemis.Core.Models.Profile
/// <param name="order">The order where to place the child (1-based), defaults to the end of the collection</param> /// <param name="order">The order where to place the child (1-based), defaults to the end of the collection</param>
public virtual void AddChild(ProfileElement child, int? order = null) public virtual void AddChild(ProfileElement child, int? order = null)
{ {
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
lock (ChildrenList) lock (ChildrenList)
{ {
// Add to the end of the list // Add to the end of the list
@ -169,6 +179,9 @@ namespace Artemis.Core.Models.Profile
/// <param name="child">The profile element to remove</param> /// <param name="child">The profile element to remove</param>
public virtual void RemoveChild(ProfileElement child) public virtual void RemoveChild(ProfileElement child)
{ {
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
lock (ChildrenList) lock (ChildrenList)
{ {
ChildrenList.Remove(child); ChildrenList.Remove(child);

View File

@ -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 we are at the end of the main timeline, wrap around back to the beginning
if (DisplayContinuously && TimelinePosition >= mainSegmentEnd) if (DisplayContinuously && TimelinePosition >= mainSegmentEnd)
TimelinePosition = StartSegmentLength + (mainSegmentEnd - TimelinePosition); TimelinePosition = StartSegmentLength + (mainSegmentEnd - TimelinePosition);
else if (TimelinePosition >= TimelineLength)
TimelinePosition = TimelineLength;
} }
else else
{ {

View File

@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using Artemis.Core.Exceptions; using Artemis.Core.Exceptions;
using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Surface; using Artemis.Core.Models.Surface;
@ -94,6 +95,11 @@ namespace Artemis.Core.Plugins.Abstract
/// </summary> /// </summary>
public abstract class ProfileModule : Module public abstract class ProfileModule : Module
{ {
protected ProfileModule()
{
OpacityOverride = 1;
}
protected readonly List<PropertyInfo> HiddenPropertiesList = new List<PropertyInfo>(); protected readonly List<PropertyInfo> HiddenPropertiesList = new List<PropertyInfo>();
/// <summary> /// <summary>
@ -113,6 +119,10 @@ namespace Artemis.Core.Plugins.Abstract
{ {
lock (this) lock (this)
{ {
OpacityOverride = AnimatingProfileChange
? Math.Max(0, OpacityOverride - 0.1)
: Math.Min(1, OpacityOverride + 0.1);
// Update the profile // Update the profile
if (!IsProfileUpdatingDisabled) if (!IsProfileUpdatingDisabled)
ActiveProfile?.Update(deltaTime); ActiveProfile?.Update(deltaTime);
@ -129,6 +139,27 @@ 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) internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface)
{ {
if (profile != null && profile.Module != this) if (profile != null && profile.Module != this)
@ -147,6 +178,16 @@ namespace Artemis.Core.Plugins.Abstract
OnActiveProfileChanged(); OnActiveProfileChanged();
} }
/// <summary>
/// Overrides the opacity of the root folder
/// </summary>
public double OpacityOverride { get; set; }
/// <summary>
/// Indicates whether or not a profile change is being animated
/// </summary>
public bool AnimatingProfileChange { get; private set; }
#region Events #region Events
public event EventHandler ActiveProfileChanged; public event EventHandler ActiveProfileChanged;

View File

@ -52,6 +52,8 @@ namespace Artemis.Core.Plugins.LayerBrush.Abstract
{ {
var artemisLed = Layer.Leds[index]; var artemisLed = Layer.Leds[index];
var renderPoint = points[index * 2 + 1]; var renderPoint = points[index * 2 + 1];
if (!float.IsFinite(renderPoint.X) || !float.IsFinite(renderPoint.Y))
continue;
// Let the brush determine the color // Let the brush determine the color
ledPaint.Color = GetColor(artemisLed, renderPoint); ledPaint.Color = GetColor(artemisLed, renderPoint);

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,17 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.Core.Exceptions; using Artemis.Core.Exceptions;
using Artemis.Core.JsonConverters; using Artemis.Core.JsonConverters;
using Artemis.Core.Models.Profile;
using Artemis.Core.Ninject; using Artemis.Core.Ninject;
using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Models; using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage.Interfaces; using Artemis.Core.Services.Storage.Interfaces;
using Artemis.Core.Utilities;
using Artemis.Storage; using Artemis.Storage;
using Newtonsoft.Json; using Newtonsoft.Json;
using RGB.NET.Core; using RGB.NET.Core;
@ -35,6 +38,7 @@ namespace Artemis.Core.Services
private readonly ISurfaceService _surfaceService; private readonly ISurfaceService _surfaceService;
private List<BaseDataModelExpansion> _dataModelExpansions; private List<BaseDataModelExpansion> _dataModelExpansions;
private List<Module> _modules; private List<Module> _modules;
private IntroAnimation _introAnimation;
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else // 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, internal CoreService(ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService,
@ -90,11 +94,38 @@ namespace Artemis.Core.Services
else else
_logger.Information("Initialized without an active surface entity"); _logger.Information("Initialized without an active surface entity");
PlayIntroAnimation();
_profileService.ActivateLastActiveProfiles(); _profileService.ActivateLastActiveProfiles();
OnInitialized(); 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) public void SetMainWindowHandle(IntPtr handle)
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Artemis.Core.Events; using Artemis.Core.Events;
namespace Artemis.Core.Services.Interfaces namespace Artemis.Core.Services.Interfaces

View File

@ -1,10 +1,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile;
using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Interfaces;
namespace Artemis.Core.Services.Storage.Interfaces namespace Artemis.Core.Services.Storage.Interfaces
{ {
/// <summary>
/// Provides access to profile storage and is responsible for activating default profiles
/// </summary>
public interface IProfileService : IArtemisService public interface IProfileService : IArtemisService
{ {
/// <summary> /// <summary>
@ -18,14 +22,14 @@ namespace Artemis.Core.Services.Storage.Interfaces
/// <param name="module">The profile module to create the profile for</param> /// <param name="module">The profile module to create the profile for</param>
/// <param name="name">The name of the new profile</param> /// <param name="name">The name of the new profile</param>
/// <returns></returns> /// <returns></returns>
ProfileDescriptor CreateProfile(ProfileModule module, string name); ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name);
/// <summary> /// <summary>
/// Gets a descriptor for each profile stored for the given <see cref="ProfileModule" /> /// Gets a descriptor for each profile stored for the given <see cref="ProfileModule" />
/// </summary> /// </summary>
/// <param name="module">The module to return profile descriptors for</param> /// <param name="module">The module to return profile descriptors for</param>
/// <returns></returns> /// <returns></returns>
List<ProfileDescriptor> GetProfiles(ProfileModule module); List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module);
/// <summary> /// <summary>
/// Writes the profile to persistent storage /// Writes the profile to persistent storage
@ -40,30 +44,53 @@ namespace Artemis.Core.Services.Storage.Interfaces
/// <param name="profile">The profile to delete</param> /// <param name="profile">The profile to delete</param>
void DeleteProfile(Profile profile); void DeleteProfile(Profile profile);
/// <summary>
/// Permanently deletes the profile described by the provided profile descriptor
/// </summary>
/// <param name="profileDescriptor">The descriptor pointing to the profile to delete</param>
void DeleteProfile(ProfileDescriptor profileDescriptor);
/// <summary> /// <summary>
/// Activates the profile described in the given <see cref="ProfileDescriptor" /> with the currently active surface /// Activates the profile described in the given <see cref="ProfileDescriptor" /> with the currently active surface
/// </summary> /// </summary>
/// <param name="profileDescriptor">The descriptor describing the profile to activate</param> /// <param name="profileDescriptor">The descriptor describing the profile to activate</param>
Profile ActivateProfile(ProfileDescriptor profileDescriptor); Profile ActivateProfile(ProfileDescriptor profileDescriptor);
/// <summary>
/// Asynchronously activates the profile described in the given <see cref="ProfileDescriptor" /> with the currently
/// active surface using a fade animation
/// </summary>
/// <param name="profileDescriptor">The descriptor describing the profile to activate</param>
Task<Profile> ActivateProfileAnimated(ProfileDescriptor profileDescriptor);
/// <summary> /// <summary>
/// Clears the active profile on the given <see cref="ProfileModule" /> /// Clears the active profile on the given <see cref="ProfileModule" />
/// </summary> /// </summary>
/// <param name="module">The profile module to deactivate the active profile on</param> /// <param name="module">The profile module to deactivate the active profile on</param>
void ClearActiveProfile(ProfileModule module); void ClearActiveProfile(ProfileModule module);
/// <summary>
/// Asynchronously clears the active profile on the given <see cref="ProfileModule" /> using a fade animation
/// </summary>
/// <param name="module">The profile module to deactivate the active profile on</param>
Task ClearActiveProfileAnimated(ProfileModule module);
/// <summary> /// <summary>
/// Attempts to restore the profile to the state it had before the last <see cref="UpdateProfile" /> call. /// Attempts to restore the profile to the state it had before the last <see cref="UpdateProfile" /> call.
/// </summary> /// </summary>
/// <param name="selectedProfile"></param> /// <param name="profile"></param>
/// <param name="module"></param> bool UndoUpdateProfile(Profile profile);
bool UndoUpdateProfile(Profile selectedProfile, ProfileModule module);
/// <summary> /// <summary>
/// Attempts to restore the profile to the state it had before the last <see cref="UndoUpdateProfile" /> call. /// Attempts to restore the profile to the state it had before the last <see cref="UndoUpdateProfile" /> call.
/// </summary> /// </summary>
/// <param name="selectedProfile"></param> /// <param name="profile"></param>
/// <param name="module"></param> bool RedoUpdateProfile(Profile profile);
bool RedoUpdateProfile(Profile selectedProfile, ProfileModule module);
/// <summary>
/// Prepares the profile for rendering. You should not need to call this, it is exposed for some niche usage in the core
/// </summary>
/// <param name="profile"></param>
void InstantiateProfile(Profile profile);
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Surface; using Artemis.Core.Models.Surface;
@ -15,9 +16,6 @@ using Serilog;
namespace Artemis.Core.Services.Storage namespace Artemis.Core.Services.Storage
{ {
/// <summary>
/// Provides access to profile storage and is responsible for activating default profiles
/// </summary>
public class ProfileService : IProfileService public class ProfileService : IProfileService
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
@ -51,20 +49,13 @@ namespace Artemis.Core.Services.Storage
} }
} }
public List<ProfileDescriptor> GetProfiles(ProfileModule module) public List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module)
{ {
var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid);
return profileEntities.Select(e => new ProfileDescriptor(module, e)).ToList(); return profileEntities.Select(e => new ProfileDescriptor(module, e)).ToList();
} }
public ProfileDescriptor GetLastActiveProfile(ProfileModule module) public ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name)
{
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 ProfileDescriptor CreateProfile(ProfileModule module, string name)
{ {
var profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, PluginGuid = module.PluginInfo.Guid}; var profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, PluginGuid = module.PluginInfo.Guid};
return new ProfileDescriptor(module, profileEntity); return new ProfileDescriptor(module, profileEntity);
@ -72,31 +63,59 @@ namespace Artemis.Core.Services.Storage
public Profile ActivateProfile(ProfileDescriptor profileDescriptor) public Profile ActivateProfile(ProfileDescriptor profileDescriptor)
{ {
if (profileDescriptor.ProfileModule.ActiveProfile.EntityId == profileDescriptor.Id) if (profileDescriptor.ProfileModule.ActiveProfile?.EntityId == profileDescriptor.Id)
return profileDescriptor.ProfileModule.ActiveProfile; return profileDescriptor.ProfileModule.ActiveProfile;
var profile = new Profile(profileDescriptor.ProfileModule, profileDescriptor.ProfileEntity); var profileEntity = _profileRepository.Get(profileDescriptor.Id);
InitializeLayerProperties(profile); var profile = new Profile(profileDescriptor.ProfileModule, profileEntity);
InstantiateLayers(profile); InstantiateProfile(profile);
InstantiateFolders(profile);
profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _surfaceService.ActiveSurface); profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _surfaceService.ActiveSurface);
return profile; return profile;
} }
public async Task<Profile> ActivateProfileAnimated(ProfileDescriptor profileDescriptor)
{
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) public void ClearActiveProfile(ProfileModule module)
{ {
module.ChangeActiveProfile(null, _surfaceService.ActiveSurface); module.ChangeActiveProfile(null, _surfaceService.ActiveSurface);
} }
public async Task ClearActiveProfileAnimated(ProfileModule module)
{
await module.ChangeActiveProfileAnimated(null, _surfaceService.ActiveSurface);
}
public void DeleteProfile(Profile profile) public void DeleteProfile(Profile profile)
{ {
_logger.Debug("Removing 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); _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) public void UpdateProfile(Profile profile, bool includeChildren)
{ {
_logger.Debug("Updating profile " + profile); _logger.Debug("Updating profile " + profile);
@ -116,46 +135,73 @@ namespace Artemis.Core.Services.Storage
_profileRepository.Save(profile.ProfileEntity); _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"); if (!profile.UndoStack.Any())
return false; {
} _logger.Debug("Undo profile update - Failed, undo stack empty");
return false;
}
ActivateProfile(module, null); var top = profile.UndoStack.Pop();
var top = profile.UndoStack.Pop(); var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); profile.RedoStack.Push(memento);
profile.RedoStack.Push(memento); profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings);
profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings);
profile.ApplyToProfile(); profile.ApplyToProfile();
ActivateProfile(module, profile); InstantiateProfile(profile);
}
_logger.Debug("Undo profile update - Success"); _logger.Debug("Undo profile update - Success");
return true; 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"); if (!profile.RedoStack.Any())
return false; {
_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<ProfileEntity>(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<ProfileEntity>(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);
}
/// <summary>
/// Initializes the properties on the layers of the given profile
/// </summary>
/// <param name="profile"></param>
private void InitializeLayerProperties(Profile profile) private void InitializeLayerProperties(Profile profile)
{ {
foreach (var layer in profile.GetAllLayers()) foreach (var layer in profile.GetAllLayers())
@ -167,6 +213,9 @@ namespace Artemis.Core.Services.Storage
} }
} }
/// <summary>
/// Instantiates all plugin-related classes on the folders of the given profile
/// </summary>
private void InstantiateFolders(Profile profile) private void InstantiateFolders(Profile profile)
{ {
foreach (var folder in profile.GetAllFolders()) foreach (var folder in profile.GetAllFolders())
@ -182,6 +231,9 @@ namespace Artemis.Core.Services.Storage
} }
} }
/// <summary>
/// Instantiates all plugin-related classes on the layers of the given profile
/// </summary>
private void InstantiateLayers(Profile profile) private void InstantiateLayers(Profile profile)
{ {
foreach (var layer in profile.GetAllLayers()) foreach (var layer in profile.GetAllLayers())
@ -204,6 +256,10 @@ namespace Artemis.Core.Services.Storage
} }
} }
/// <summary>
/// Populates all missing LEDs on all currently active profiles
/// </summary>
/// <param name="surface"></param>
private void ActiveProfilesPopulateLeds(ArtemisSurface surface) private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
{ {
var profileModules = _pluginService.GetPluginsOfType<ProfileModule>(); var profileModules = _pluginService.GetPluginsOfType<ProfileModule>();
@ -211,6 +267,10 @@ namespace Artemis.Core.Services.Storage
profileModule.ActiveProfile.PopulateLeds(surface); profileModule.ActiveProfile.PopulateLeds(surface);
} }
/// <summary>
/// Instantiates all missing plugin-related classes on the profile trees of all currently active profiles
/// </summary>
private void ActiveProfilesInstantiatePlugins() private void ActiveProfilesInstantiatePlugins()
{ {
var profileModules = _pluginService.GetPluginsOfType<ProfileModule>(); var profileModules = _pluginService.GetPluginsOfType<ProfileModule>();
@ -242,8 +302,8 @@ namespace Artemis.Core.Services.Storage
ActiveProfilesInstantiatePlugins(); ActiveProfilesInstantiatePlugins();
else if (e.PluginInfo.Instance is ProfileModule profileModule) else if (e.PluginInfo.Instance is ProfileModule profileModule)
{ {
var activeProfile = GetActiveProfile(profileModule); var activeProfile = GetLastActiveProfile(profileModule);
ActivateProfile(profileModule, activeProfile); ActivateProfile(activeProfile);
} }
} }

View File

@ -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<ProfileEntity>(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<ModuleViewModel> GetViewModels()
{
return new List<ModuleViewModel>();
}
}
}

View File

@ -24,9 +24,9 @@ namespace Artemis.UI.Shared.Services.Interfaces
void ChangeSelectedProfileElement(RenderProfileElement profileElement); void ChangeSelectedProfileElement(RenderProfileElement profileElement);
void UpdateSelectedProfileElement(); void UpdateSelectedProfileElement();
void UpdateProfilePreview(); void UpdateProfilePreview();
bool UndoUpdateProfile(ProfileModule module); bool UndoUpdateProfile();
bool RedoUpdateProfile(ProfileModule module); bool RedoUpdateProfile();
Module GetCurrentModule(); ProfileModule GetCurrentModule();
/// <summary> /// <summary>
/// Occurs when a new profile is selected /// Occurs when a new profile is selected

View File

@ -77,8 +77,9 @@ namespace Artemis.UI.Shared.Services
var profileElementEvent = new ProfileEventArgs(profile, SelectedProfile); var profileElementEvent = new ProfileEventArgs(profile, SelectedProfile);
SelectedProfile = profile; SelectedProfile = profile;
UpdateProfilePreview();
OnSelectedProfileChanged(profileElementEvent); OnSelectedProfileChanged(profileElementEvent);
UpdateProfilePreview();
} }
} }
@ -88,8 +89,9 @@ namespace Artemis.UI.Shared.Services
{ {
_logger.Verbose("UpdateSelectedProfile {profile}", SelectedProfile); _logger.Verbose("UpdateSelectedProfile {profile}", SelectedProfile);
_profileService.UpdateProfile(SelectedProfile, true); _profileService.UpdateProfile(SelectedProfile, true);
UpdateProfilePreview();
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile)); OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile));
UpdateProfilePreview();
} }
} }
@ -132,13 +134,19 @@ namespace Artemis.UI.Shared.Services
OnProfilePreviewUpdated(); OnProfilePreviewUpdated();
} }
public bool UndoUpdateProfile(ProfileModule module) public bool UndoUpdateProfile()
{ {
var undid = _profileService.UndoUpdateProfile(SelectedProfile, module); var undid = _profileService.UndoUpdateProfile(SelectedProfile);
if (!undid) if (!undid)
return false; 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)); OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
OnSelectedProfileElementChanged(new RenderProfileElementEventArgs(SelectedProfileElement));
if (SelectedProfileElement != null) if (SelectedProfileElement != null)
{ {
@ -152,9 +160,9 @@ namespace Artemis.UI.Shared.Services
return true; return true;
} }
public bool RedoUpdateProfile(ProfileModule module) public bool RedoUpdateProfile()
{ {
var redid = _profileService.RedoUpdateProfile(SelectedProfile, module); var redid = _profileService.RedoUpdateProfile(SelectedProfile);
if (!redid) if (!redid)
return false; return false;
@ -248,9 +256,9 @@ namespace Artemis.UI.Shared.Services
return time; return time;
} }
public Module GetCurrentModule() public ProfileModule GetCurrentModule()
{ {
return (Module) SelectedProfile?.PluginInfo.Instance; return SelectedProfile?.Module;
} }
public event EventHandler<ProfileEventArgs> ProfileSelected; public event EventHandler<ProfileEventArgs> ProfileSelected;

View File

@ -79,7 +79,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<RadioButton Grid.Column="0" <RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding RenderProfileElement.DisplayContinuously}" IsChecked="{Binding DisplayContinuously}"
MinWidth="0" MinWidth="0"
Padding="5 0"> Padding="5 0">
<RadioButton.ToolTip> <RadioButton.ToolTip>
@ -96,7 +96,7 @@
</RadioButton> </RadioButton>
<RadioButton Grid.Column="1" <RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding RenderProfileElement.DisplayContinuously, Converter={StaticResource InverseBooleanConverter}}" IsChecked="{Binding DisplayContinuously, Converter={StaticResource InverseBooleanConverter}}"
MinWidth="0" MinWidth="0"
Padding="5 0"> Padding="5 0">
<RadioButton.ToolTip> <RadioButton.ToolTip>
@ -135,7 +135,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<RadioButton Grid.Column="0" <RadioButton Grid.Column="0"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline}" IsChecked="{Binding AlwaysFinishTimeline}"
MinWidth="0" MinWidth="0"
Padding="5 0"> Padding="5 0">
<RadioButton.ToolTip> <RadioButton.ToolTip>
@ -152,7 +152,7 @@
</RadioButton> </RadioButton>
<RadioButton Grid.Column="1" <RadioButton Grid.Column="1"
Style="{StaticResource MaterialDesignTabRadioButton}" Style="{StaticResource MaterialDesignTabRadioButton}"
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}" IsChecked="{Binding AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}"
MinWidth="0" MinWidth="0"
Padding="5 0"> Padding="5 0">
<RadioButton.ToolTip> <RadioButton.ToolTip>

View File

@ -14,6 +14,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private RenderProfileElement _renderProfileElement; private RenderProfileElement _renderProfileElement;
private DisplayConditionGroupViewModel _rootGroup; private DisplayConditionGroupViewModel _rootGroup;
private int _transitionerIndex; private int _transitionerIndex;
private bool _displayContinuously;
private bool _alwaysFinishTimeline;
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
{ {
@ -39,15 +41,22 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
set => SetAndNotify(ref _renderProfileElement, value); set => SetAndNotify(ref _renderProfileElement, value);
} }
public int ConditionBehaviourIndex public bool DisplayContinuously
{ {
get => RenderProfileElement != null && RenderProfileElement.AlwaysFinishTimeline ? 0 : 1; get => _displayContinuously;
set set
{ {
if (RenderProfileElement == null) if (!SetAndNotify(ref _displayContinuously, value)) return;
return; _profileEditorService.UpdateSelectedProfileElement();
}
}
RenderProfileElement.AlwaysFinishTimeline = value == 0; public bool AlwaysFinishTimeline
{
get => _alwaysFinishTimeline;
set
{
if (!SetAndNotify(ref _alwaysFinishTimeline, value)) return;
_profileEditorService.UpdateSelectedProfileElement(); _profileEditorService.UpdateSelectedProfileElement();
} }
} }
@ -70,9 +79,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e) private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
{ {
RenderProfileElement = e.RenderProfileElement; RenderProfileElement = e.RenderProfileElement;
NotifyOfPropertyChange(nameof(ConditionBehaviourIndex));
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled)); NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
_displayContinuously = RenderProfileElement?.DisplayContinuously ?? false;
NotifyOfPropertyChange(nameof(DisplayContinuously));
_alwaysFinishTimeline = RenderProfileElement?.AlwaysFinishTimeline ?? false;
NotifyOfPropertyChange(nameof(AlwaysFinishTimeline));
if (e.RenderProfileElement == null) if (e.RenderProfileElement == null)
{ {
RootGroup?.Dispose(); RootGroup?.Dispose();

View File

@ -209,7 +209,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
public List<LayerPropertyGroupViewModel> GetAllLayerPropertyGroupViewModels() public List<LayerPropertyGroupViewModel> GetAllLayerPropertyGroupViewModels()
{ {
var groups = LayerPropertyGroups.ToList(); var groups = LayerPropertyGroups.ToList();
groups.AddRange(groups.SelectMany(g => g.Children).Where(g => g is LayerPropertyGroupViewModel).Cast<LayerPropertyGroupViewModel>()); var toAdd = groups.SelectMany(g => g.Children).Where(g => g is LayerPropertyGroupViewModel).Cast<LayerPropertyGroupViewModel>().ToList();
groups.AddRange(toAdd);
return groups; return groups;
} }

View File

@ -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.Timeline;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using Humanizer;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
@ -42,6 +43,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
TreePropertyGroupViewModel = _layerPropertyVmFactory.TreePropertyGroupViewModel(this); TreePropertyGroupViewModel = _layerPropertyVmFactory.TreePropertyGroupViewModel(this);
TimelinePropertyGroupViewModel = _layerPropertyVmFactory.TimelinePropertyGroupViewModel(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; LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
PopulateChildren(); PopulateChildren();
DetermineType(); DetermineType();

View File

@ -21,7 +21,8 @@
Text="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Name}" Text="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Name}"
ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}" ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="0 5"> HorizontalAlignment="Left"
Margin="3 5 0 5">
<TextBlock.Style> <TextBlock.Style>
<Style TargetType="{x:Type TextBlock}"> <Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed" /> <Setter Property="Visibility" Value="Collapsed" />

View File

@ -9,7 +9,7 @@
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:TreePropertyViewModel}}"> d:DataContext="{d:DesignInstance {x:Type local:TreePropertyViewModel}}">
<Grid Height="22"> <Grid Height="22" Margin="-20 0 0 0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />

View File

@ -7,6 +7,8 @@
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:profileEditor="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor" xmlns:profileEditor="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor"
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors" xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
xmlns:profile="clr-namespace:Artemis.Core.Models.Profile;assembly=Artemis.Core"
xmlns:layerBrush="clr-namespace:Artemis.Core.Plugins.LayerBrush;assembly=Artemis.Core"
mc:Ignorable="d" mc:Ignorable="d"
behaviors:InputBindingBehavior.PropagateInputBindingsToWindow="True" behaviors:InputBindingBehavior.PropagateInputBindingsToWindow="True"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
@ -19,6 +21,38 @@
<ResourceDictionary <ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" /> Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="SimpleTemplate">
<StackPanel d:DataContext="{d:DesignInstance {x:Type profile:ProfileDescriptor}}" Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</ControlTemplate>
<ControlTemplate x:Key="ExtendedTemplate">
<Grid d:DataContext="{d:DesignInstance {x:Type profile:ProfileDescriptor}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
<Button Grid.Column="1"
Style="{StaticResource MaterialDesignFloatingActionMiniDarkButton}"
Width="26"
Height="26"
VerticalAlignment="Top"
Command="{s:Action DeleteProfile}"
CommandParameter="{Binding}">
<materialDesign:PackIcon Kind="TrashCanOutline" Height="14" Width="14" HorizontalAlignment="Right" />
</Button>
</Grid>
</ControlTemplate>
<DataTemplate x:Key="ProfileDescriptorTemplate">
<Control x:Name="TemplateControl" Focusable="False" Template="{StaticResource ExtendedTemplate}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}">
<Setter TargetName="TemplateControl" Property="Template" Value="{StaticResource SimpleTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
@ -98,12 +132,11 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ComboBox Name="LocaleCombo" <ComboBox Height="26"
Height="26"
VerticalAlignment="Top" VerticalAlignment="Top"
ItemsSource="{Binding Profiles}" ItemsSource="{Binding Profiles}"
SelectedItem="{Binding SelectedProfile}" SelectedItem="{Binding SelectedProfile}"
DisplayMemberPath="Name"> ItemTemplate="{StaticResource ProfileDescriptorTemplate}">
<ComboBox.ItemsPanel> <ComboBox.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<VirtualizingStackPanel /> <VirtualizingStackPanel />

View File

@ -25,7 +25,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
private readonly IProfileService _profileService; private readonly IProfileService _profileService;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly ISnackbarMessageQueue _snackbarMessageQueue; private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private BindableCollection<Profile> _profiles; private BindableCollection<ProfileDescriptor> _profiles;
private PluginSetting<GridLength> _sidePanelsWidth; private PluginSetting<GridLength> _sidePanelsWidth;
private PluginSetting<GridLength> _displayConditionsHeight; private PluginSetting<GridLength> _displayConditionsHeight;
private PluginSetting<GridLength> _bottomPanelsHeight; private PluginSetting<GridLength> _bottomPanelsHeight;
@ -34,6 +34,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
private ProfileTreeViewModel _profileTreeViewModel; private ProfileTreeViewModel _profileTreeViewModel;
private LayerPropertiesViewModel _layerPropertiesViewModel; private LayerPropertiesViewModel _layerPropertiesViewModel;
private DisplayConditionsViewModel _displayConditionsViewModel; private DisplayConditionsViewModel _displayConditionsViewModel;
private ProfileDescriptor _selectedProfile;
public ProfileEditorViewModel(ProfileModule module, public ProfileEditorViewModel(ProfileModule module,
ICollection<ProfileEditorPanelViewModel> viewModels, ICollection<ProfileEditorPanelViewModel> viewModels,
@ -52,7 +53,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
Module = module; Module = module;
DialogService = dialogService; DialogService = dialogService;
Profiles = new BindableCollection<Profile>(); Profiles = new BindableCollection<ProfileDescriptor>();
// Run this first to let VMs activate without causing constant UI updates // Run this first to let VMs activate without causing constant UI updates
Items.AddRange(viewModels); Items.AddRange(viewModels);
@ -91,7 +92,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
set => SetAndNotify(ref _profileViewModel, value); set => SetAndNotify(ref _profileViewModel, value);
} }
public BindableCollection<Profile> Profiles public BindableCollection<ProfileDescriptor> Profiles
{ {
get => _profiles; get => _profiles;
set => SetAndNotify(ref _profiles, value); set => SetAndNotify(ref _profiles, value);
@ -121,17 +122,22 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
set => SetAndNotify(ref _elementPropertiesWidth, value); set => SetAndNotify(ref _elementPropertiesWidth, value);
} }
public Profile SelectedProfile public ProfileDescriptor SelectedProfile
{ {
get => Module.ActiveProfile; get => _selectedProfile;
set => ChangeSelectedProfile(value); set
{
if (!SetAndNotify(ref _selectedProfile, value)) return;
NotifyOfPropertyChange(nameof(CanDeleteActiveProfile));
ActivateSelectedProfile();
}
} }
public bool CanDeleteActiveProfile => SelectedProfile != null && Profiles.Count > 1; public bool CanDeleteActiveProfile => SelectedProfile != null && Profiles.Count > 1;
public Profile CreateProfile(string name) public ProfileDescriptor CreateProfile(string name)
{ {
var profile = _profileService.CreateProfile(Module, name); var profile = _profileService.CreateProfileDescriptor(Module, name);
Profiles.Add(profile); Profiles.Add(profile);
return profile; return profile;
} }
@ -146,6 +152,17 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
} }
} }
public async Task DeleteProfile(ProfileDescriptor profileDescriptor)
{
var result = await DialogService.ShowConfirmDialog(
"Delete profile",
$"Are you sure you want to delete '{profileDescriptor.Name}'? This cannot be undone."
);
if (result)
RemoveProfile(profileDescriptor);
}
public async Task DeleteActiveProfile() public async Task DeleteActiveProfile()
{ {
var result = await DialogService.ShowConfirmDialog( var result = await DialogService.ShowConfirmDialog(
@ -153,21 +170,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
"Are you sure you want to delete your currently active profile? This cannot be undone." "Are you sure you want to delete your currently active profile? This cannot be undone."
); );
if (!result || !CanDeleteActiveProfile) if (result)
return; RemoveProfile(SelectedProfile);
var profile = SelectedProfile;
var index = Profiles.IndexOf(profile);
// Get a new active profile
var newActiveProfile = index - 1 > -1 ? Profiles[index - 1] : Profiles[index + 1];
// Activate the new active profile
SelectedProfile = newActiveProfile;
// Remove the old one
Profiles.Remove(profile);
_profileService.DeleteProfile(profile);
} }
public void Undo() public void Undo()
@ -176,7 +180,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
var beforeGroups = LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels(); var beforeGroups = LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels();
var expandedPaths = beforeGroups.Where(g => g.IsExpanded).Select(g => g.LayerPropertyGroup.Path).ToList(); var expandedPaths = beforeGroups.Where(g => g.IsExpanded).Select(g => g.LayerPropertyGroup.Path).ToList();
if (!_profileEditorService.UndoUpdateProfile(Module)) if (!_profileEditorService.UndoUpdateProfile())
{ {
_snackbarMessageQueue.Enqueue("Nothing to undo"); _snackbarMessageQueue.Enqueue("Nothing to undo");
return; return;
@ -195,7 +199,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
var beforeGroups = LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels(); var beforeGroups = LayerPropertiesViewModel.GetAllLayerPropertyGroupViewModels();
var expandedPaths = beforeGroups.Where(g => g.IsExpanded).Select(g => g.LayerPropertyGroup.Path).ToList(); var expandedPaths = beforeGroups.Where(g => g.IsExpanded).Select(g => g.LayerPropertyGroup.Path).ToList();
if (!_profileEditorService.RedoUpdateProfile(Module)) if (!_profileEditorService.RedoUpdateProfile())
{ {
_snackbarMessageQueue.Enqueue("Nothing to redo"); _snackbarMessageQueue.Enqueue("Nothing to redo");
return; return;
@ -225,29 +229,38 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
base.OnClose(); base.OnClose();
} }
private void ChangeSelectedProfile(Profile profile) private void RemoveProfile(ProfileDescriptor profileDescriptor)
{ {
var oldProfile = Module.ActiveProfile; if (SelectedProfile == profileDescriptor && !CanDeleteActiveProfile)
_profileService.ActivateProfile(Module, profile); return;
if (oldProfile != null) var index = Profiles.IndexOf(profileDescriptor);
_profileService.UpdateProfile(oldProfile, false);
if (profile != null)
_profileService.UpdateProfile(profile, false);
if (_profileEditorService.SelectedProfile != profile) // Get a new active profile
var newActiveProfile = index - 1 > -1 ? Profiles[index - 1] : Profiles[index + 1];
// Activate the new active profile
SelectedProfile = newActiveProfile;
// Remove the old one
Profiles.Remove(profileDescriptor);
_profileService.DeleteProfile(profileDescriptor);
}
private void ActivateSelectedProfile()
{
Execute.PostToUIThread(async () =>
{
var changeTask = _profileService.ActivateProfileAnimated(SelectedProfile);
_profileEditorService.ChangeSelectedProfile(null);
var profile = await changeTask;
_profileEditorService.ChangeSelectedProfile(profile); _profileEditorService.ChangeSelectedProfile(profile);
});
NotifyOfPropertyChange(nameof(SelectedProfile));
NotifyOfPropertyChange(nameof(CanDeleteActiveProfile));
} }
private void ModuleOnActiveProfileChanged(object sender, EventArgs e) private void ModuleOnActiveProfileChanged(object sender, EventArgs e)
{ {
if (SelectedProfile == Module.ActiveProfile) SelectedProfile = Profiles.FirstOrDefault(d => d.Id == Module.ActiveProfile.EntityId);
return;
SelectedProfile = Profiles.FirstOrDefault(p => p == Module.ActiveProfile);
} }
private void LoadWorkspaceSettings() private void LoadWorkspaceSettings()
@ -269,29 +282,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
private void LoadProfiles() private void LoadProfiles()
{ {
// Get all profiles from the database // Get all profiles from the database
var profiles = _profileService.GetProfiles(Module); Profiles.Clear();
// Get the latest active profile, this falls back to just any profile so if null, create a default profile Profiles.AddRange(_profileService.GetProfileDescriptors(Module).OrderBy(d => d.Name));
var activeProfile = _profileService.GetActiveProfile(Module) ?? _profileService.CreateProfile(Module, "Default");
// GetActiveProfile can return a duplicate because inactive profiles aren't kept in memory, make sure it's unique in the profiles list // Populate the selected profile
profiles = profiles.Where(p => p.EntityId != activeProfile.EntityId).ToList(); SelectedProfile = Profiles.FirstOrDefault(p => p.IsLastActiveProfile);
profiles.Add(activeProfile);
// Populate the UI collection if (SelectedProfile != null)
Profiles.AddRange(profiles.Except(Profiles).ToList()); return;
Profiles.RemoveRange(Profiles.Except(profiles).ToList());
var index = 0;
foreach (var profile in Profiles.OrderBy(p => p.Name).ToList())
{
Profiles.Move(Profiles.IndexOf(profile), index);
index++;
}
SelectedProfile = activeProfile; // Create a default profile if there is none
if (_profileEditorService.SelectedProfile != activeProfile) var defaultProfile = _profileService.CreateProfileDescriptor(Module, "Default");
_profileEditorService.ChangeSelectedProfile(activeProfile); Profiles.Add(defaultProfile);
if (!activeProfile.IsActivated) SelectedProfile = defaultProfile;
_profileService.ActivateProfile(Module, activeProfile);
} }
} }
} }

View File

@ -2,9 +2,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.LayerShapes; using Artemis.Core.Models.Profile.LayerShapes;
using Artemis.Core.Models.Surface; using Artemis.Core.Models.Surface;
@ -12,7 +10,6 @@ using Artemis.UI.Extensions;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using Stylet; using Stylet;
using Rectangle = Artemis.Core.Models.Profile.LayerShapes.Rectangle;
namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{ {
@ -48,7 +45,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
get => _shapeGeometry; get => _shapeGeometry;
set => SetAndNotify(ref _shapeGeometry, value); set => SetAndNotify(ref _shapeGeometry, value);
} }
public Rect ViewportRectangle public Rect ViewportRectangle
{ {
get => _viewportRectangle; get => _viewportRectangle;
@ -117,7 +114,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private void Update() private void Update()
{ {
IsSelected = _profileEditorService.SelectedProfileElement == Layer; IsSelected = _profileEditorService.SelectedProfileElement == Layer;
CreateShapeGeometry(); CreateShapeGeometry();
CreateViewportRectangle(); CreateViewportRectangle();
} }

View File

@ -56,7 +56,7 @@
&lt;/Entry.SortBy&gt;&#xD; &lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD; &lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD; &lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Test Methods"&gt;&#xD; &lt;Entry DisplayName="Test Methods" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD; &lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD; &lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD; &lt;Kind Is="Method" /&gt;&#xD;
@ -89,7 +89,7 @@
&lt;/Entry.Match&gt;&#xD; &lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD; &lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD; &lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Test Methods"&gt;&#xD; &lt;Entry DisplayName="Test Methods" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD; &lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD; &lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD; &lt;Kind Is="Method" /&gt;&#xD;
@ -102,7 +102,7 @@
&lt;/Entry&gt;&#xD; &lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD; &lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="Default Pattern"&gt;&#xD; &lt;TypePattern DisplayName="Default Pattern"&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Public Delegates"&gt;&#xD; &lt;Entry DisplayName="Public Delegates" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD; &lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD; &lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD; &lt;Access Is="Public" /&gt;&#xD;
@ -113,17 +113,6 @@
&lt;Name /&gt;&#xD; &lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD; &lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD; &lt;/Entry&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Public Enums"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Enum" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Static Fields and Constants"&gt;&#xD; &lt;Entry DisplayName="Static Fields and Constants"&gt;&#xD;
&lt;Entry.Match&gt;&#xD; &lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD; &lt;Or&gt;&#xD;
@ -168,7 +157,7 @@
&lt;/Or&gt;&#xD; &lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD; &lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD; &lt;/Entry&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Interface Implementations"&gt;&#xD; &lt;Entry DisplayName="Interface Implementations" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD; &lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD; &lt;And&gt;&#xD;
&lt;Kind Is="Member" /&gt;&#xD; &lt;Kind Is="Member" /&gt;&#xD;
@ -189,6 +178,17 @@
&lt;Kind Is="Type" /&gt;&#xD; &lt;Kind Is="Type" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD; &lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD; &lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public Enums" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Enum" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD; &lt;/TypePattern&gt;&#xD;
&lt;/Patterns&gt;</s:String> &lt;/Patterns&gt;</s:String>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=Formatting/@KeyIndexDefined">True</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/Generate/=Formatting/@KeyIndexDefined">True</s:Boolean>

View File

@ -2,7 +2,9 @@
using System.ComponentModel; using System.ComponentModel;
using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile;
using Artemis.Core.Plugins.LayerBrush.Abstract; using Artemis.Core.Plugins.LayerBrush.Abstract;
using Artemis.Plugins.LayerBrushes.Color.PropertyGroups;
using SkiaSharp; using SkiaSharp;
using static Artemis.Plugins.LayerBrushes.Color.PropertyGroups.ColorBrushProperties;
namespace Artemis.Plugins.LayerBrushes.Color namespace Artemis.Plugins.LayerBrushes.Color
{ {
@ -18,7 +20,7 @@ namespace Artemis.Plugins.LayerBrushes.Color
{ {
Layer.RenderPropertiesUpdated += HandleShaderChange; Layer.RenderPropertiesUpdated += HandleShaderChange;
Properties.LayerPropertyBaseValueChanged += HandleShaderChange; Properties.LayerPropertyBaseValueChanged += HandleShaderChange;
Properties.Gradient.BaseValue.PropertyChanged += BaseValueOnPropertyChanged; Properties.Colors.BaseValue.PropertyChanged += BaseValueOnPropertyChanged;
} }
public override void DisableLayerBrush() public override void DisableLayerBrush()
@ -26,9 +28,9 @@ namespace Artemis.Plugins.LayerBrushes.Color
Layer.RenderPropertiesUpdated -= HandleShaderChange; Layer.RenderPropertiesUpdated -= HandleShaderChange;
Properties.GradientType.BaseValueChanged -= HandleShaderChange; Properties.GradientType.BaseValueChanged -= HandleShaderChange;
Properties.Color.BaseValueChanged -= HandleShaderChange; Properties.Color.BaseValueChanged -= HandleShaderChange;
Properties.GradientTileMode.BaseValueChanged -= HandleShaderChange; Properties.TileMode.BaseValueChanged -= HandleShaderChange;
Properties.GradientRepeat.BaseValueChanged -= HandleShaderChange; Properties.ColorsMultiplier.BaseValueChanged -= HandleShaderChange;
Properties.Gradient.BaseValue.PropertyChanged -= BaseValueOnPropertyChanged; Properties.Colors.BaseValue.PropertyChanged -= BaseValueOnPropertyChanged;
_paint?.Dispose(); _paint?.Dispose();
_shader?.Dispose(); _shader?.Dispose();
@ -39,10 +41,10 @@ namespace Artemis.Plugins.LayerBrushes.Color
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
// While rendering a solid, if the color was changed since the last frame, recreate the shader // While rendering a solid, if the color was changed since the last frame, recreate the shader
if (Properties.GradientType.BaseValue == GradientType.Solid && _color != Properties.Color.CurrentValue) if (Properties.GradientType.BaseValue == ColorType.Solid && _color != Properties.Color.CurrentValue)
CreateSolid(); CreateSolid();
// While rendering a linear gradient, if the rotation was changed since the last frame, recreate the shader // While rendering a linear gradient, if the rotation was changed since the last frame, recreate the shader
else if (Properties.GradientType.BaseValue == GradientType.LinearGradient && Math.Abs(_linearGradientRotation - Properties.LinearGradientRotation.CurrentValue) > 0.01) else if (Properties.GradientType.BaseValue == ColorType.LinearGradient && Math.Abs(_linearGradientRotation - Properties.LinearGradientRotation.CurrentValue) > 0.01)
CreateLinearGradient(); CreateLinearGradient();
} }
@ -84,16 +86,16 @@ namespace Artemis.Plugins.LayerBrushes.Color
{ {
switch (Properties.GradientType.CurrentValue) switch (Properties.GradientType.CurrentValue)
{ {
case GradientType.Solid: case ColorType.Solid:
CreateSolid(); CreateSolid();
break; break;
case GradientType.LinearGradient: case ColorType.LinearGradient:
CreateLinearGradient(); CreateLinearGradient();
break; break;
case GradientType.RadialGradient: case ColorType.RadialGradient:
CreateRadialGradient(); CreateRadialGradient();
break; break;
case GradientType.SweepGradient: case ColorType.SweepGradient:
CreateSweepGradient(); CreateSweepGradient();
break; break;
default: default:
@ -120,16 +122,16 @@ namespace Artemis.Plugins.LayerBrushes.Color
private void CreateLinearGradient() private void CreateLinearGradient()
{ {
var repeat = Properties.GradientRepeat.CurrentValue; var repeat = Properties.ColorsMultiplier.CurrentValue;
_linearGradientRotation = Properties.LinearGradientRotation.CurrentValue; _linearGradientRotation = Properties.LinearGradientRotation.CurrentValue;
_shader?.Dispose(); _shader?.Dispose();
_shader = SKShader.CreateLinearGradient( _shader = SKShader.CreateLinearGradient(
new SKPoint(_shaderBounds.Left, _shaderBounds.Top), new SKPoint(_shaderBounds.Left, _shaderBounds.Top),
new SKPoint(_shaderBounds.Right, _shaderBounds.Top), new SKPoint(_shaderBounds.Right, _shaderBounds.Top),
Properties.Gradient.BaseValue.GetColorsArray(repeat), Properties.Colors.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(repeat), Properties.Colors.BaseValue.GetPositionsArray(repeat),
Properties.GradientTileMode.CurrentValue, Properties.TileMode.CurrentValue,
SKMatrix.MakeRotationDegrees(_linearGradientRotation, _shaderBounds.Left, _shaderBounds.MidY) SKMatrix.MakeRotationDegrees(_linearGradientRotation, _shaderBounds.Left, _shaderBounds.MidY)
); );
UpdatePaint(); UpdatePaint();
@ -137,29 +139,29 @@ namespace Artemis.Plugins.LayerBrushes.Color
private void CreateRadialGradient() private void CreateRadialGradient()
{ {
var repeat = Properties.GradientRepeat.CurrentValue; var repeat = Properties.ColorsMultiplier.CurrentValue;
_shader?.Dispose(); _shader?.Dispose();
_shader = SKShader.CreateRadialGradient( _shader = SKShader.CreateRadialGradient(
new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY), new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY),
Math.Max(_shaderBounds.Width, _shaderBounds.Height) / 2f, Math.Max(_shaderBounds.Width, _shaderBounds.Height) / 2f,
Properties.Gradient.BaseValue.GetColorsArray(repeat), Properties.Colors.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(repeat), Properties.Colors.BaseValue.GetPositionsArray(repeat),
Properties.GradientTileMode.CurrentValue Properties.TileMode.CurrentValue
); );
UpdatePaint(); UpdatePaint();
} }
private void CreateSweepGradient() private void CreateSweepGradient()
{ {
var repeat = Properties.GradientRepeat.CurrentValue; var repeat = Properties.ColorsMultiplier.CurrentValue;
_shader?.Dispose(); _shader?.Dispose();
_shader = SKShader.CreateSweepGradient( _shader = SKShader.CreateSweepGradient(
new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY), new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY),
Properties.Gradient.BaseValue.GetColorsArray(repeat), Properties.Colors.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(repeat), Properties.Colors.BaseValue.GetPositionsArray(repeat),
Properties.GradientTileMode.CurrentValue, Properties.TileMode.CurrentValue,
0, 0,
360 360
); );

View File

@ -1,115 +0,0 @@
using System.ComponentModel;
using Artemis.Core.Events;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Colors;
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
using Artemis.Core.Models.Profile.LayerProperties.Types;
using SkiaSharp;
namespace Artemis.Plugins.LayerBrushes.Color
{
public class ColorBrushProperties : LayerPropertyGroup
{
[PropertyDescription(Description = "The type of color brush to draw")]
public EnumLayerProperty<GradientType> GradientType { get; set; }
[PropertyDescription(Description = "How to handle the layer having to stretch beyond it's regular size")]
public EnumLayerProperty<SKShaderTileMode> GradientTileMode { get; set; }
[PropertyDescription(Description = "The color of the brush")]
public SKColorLayerProperty Color { get; set; }
[PropertyDescription(Description = "The gradient of the brush")]
public ColorGradientLayerProperty Gradient { get; set; }
[PropertyDescription(DisableKeyframes = true, Description = "How many times to repeat the colors in the selected gradient", MinInputValue = 0, MaxInputValue = 10)]
public IntLayerProperty GradientRepeat { get; set; }
#region Linear greadient properties
[PropertyDescription(Name = "Rotation", Description = "Change the rotation of the linear gradient without affecting the rotation of the shape", InputAffix = "°")]
public FloatLayerProperty LinearGradientRotation { get; set; }
#endregion
protected override void PopulateDefaults()
{
GradientType.DefaultValue = LayerBrushes.Color.GradientType.Solid;
Color.DefaultValue = new SKColor(255, 0, 0);
Gradient.DefaultValue = ColorGradient.GetUnicornBarf();
GradientRepeat.DefaultValue = 0;
}
protected override void EnableProperties()
{
GradientType.BaseValueChanged += OnBaseValueChanged;
if (ProfileElement is Layer layer)
layer.General.ResizeMode.BaseValueChanged += OnBaseValueChanged;
UpdateVisibility();
}
protected override void DisableProperties()
{
GradientType.BaseValueChanged -= OnBaseValueChanged;
if (ProfileElement is Layer layer)
layer.General.ResizeMode.BaseValueChanged -= OnBaseValueChanged;
}
private void OnBaseValueChanged(object sender, LayerPropertyEventArgs e)
{
UpdateVisibility();
}
private void UpdateVisibility()
{
var normalRender = false;
if (ProfileElement is Layer layer)
normalRender = layer.General.ResizeMode.CurrentValue == LayerResizeMode.Normal;
Color.IsHidden = GradientType.BaseValue != LayerBrushes.Color.GradientType.Solid;
Gradient.IsHidden = GradientType.BaseValue == LayerBrushes.Color.GradientType.Solid;
GradientRepeat.IsHidden = GradientType.BaseValue == LayerBrushes.Color.GradientType.Solid;
RadialGradientCenterOffset.IsHidden = GradientType.BaseValue != LayerBrushes.Color.GradientType.RadialGradient;
RadialGradientResizeMode.IsHidden = GradientType.BaseValue != LayerBrushes.Color.GradientType.RadialGradient;
GradientTileMode.IsHidden = normalRender;
RadialGradientResizeMode.IsHidden = !normalRender || GradientType.BaseValue != LayerBrushes.Color.GradientType.RadialGradient;
}
#region Radial gradient properties
[PropertyDescription(Name = "Center offset", Description = "Change the position of the gradient by offsetting it from the center of the layer", InputAffix = "%")]
public SKPointLayerProperty RadialGradientCenterOffset { get; set; }
[PropertyDescription(Name = "Resize mode", Description = "How to make the gradient adjust to scale changes")]
public EnumLayerProperty<RadialGradientResizeMode> RadialGradientResizeMode { get; set; }
#endregion
}
public enum GradientType
{
[Description("Solid")]
Solid,
[Description("Linear Gradient")]
LinearGradient,
[Description("Radial Gradient")]
RadialGradient,
[Description("Sweep Gradient")]
SweepGradient
}
public enum RadialGradientResizeMode
{
[Description("Stretch or shrink")]
Stretch,
[Description("Maintain a circle")]
MaintainCircle
}
}

View File

@ -0,0 +1,99 @@
using System.ComponentModel;
using Artemis.Core.Events;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Colors;
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
using Artemis.Core.Models.Profile.LayerProperties.Types;
using SkiaSharp;
namespace Artemis.Plugins.LayerBrushes.Color.PropertyGroups
{
public class ColorBrushProperties : LayerPropertyGroup
{
[PropertyDescription(Name = "Type", Description = "The type of color brush to draw")]
public EnumLayerProperty<ColorType> GradientType { get; set; }
[PropertyDescription(Description = "How to handle the layer having to stretch beyond it's regular size")]
public EnumLayerProperty<SKShaderTileMode> TileMode { get; set; }
[PropertyDescription(Description = "The color of the brush")]
public SKColorLayerProperty Color { get; set; }
[PropertyDescription(Description = "The gradient of the brush")]
public ColorGradientLayerProperty Colors { get; set; }
[PropertyDescription(Name = "Colors multiplier", Description = "How many times to repeat the colors in the selected gradient", DisableKeyframes = true, MinInputValue = 0, MaxInputValue = 10)]
public IntLayerProperty ColorsMultiplier { get; set; }
[PropertyDescription(Description = "Change the rotation of the linear gradient without affecting the rotation of the shape", InputAffix = "°")]
public FloatLayerProperty LinearGradientRotation { get; set; }
[PropertyGroupDescription(Description = "Advanced radial gradient controls")]
public RadialGradientProperties RadialGradient { get; set; }
protected override void PopulateDefaults()
{
GradientType.DefaultValue = ColorType.Solid;
Color.DefaultValue = new SKColor(255, 0, 0);
Colors.DefaultValue = ColorGradient.GetUnicornBarf();
ColorsMultiplier.DefaultValue = 0;
}
protected override void EnableProperties()
{
GradientType.BaseValueChanged += OnBaseValueChanged;
if (ProfileElement is Layer layer)
layer.General.ResizeMode.BaseValueChanged += OnBaseValueChanged;
UpdateVisibility();
}
protected override void DisableProperties()
{
GradientType.BaseValueChanged -= OnBaseValueChanged;
if (ProfileElement is Layer layer)
layer.General.ResizeMode.BaseValueChanged -= OnBaseValueChanged;
}
private void OnBaseValueChanged(object sender, LayerPropertyEventArgs e)
{
UpdateVisibility();
}
private void UpdateVisibility()
{
var normalRender = false;
if (ProfileElement is Layer layer)
normalRender = layer.General.ResizeMode.CurrentValue == LayerResizeMode.Normal;
// Solid settings
Color.IsHidden = GradientType.BaseValue != ColorType.Solid;
// Gradients settings
Colors.IsHidden = GradientType.BaseValue == ColorType.Solid;
ColorsMultiplier.IsHidden = GradientType.BaseValue == ColorType.Solid;
// Linear-gradient settings
LinearGradientRotation.IsHidden = GradientType.BaseValue != ColorType.LinearGradient;
RadialGradient.IsHidden = GradientType.BaseValue != ColorType.RadialGradient;
// Normal render settings
TileMode.IsHidden = normalRender;
}
public enum ColorType
{
[Description("Solid")]
Solid,
[Description("Linear Gradient")]
LinearGradient,
[Description("Radial Gradient")]
RadialGradient,
[Description("Sweep Gradient")]
SweepGradient
}
}
}

View File

@ -0,0 +1,58 @@
using System.ComponentModel;
using Artemis.Core.Events;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
using Artemis.Core.Models.Profile.LayerProperties.Types;
namespace Artemis.Plugins.LayerBrushes.Color.PropertyGroups
{
public class RadialGradientProperties : LayerPropertyGroup
{
[PropertyDescription(Name = "Center offset", Description = "Change the position of the gradient by offsetting it from the center of the layer", InputAffix = "%")]
public SKPointLayerProperty CenterOffset { get; set; }
[PropertyDescription(Name = "Resize mode", Description = "How to make the gradient adjust to scale changes")]
public EnumLayerProperty<RadialGradientResizeMode> ResizeMode { get; set; }
protected override void PopulateDefaults()
{
}
protected override void EnableProperties()
{
if (ProfileElement is Layer layer)
layer.General.ResizeMode.BaseValueChanged += OnBaseValueChanged;
UpdateVisibility();
}
protected override void DisableProperties()
{
if (ProfileElement is Layer layer)
layer.General.ResizeMode.BaseValueChanged -= OnBaseValueChanged;
}
private void OnBaseValueChanged(object sender, LayerPropertyEventArgs e)
{
UpdateVisibility();
}
private void UpdateVisibility()
{
var normalRender = false;
if (ProfileElement is Layer layer)
normalRender = layer.General.ResizeMode.CurrentValue == LayerResizeMode.Normal;
ResizeMode.IsHidden = !normalRender;
}
public enum RadialGradientResizeMode
{
[Description("Stretch or shrink")]
Stretch,
[Description("Maintain a circle")]
MaintainCircle
}
}
}

View File

@ -23,5 +23,9 @@ namespace Artemis.Plugins.LayerBrushes.ColorRgbNet
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -49,6 +49,10 @@ namespace Artemis.Plugins.LayerBrushes.Noise
UpdateVisibility(); UpdateVisibility();
} }
protected override void DisableProperties()
{
}
private void UpdateVisibility() private void UpdateVisibility()
{ {
GradientColor.IsHidden = ColorType.BaseValue != ColorMappingType.Gradient; GradientColor.IsHidden = ColorType.BaseValue != ColorMappingType.Gradient;

View File

@ -18,7 +18,7 @@ namespace Artemis.Plugins.LayerEffects.Filter
{ {
} }
private void UpdateVisibility() protected override void DisableProperties()
{ {
} }
} }

View File

@ -25,5 +25,9 @@ namespace Artemis.Plugins.LayerEffects.Filter
{ {
ColorMatrix.IsHidden = true; ColorMatrix.IsHidden = true;
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -16,5 +16,9 @@ namespace Artemis.Plugins.LayerEffects.Filter
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -16,5 +16,9 @@ namespace Artemis.Plugins.LayerEffects.Filter
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -25,5 +25,9 @@ namespace Artemis.Plugins.LayerEffects.Filter
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -16,5 +16,9 @@ namespace Artemis.Plugins.LayerEffects.Filter
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }

View File

@ -43,5 +43,9 @@ namespace Artemis.Plugins.LayerEffects.Filter
protected override void EnableProperties() protected override void EnableProperties()
{ {
} }
protected override void DisableProperties()
{
}
} }
} }