using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Artemis.Core.Events; using Artemis.Core.Exceptions; using Artemis.Core.Plugins.Models; using SkiaSharp; namespace Artemis.Core.Models.Profile.LayerProperties { /// /// Contains all the properties of the layer and provides easy access to the default properties. /// public class LayerPropertyCollection : IEnumerable { private readonly Dictionary<(Guid, string), BaseLayerProperty> _properties; internal LayerPropertyCollection(Layer layer) { _properties = new Dictionary<(Guid, string), BaseLayerProperty>(); Layer = layer; CreateDefaultProperties(); } /// /// Gets the layer these properties are applied on /// public Layer Layer { get; } /// public IEnumerator GetEnumerator() { return _properties.Values.GetEnumerator(); } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// If found, returns the matching the provided ID /// /// The type of the layer property /// The plugin this property belongs to /// /// public LayerProperty GetLayerPropertyById(PluginInfo pluginInfo, string id) { if (!_properties.ContainsKey((pluginInfo.Guid, id))) return null; var property = _properties[(pluginInfo.Guid, id)]; if (property.Type != typeof(T)) throw new ArtemisCoreException($"Property type mismatch. Expected property {property} to have type {typeof(T)} but it has {property.Type} instead."); return (LayerProperty)_properties[(pluginInfo.Guid, id)]; } /// /// Removes the provided layer property from the layer. /// /// The type of value of the layer property /// The property to remove from the layer internal void RemoveLayerProperty(LayerProperty layerProperty) { RemoveLayerProperty((BaseLayerProperty) layerProperty); } /// /// Removes the provided layer property from the layer. /// /// The property to remove from the layer internal void RemoveLayerProperty(BaseLayerProperty layerProperty) { if (!_properties.ContainsKey((layerProperty.PluginInfo.Guid, layerProperty.Id))) throw new ArtemisCoreException($"Could not find a property with ID {layerProperty.Id}."); var property = _properties[(layerProperty.PluginInfo.Guid, layerProperty.Id)]; property.Parent?.Children.Remove(property); _properties.Remove((layerProperty.PluginInfo.Guid, layerProperty.Id)); OnLayerPropertyRemoved(new LayerPropertyEventArgs(property)); } /// /// Adds the provided layer property and its children to the layer. /// If found, the last stored base value and keyframes will be applied to the provided property. /// /// The type of value of the layer property /// The property to apply to the layer /// True if an existing value was found and applied, otherwise false. internal bool RegisterLayerProperty(LayerProperty layerProperty) { return RegisterLayerProperty((BaseLayerProperty) layerProperty); } /// /// Adds the provided layer property to the layer. /// If found, the last stored base value and keyframes will be applied to the provided property. /// /// The property to apply to the layer /// True if an existing value was found and applied, otherwise false. internal bool RegisterLayerProperty(BaseLayerProperty layerProperty) { if (_properties.ContainsKey((layerProperty.PluginInfo.Guid, layerProperty.Id))) throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}."); var entity = Layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.Id == layerProperty.Id && p.ValueType == layerProperty.Type.Name); // TODO: Catch serialization exceptions and log them if (entity != null) layerProperty.ApplyToProperty(entity); _properties.Add((layerProperty.PluginInfo.Guid, layerProperty.Id), layerProperty); OnLayerPropertyRegistered(new LayerPropertyEventArgs(layerProperty)); return entity != null; } #region Default properties /// /// Gets the shape type property of the layer /// public LayerProperty ShapeType { get; private set; } /// /// Gets the fill type property of the layer /// public LayerProperty FillType { get; private set; } /// /// Gets the blend mode property of the layer /// public LayerProperty BlendMode { get; private set; } /// /// Gets the brush reference property of the layer /// public LayerProperty BrushReference { get; private set; } /// /// Gets the anchor point property of the layer /// public LayerProperty AnchorPoint { get; private set; } /// /// Gets the position of the layer /// public LayerProperty Position { get; private set; } /// /// Gets the size property of the layer /// public LayerProperty Scale { get; private set; } /// /// Gets the rotation property of the layer range 0 - 360 /// public LayerProperty Rotation { get; private set; } /// /// Gets the opacity property of the layer range 0 - 100 /// public LayerProperty Opacity { get; private set; } private void CreateDefaultProperties() { // Shape var shape = new LayerProperty(Layer, "Core.Shape", "Shape", "A collection of basic shape properties"); ShapeType = new LayerProperty(Layer, shape, "Core.ShapeType", "Shape type", "The type of shape to draw in this layer") {CanUseKeyframes = false}; FillType = new LayerProperty(Layer, shape, "Core.FillType", "Fill type", "How to make the shape adjust to scale changes") {CanUseKeyframes = false}; BlendMode = new LayerProperty(Layer, shape, "Core.BlendMode", "Blend mode", "How to blend this layer into the resulting image") {CanUseKeyframes = false}; ShapeType.Value = LayerShapeType.Rectangle; FillType.Value = LayerFillType.Stretch; BlendMode.Value = SKBlendMode.SrcOver; RegisterLayerProperty(shape); foreach (var shapeProperty in shape.Children) RegisterLayerProperty(shapeProperty); // Brush var brush = new LayerProperty(Layer, "Core.Brush", "Brush", "A collection of properties that configure the selected brush"); BrushReference = new LayerProperty(Layer, brush, "Core.BrushReference", "Brush type", "The type of brush to use for this layer") {CanUseKeyframes = false}; RegisterLayerProperty(brush); foreach (var brushProperty in brush.Children) RegisterLayerProperty(brushProperty); // Transform var transform = new LayerProperty(Layer, "Core.Transform", "Transform", "A collection of transformation properties") {ExpandByDefault = true}; AnchorPoint = new LayerProperty(Layer, transform, "Core.AnchorPoint", "Anchor Point", "The point at which the shape is attached to its position") {InputStepSize = 0.001f}; Position = new LayerProperty(Layer, transform, "Core.Position", "Position", "The position of the shape") {InputStepSize = 0.001f}; Scale = new LayerProperty(Layer, transform, "Core.Scale", "Scale", "The scale of the shape") {InputAffix = "%", MinInputValue = 0f}; Rotation = new LayerProperty(Layer, transform, "Core.Rotation", "Rotation", "The rotation of the shape in degrees") {InputAffix = "°"}; Opacity = new LayerProperty(Layer, transform, "Core.Opacity", "Opacity", "The opacity of the shape") {InputAffix = "%", MinInputValue = 0f, MaxInputValue = 100f}; Scale.Value = new SKSize(100, 100); Opacity.Value = 100; RegisterLayerProperty(transform); foreach (var transformProperty in transform.Children) RegisterLayerProperty(transformProperty); } #endregion #region Events public event EventHandler LayerPropertyRegistered; public event EventHandler LayerPropertyRemoved; private void OnLayerPropertyRegistered(LayerPropertyEventArgs e) { LayerPropertyRegistered?.Invoke(this, e); } private void OnLayerPropertyRemoved(LayerPropertyEventArgs e) { LayerPropertyRemoved?.Invoke(this, e); } #endregion } }