Profile elements - Simplified class hierarchy

Profile editor - Seperated event types and avoid a lot of type checking
Profile editor - Avoid changing profiles/layers if the new equals the old
Color picker - Fixed binding error
Profile tree - Select first element by default
Profile editor - Adjusted initialization order
This commit is contained in:
Robert committed 2020-07-09 12:23:57 +02:00
1 parent 70aaa645fb
commit 7b27d076b3
25 files changed
+303 -195

No files matched your search

-1
View File
@@ -34,7 +34,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.1.6" />
<PackageReference Include="Castle.Core" Version="4.4.1" />
<PackageReference Include="HidSharp" Version="2.1.0" />
<PackageReference Include="LiteDB" Version="5.0.8" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
@@ -32,5 +32,7 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
_children.Remove(displayConditionPart);
}
}
public abstract void ApplyToEntity();
}
}
@@ -5,6 +5,12 @@ namespace Artemis.Core.Models.Profile.Conditions
public class DisplayConditionGroup : DisplayConditionPart
{
public BooleanOperator BooleanOperator { get; set; }
public override void ApplyToEntity()
{
foreach (var child in Children)
child.ApplyToEntity();
}
}
public enum BooleanOperator
+6 -3
View File
@@ -7,7 +7,7 @@ using SkiaSharp;
namespace Artemis.Core.Models.Profile
{
public sealed class Folder : EffectProfileElement
public sealed class Folder : RenderProfileElement
{
private SKBitmap _folderBitmap;
@@ -159,7 +159,7 @@ namespace Artemis.Core.Models.Profile
var path = new SKPath {FillType = SKPathFillType.Winding};
foreach (var child in Children)
{
if (child is EffectProfileElement effectChild && effectChild.Path != null)
if (child is RenderProfileElement effectChild && effectChild.Path != null)
path.AddPath(effectChild.Path);
}
@@ -182,10 +182,13 @@ namespace Artemis.Core.Models.Profile
FolderEntity.Enabled = Enabled;
FolderEntity.ProfileId = Profile.EntityId;
FolderEntity.ExpandedPropertyGroups.Clear();
FolderEntity.ExpandedPropertyGroups.AddRange(_expandedPropertyGroups);
ApplyLayerEffectsToEntity();
// TODO: conditions
// Conditions
DisplayConditionGroup?.ApplyToEntity();
}
#region Events
+4 -12
View File
@@ -21,9 +21,8 @@ namespace Artemis.Core.Models.Profile
/// Represents a layer on a profile. To create new layers use the <see cref="LayerService" /> by injecting
/// <see cref="ILayerService" /> into your code
/// </summary>
public sealed class Layer : EffectProfileElement
public sealed class Layer : RenderProfileElement
{
private DisplayConditionGroup _displayConditionGroup;
private LayerGeneralProperties _general;
private SKBitmap _layerBitmap;
private BaseLayerBrush _layerBrush;
@@ -117,14 +116,7 @@ namespace Artemis.Core.Models.Profile
internal set => SetAndNotify(ref _layerBrush, value);
}
/// <summary>
/// Gets or sets the root display condition group
/// </summary>
public DisplayConditionGroup DisplayConditionGroup
{
get => _displayConditionGroup;
set => SetAndNotify(ref _displayConditionGroup, value);
}
public override string ToString()
{
@@ -164,8 +156,8 @@ namespace Artemis.Core.Models.Profile
LayerEntity.Leds.Add(ledEntity);
}
// Conditions TODO
LayerEntity.Conditions.Clear();
// Conditions
DisplayConditionGroup?.ApplyToEntity();
}
#endregion
@@ -20,7 +20,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
/// <summary>
/// Gets the profile element (such as layer or folder) this effect is applied to
/// </summary>
public PropertiesProfileElement ProfileElement { get; internal set; }
public RenderProfileElement ProfileElement { get; internal set; }
/// <summary>
/// The parent group of this layer property, set after construction
@@ -31,7 +31,7 @@ namespace Artemis.Core.Models.Profile
/// <summary>
/// Gets the profile element (such as layer or folder) this effect is applied to
/// </summary>
public PropertiesProfileElement ProfileElement { get; internal set; }
public RenderProfileElement ProfileElement { get; internal set; }
/// <summary>
/// The path of this property group
@@ -129,7 +129,7 @@ namespace Artemis.Core.Models.Profile
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
}
internal void InitializeProperties(ILayerService layerService, PropertiesProfileElement profileElement, [NotNull] string path)
internal void InitializeProperties(ILayerService layerService, RenderProfileElement profileElement, [NotNull] string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
@@ -236,7 +236,7 @@ namespace Artemis.Core.Models.Profile
OnPropertyGroupOverriding(new PropertyGroupUpdatingEventArgs(overrideTime));
}
private void InitializeProperty(PropertiesProfileElement profileElement, string path, BaseLayerProperty instance)
private void InitializeProperty(RenderProfileElement profileElement, string path, BaseLayerProperty instance)
{
Guid pluginGuid;
if (IsCorePropertyGroup || instance.IsCoreProperty)
@@ -6,52 +6,6 @@ namespace Artemis.Core.Models.Profile
{
public abstract class PropertiesProfileElement : ProfileElement
{
private SKPath _path;
internal abstract PropertiesEntity PropertiesEntity { get; }
/// <summary>
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
/// clipped.
/// </summary>
public SKPath Path
{
get => _path;
protected set
{
SetAndNotify(ref _path, value);
// I can't really be sure about the performance impact of calling Bounds often but
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
Bounds = value?.Bounds ?? SKRect.Empty;
}
}
/// <summary>
/// The bounds of this entity
/// </summary>
public SKRect Bounds
{
get => _bounds;
private set => SetAndNotify(ref _bounds, value);
}
#region Property group expansion
protected List<string> _expandedPropertyGroups;
private SKRect _bounds;
public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
{
return _expandedPropertyGroups.Contains(layerPropertyGroup.Path);
}
public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
{
if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Remove(layerPropertyGroup.Path);
else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Add(layerPropertyGroup.Path);
}
#endregion
}
}
@@ -3,13 +3,69 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.Annotations;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.LayerEffect.Abstract;
using Artemis.Storage.Entities.Profile;
using SkiaSharp;
namespace Artemis.Core.Models.Profile
{
public abstract class EffectProfileElement : PropertiesProfileElement
public abstract class RenderProfileElement : ProfileElement
{
#region Properties
private SKPath _path;
internal abstract PropertiesEntity PropertiesEntity { get; }
/// <summary>
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
/// clipped.
/// </summary>
public SKPath Path
{
get => _path;
protected set
{
SetAndNotify(ref _path, value);
// I can't really be sure about the performance impact of calling Bounds often but
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
Bounds = value?.Bounds ?? SKRect.Empty;
}
}
/// <summary>
/// The bounds of this entity
/// </summary>
public SKRect Bounds
{
get => _bounds;
private set => SetAndNotify(ref _bounds, value);
}
#region Property group expansion
protected List<string> _expandedPropertyGroups;
private SKRect _bounds;
public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
{
return _expandedPropertyGroups.Contains(layerPropertyGroup.Path);
}
public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
{
if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Remove(layerPropertyGroup.Path);
else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
_expandedPropertyGroups.Add(layerPropertyGroup.Path);
}
#endregion
#endregion
#region Effects
protected List<BaseLayerEffect> _layerEffects;
internal abstract EffectsEntity EffectsEntity { get; }
@@ -71,6 +127,23 @@ namespace Artemis.Core.Models.Profile
effect.Dispose();
}
#endregion
#region Conditions
private DisplayConditionGroup _displayConditionGroup;
/// <summary>
/// Gets or sets the root display condition group
/// </summary>
public DisplayConditionGroup DisplayConditionGroup
{
get => _displayConditionGroup;
set => SetAndNotify(ref _displayConditionGroup, value);
}
#endregion
#region Events
public event EventHandler LayerEffectsUpdated;
@@ -14,7 +14,7 @@ namespace Artemis.Core.Plugins.LayerEffect.Abstract
public abstract class BaseLayerEffect : PropertyChangedBase, IDisposable
{
private Guid _entityId;
private EffectProfileElement _profileElement;
private RenderProfileElement _profileElement;
private string _name;
private bool _enabled;
private bool _hasBeenRenamed;
@@ -33,7 +33,7 @@ namespace Artemis.Core.Plugins.LayerEffect.Abstract
/// <summary>
/// Gets the profile element (such as layer or folder) this effect is applied to
/// </summary>
public EffectProfileElement ProfileElement
public RenderProfileElement ProfileElement
{
get => _profileElement;
internal set => SetAndNotify(ref _profileElement, value);
@@ -42,17 +42,17 @@ namespace Artemis.Core.Services.Interfaces
/// Instantiates and adds the <see cref="BaseLayerEffect" /> described by the provided
/// <see cref="LayerEffectDescriptor" /> to the <see cref="Layer" />.
/// </summary>
/// <param name="effectProfileElement">The layer/folder to instantiate the effect for</param>
void InstantiateLayerEffects(EffectProfileElement effectProfileElement);
/// <param name="renderProfileElement">The layer/folder to instantiate the effect for</param>
void InstantiateLayerEffects(RenderProfileElement renderProfileElement);
/// <summary>
/// Adds the <see cref="BaseLayerEffect" /> described by the provided <see cref="LayerEffectDescriptor" /> to the
/// <see cref="Layer" />.
/// </summary>
/// <param name="effectProfileElement">The layer/folder to instantiate the effect for</param>
/// <param name="renderProfileElement">The layer/folder to instantiate the effect for</param>
/// <param name="layerEffectDescriptor"></param>
/// <returns></returns>
BaseLayerEffect AddLayerEffect(EffectProfileElement effectProfileElement, LayerEffectDescriptor layerEffectDescriptor);
BaseLayerEffect AddLayerEffect(RenderProfileElement renderProfileElement, LayerEffectDescriptor layerEffectDescriptor);
void RemoveLayerEffect(BaseLayerEffect layerEffect);
}
+15 -11
View File
@@ -20,7 +20,7 @@ namespace Artemis.Core.Services
private readonly ILogger _logger;
private readonly IPluginService _pluginService;
public LayerService(IKernel kernel, ILogger logger, IPluginService pluginService)
public LayerService(IKernel kernel, ILogger logger, IPluginService pluginService, IDataModelService dataModelService)
{
_kernel = kernel;
_logger = logger;
@@ -39,7 +39,6 @@ namespace Artemis.Core.Services
// With the properties loaded, the layer brush and effect can be instantiated
InstantiateLayerBrush(layer);
InstantiateLayerEffects(layer);
return layer;
}
@@ -85,21 +84,21 @@ namespace Artemis.Core.Services
return brush;
}
public BaseLayerEffect AddLayerEffect(EffectProfileElement effectElement, LayerEffectDescriptor layerEffectDescriptor)
public BaseLayerEffect AddLayerEffect(RenderProfileElement renderElement, LayerEffectDescriptor layerEffectDescriptor)
{
// Create the effect with dependency injection
var effect = (BaseLayerEffect) _kernel.Get(layerEffectDescriptor.LayerEffectType);
effect.ProfileElement = effectElement;
effect.ProfileElement = renderElement;
effect.EntityId = Guid.NewGuid();
effect.Enabled = true;
effect.Order = effectElement.LayerEffects.Count + 1;
effect.Order = renderElement.LayerEffects.Count + 1;
effect.Descriptor = layerEffectDescriptor;
effect.Initialize(this);
effect.Update(0);
effectElement.AddLayerEffect(effect);
renderElement.AddLayerEffect(effect);
_logger.Debug("Added layer effect with root path {rootPath}", effect.PropertyRootPath);
return effect;
}
@@ -109,16 +108,16 @@ namespace Artemis.Core.Services
layerEffect.ProfileElement.RemoveLayerEffect(layerEffect);
}
public void InstantiateLayerEffects(EffectProfileElement effectElement)
public void InstantiateLayerEffects(RenderProfileElement renderElement)
{
var layerEffectProviders = _pluginService.GetPluginsOfType<LayerEffectProvider>();
var descriptors = layerEffectProviders.SelectMany(l => l.LayerEffectDescriptors).ToList();
var entities = effectElement.EffectsEntity.LayerEffects.OrderByDescending(e => e.Order).ToList();
var entities = renderElement.EffectsEntity.LayerEffects.OrderByDescending(e => e.Order).ToList();
foreach (var layerEffectEntity in entities)
{
// Skip effects already on the element
if (effectElement.LayerEffects.Any(e => e.EntityId == layerEffectEntity.Id))
if (renderElement.LayerEffects.Any(e => e.EntityId == layerEffectEntity.Id))
continue;
// Get a matching descriptor
@@ -130,7 +129,7 @@ namespace Artemis.Core.Services
// Create the effect with dependency injection
var effect = (BaseLayerEffect) _kernel.Get(descriptor.LayerEffectType);
effect.ProfileElement = effectElement;
effect.ProfileElement = renderElement;
effect.EntityId = layerEffectEntity.Id;
effect.Order = layerEffectEntity.Order;
effect.Name = layerEffectEntity.Name;
@@ -140,9 +139,14 @@ namespace Artemis.Core.Services
effect.Initialize(this);
effect.Update(0);
effectElement.AddLayerEffect(effect);
renderElement.AddLayerEffect(effect);
_logger.Debug("Instantiated layer effect with root path {rootPath}", effect.PropertyRootPath);
}
}
public void InstantiateDisplayConditions(RenderProfileElement renderElement)
{
}
}
}