using System; 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 Artemis.Storage.Entities.Profile.Abstract; using SkiaSharp; namespace Artemis.Core.Models.Profile { public abstract class RenderProfileElement : ProfileElement { #region Properties private SKPath _path; internal abstract RenderElementEntity RenderElementEntity { get; } /// /// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is /// clipped. /// 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; } } /// /// The bounds of this entity /// public SKRect Bounds { get => _bounds; private set => SetAndNotify(ref _bounds, value); } #region Property group expansion protected List _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 _layerEffects; /// /// Gets a read-only collection of the layer effects on this entity /// public ReadOnlyCollection LayerEffects => _layerEffects.AsReadOnly(); protected void ApplyLayerEffectsToEntity() { RenderElementEntity.LayerEffects.Clear(); foreach (var layerEffect in LayerEffects) { var layerEffectEntity = new LayerEffectEntity { Id = layerEffect.EntityId, PluginGuid = layerEffect.PluginInfo.Guid, EffectType = layerEffect.GetType().Name, Name = layerEffect.Name, Enabled = layerEffect.Enabled, HasBeenRenamed = layerEffect.HasBeenRenamed, Order = layerEffect.Order }; RenderElementEntity.LayerEffects.Add(layerEffectEntity); layerEffect.BaseProperties.ApplyToEntity(); } } internal void RemoveLayerEffect([NotNull] BaseLayerEffect effect) { if (effect == null) throw new ArgumentNullException(nameof(effect)); DeactivateLayerEffect(effect); // Update the order on the remaining effects var index = 0; foreach (var baseLayerEffect in LayerEffects.OrderBy(e => e.Order)) { baseLayerEffect.Order = Order = index + 1; index++; } OnLayerEffectsUpdated(); } internal void AddLayerEffect([NotNull] BaseLayerEffect effect) { if (effect == null) throw new ArgumentNullException(nameof(effect)); _layerEffects.Add(effect); OnLayerEffectsUpdated(); } internal void DeactivateLayerEffect([NotNull] BaseLayerEffect effect) { if (effect == null) throw new ArgumentNullException(nameof(effect)); // Remove the effect from the layer and dispose it _layerEffects.Remove(effect); effect.Dispose(); } #endregion #region Conditions private DisplayConditionGroup _displayConditionGroup; /// /// Gets or sets the root display condition group /// public DisplayConditionGroup DisplayConditionGroup { get => _displayConditionGroup; set => SetAndNotify(ref _displayConditionGroup, value); } #endregion #region Events public event EventHandler LayerEffectsUpdated; internal void OnLayerEffectsUpdated() { LayerEffectsUpdated?.Invoke(this, EventArgs.Empty); } #endregion } }