diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 4a90f4988..64bf4f094 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -212,6 +212,7 @@ + diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 15ec80610..099a29e7c 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -6,6 +6,7 @@ using Artemis.Core.Extensions; using Artemis.Core.Models.Profile.Abstract; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.LayerElement; +using Artemis.Core.Services.Interfaces; using Artemis.Storage.Entities.Profile; using Newtonsoft.Json; using SkiaSharp; @@ -61,7 +62,7 @@ namespace Artemis.Core.Models.Profile public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas) { - if (RenderRectangle == null || AbsoluteRenderRectangle == null || RenderPath == null) + if (RenderPath == null) return; canvas.Save(); @@ -127,6 +128,7 @@ namespace Artemis.Core.Models.Profile { var layerElementEntity = new LayerElementEntity { + Id = layerElement.Guid, PluginGuid = layerElement.Descriptor.LayerElementProvider.PluginInfo.Guid, LayerElementType = layerElement.GetType().Name, Configuration = JsonConvert.SerializeObject(layerElement.Settings) @@ -164,7 +166,12 @@ namespace Artemis.Core.Models.Profile _layerElements.Add(layerElement); } - public void ApplySurface(ArtemisSurface surface) + internal void RemoveLayerElement(LayerElement layerElement) + { + _layerElements.Remove(layerElement); + } + + internal void PopulateLeds(ArtemisSurface surface) { var leds = new List(); @@ -181,14 +188,11 @@ namespace Artemis.Core.Models.Profile _leds = leds; CalculateRenderProperties(); } - + internal void CalculateRenderProperties() { if (!Leds.Any()) - { - // TODO: Create an empty rectangle and path return; - } // Determine to top-left and bottom-right var minX = Leds.Min(l => l.AbsoluteRenderRectangle.Left); diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs index beea80582..640e7c4a8 100644 --- a/src/Artemis.Core/Models/Profile/Profile.cs +++ b/src/Artemis.Core/Models/Profile/Profile.cs @@ -85,7 +85,6 @@ namespace Artemis.Core.Models.Profile ProfileEntity.Layers.AddRange(GetAllLayers().Select(f => f.LayerEntity)); } - internal void Activate(ArtemisSurface surface) { lock (this) @@ -93,7 +92,7 @@ namespace Artemis.Core.Models.Profile if (IsActivated) return; - ApplySurface(surface); + PopulateLeds(surface); OnActivated(); IsActivated = true; } @@ -115,10 +114,10 @@ namespace Artemis.Core.Models.Profile return $"{nameof(Order)}: {Order}, {nameof(Name)}: {Name}, {nameof(PluginInfo)}: {PluginInfo}"; } - public void ApplySurface(ArtemisSurface surface) + internal void PopulateLeds(ArtemisSurface surface) { foreach (var layer in GetAllLayers()) - layer.ApplySurface(surface); + layer.PopulateLeds(surface); } #region Events diff --git a/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs b/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs index 2ec7a5d41..6eea5d068 100644 --- a/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs +++ b/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs @@ -1,19 +1,22 @@ -using Artemis.Core.Models.Profile; +using System; +using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using SkiaSharp; namespace Artemis.Core.Plugins.LayerElement { - public abstract class LayerElement + public abstract class LayerElement : IDisposable { - protected LayerElement(Layer layer, LayerElementSettings settings, LayerElementDescriptor descriptor) + protected LayerElement(Layer layer, Guid guid, LayerElementSettings settings, LayerElementDescriptor descriptor) { Layer = layer; + Guid = guid; Settings = settings; Descriptor = descriptor; } public Layer Layer { get; } + public Guid Guid { get; } public LayerElementSettings Settings { get; } public LayerElementDescriptor Descriptor { get; } @@ -65,5 +68,9 @@ namespace Artemis.Core.Plugins.LayerElement { return shader; } + + public virtual void Dispose() + { + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/LayerElement/LayerElementSettings.cs b/src/Artemis.Core/Plugins/LayerElement/LayerElementSettings.cs index c7bc34236..2d19bc172 100644 --- a/src/Artemis.Core/Plugins/LayerElement/LayerElementSettings.cs +++ b/src/Artemis.Core/Plugins/LayerElement/LayerElementSettings.cs @@ -1,8 +1,21 @@ -using Stylet; +using System; +using Stylet; namespace Artemis.Core.Plugins.LayerElement { public abstract class LayerElementSettings : PropertyChangedBase { + private Action _propertyChangedDispatcher = Execute.DefaultPropertyChangedDispatcher; + + /// + /// Gets or sets the dispatcher to use to dispatch PropertyChanged events. Defaults to Execute.DefaultPropertyChangedDispatcher + /// + [System.Xml.Serialization.XmlIgnore] + [Newtonsoft.Json.JsonIgnore] + public override Action PropertyChangedDispatcher + { + get { return this._propertyChangedDispatcher; } + set { this._propertyChangedDispatcher = value; } + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Interfaces/ILayerService.cs b/src/Artemis.Core/Services/Interfaces/ILayerService.cs index a5da4b0a9..ff0bcd19c 100644 --- a/src/Artemis.Core/Services/Interfaces/ILayerService.cs +++ b/src/Artemis.Core/Services/Interfaces/ILayerService.cs @@ -1,4 +1,5 @@ -using Artemis.Core.Models.Profile; +using System; +using Artemis.Core.Models.Profile; using Artemis.Core.Plugins.LayerElement; namespace Artemis.Core.Services.Interfaces @@ -13,6 +14,8 @@ namespace Artemis.Core.Services.Interfaces /// The descriptor of the new layer element /// JSON settings to be deserialized and injected into the layer element /// - LayerElement InstantiateLayerElement(Layer layer, LayerElementDescriptor layerElementDescriptor, string settings = null); + LayerElement InstantiateLayerElement(Layer layer, LayerElementDescriptor layerElementDescriptor, string settings = null, Guid? guid = null); + + void RemoveLayerElement(Layer layer, LayerElement layerElement); } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/LayerService.cs b/src/Artemis.Core/Services/LayerService.cs index 0fca51b5e..3ad30d676 100644 --- a/src/Artemis.Core/Services/LayerService.cs +++ b/src/Artemis.Core/Services/LayerService.cs @@ -1,4 +1,7 @@ -using Artemis.Core.Models.Profile; +using System; +using System.Linq; +using System.Reflection; +using Artemis.Core.Models.Profile; using Artemis.Core.Plugins.Exceptions; using Artemis.Core.Plugins.LayerElement; using Artemis.Core.Services.Interfaces; @@ -17,28 +20,41 @@ namespace Artemis.Core.Services _kernel = kernel; } - public LayerElement InstantiateLayerElement(Layer layer, LayerElementDescriptor layerElementDescriptor, string settings) + public LayerElement InstantiateLayerElement(Layer layer, LayerElementDescriptor layerElementDescriptor, string settings, Guid? guid) { - // Deserialize the settings, if provided - object deserializedSettings = null; - if (settings != null) + if (guid == null) + guid = Guid.NewGuid(); + + // Determine the settings type declared by the layer element + object settingsInstance = null; + var properties = layerElementDescriptor.LayerElementType.GetProperties(); + var settingsType = properties.FirstOrDefault(p => p.Name == "Settings" && + p.DeclaringType == layerElementDescriptor.LayerElementType)?.PropertyType; + + // Deserialize the settings if provided, check for null in JSON as well + if (settings != null && settings != "null") { - var settingsType = layerElementDescriptor.LayerElementType.GetProperty(nameof(LayerElement.Settings))?.PropertyType; + // Setting where provided but no settings type was found, something is wrong if (settingsType == null) { throw new ArtemisPluginException( layerElementDescriptor.LayerElementProvider.PluginInfo, - $"Layer element of type {layerElementDescriptor.LayerElementType} has no Settings property." + $"Settings where provided but layer element of type {layerElementDescriptor.LayerElementType.Name} has no Settings property." ); } - - deserializedSettings = JsonConvert.DeserializeObject(settings, settingsType); + settingsInstance = JsonConvert.DeserializeObject(settings, settingsType); + } + // If no settings found, provide a fresh instance of the settings type + else if (settingsType != null) + { + settingsInstance = Activator.CreateInstance(settingsType); } var arguments = new IParameter[] { new ConstructorArgument("layer", layer), - new ConstructorArgument("settings", deserializedSettings), + new ConstructorArgument("guid", guid.Value), + new ConstructorArgument("settings", settingsInstance), new ConstructorArgument("descriptor", layerElementDescriptor) }; var layerElement = (LayerElement) _kernel.Get(layerElementDescriptor.LayerElementType, arguments); @@ -46,5 +62,11 @@ namespace Artemis.Core.Services return layerElement; } + + public void RemoveLayerElement(Layer layer, LayerElement layerElement) + { + layer.RemoveLayerElement(layerElement); + layerElement.Dispose(); + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 59d8de400..7a85598bb 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -4,6 +4,7 @@ using Artemis.Core.Events; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.Abstract; +using Artemis.Core.Plugins.LayerElement; using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Storage.Interfaces; using Artemis.Storage.Repositories.Interfaces; @@ -18,17 +19,21 @@ namespace Artemis.Core.Services.Storage private readonly IPluginService _pluginService; private readonly IProfileRepository _profileRepository; private readonly ISurfaceService _surfaceService; + private readonly ILayerService _layerService; - internal ProfileService(IPluginService pluginService, ISurfaceService surfaceService, IProfileRepository profileRepository) + internal ProfileService(IPluginService pluginService, ISurfaceService surfaceService, ILayerService layerService, IProfileRepository profileRepository) { _pluginService = pluginService; _surfaceService = surfaceService; + _layerService = layerService; _profileRepository = profileRepository; - _surfaceService.ActiveSurfaceConfigurationChanged += SurfaceServiceOnActiveSurfaceConfigurationChanged; - _surfaceService.SurfaceConfigurationUpdated += SurfaceServiceOnSurfaceConfigurationUpdated; + _surfaceService.ActiveSurfaceConfigurationChanged += OnActiveSurfaceConfigurationChanged; + _surfaceService.SurfaceConfigurationUpdated += OnSurfaceConfigurationUpdated; + _pluginService.PluginLoaded += OnPluginLoaded; } + public List GetProfiles(ProfileModule module) { var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); @@ -64,7 +69,7 @@ namespace Artemis.Core.Services.Storage _profileRepository.Add(profile.ProfileEntity); if (_surfaceService.ActiveSurface != null) - profile.ApplySurface(_surfaceService.ActiveSurface); + profile.PopulateLeds(_surfaceService.ActiveSurface); return profile; } @@ -72,6 +77,7 @@ namespace Artemis.Core.Services.Storage public void ActivateProfile(ProfileModule module, Profile profile) { module.ChangeActiveProfile(profile, _surfaceService.ActiveSurface); + InstantiateProfileLayerElements(profile); } public void DeleteProfile(Profile profile) @@ -90,29 +96,68 @@ namespace Artemis.Core.Services.Storage layer.ApplyToEntity(); if (_surfaceService.ActiveSurface != null) - profile.ApplySurface(_surfaceService.ActiveSurface); + profile.PopulateLeds(_surfaceService.ActiveSurface); } _profileRepository.Save(profile.ProfileEntity); } - - private void SurfaceServiceOnActiveSurfaceConfigurationChanged(object sender, SurfaceConfigurationEventArgs e) + private void InstantiateProfileLayerElements(Profile profile) { - ApplySurfaceToProfiles(e.Surface); + var layerElementProviders = _pluginService.GetPluginsOfType(); + var descriptors = layerElementProviders.SelectMany(l => l.LayerElementDescriptors).ToList(); + + foreach (var layer in profile.GetAllLayers()) + { + foreach (var elementEntity in layer.LayerEntity.Elements) + { + // Skip already instantiated layer elements + if (layer.LayerElements.Any(e => e.Guid == elementEntity.Id)) + continue; + + // Get a matching descriptor + var descriptor = descriptors.FirstOrDefault(d => d.LayerElementProvider.PluginInfo.Guid == elementEntity.PluginGuid && + d.LayerElementType.Name == elementEntity.LayerElementType); + + // If a descriptor that matches if found, instantiate it with the GUID of the element entity + if (descriptor != null) + _layerService.InstantiateLayerElement(layer, descriptor, elementEntity.Configuration, elementEntity.Id); + } + } } - private void SurfaceServiceOnSurfaceConfigurationUpdated(object sender, SurfaceConfigurationEventArgs e) - { - if (!e.Surface.IsActive) - return; - ApplySurfaceToProfiles(e.Surface); - } - - private void ApplySurfaceToProfiles(ArtemisSurface surface) + private void ActiveProfilesPopulateLeds(ArtemisSurface surface) { var profileModules = _pluginService.GetPluginsOfType(); foreach (var profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList()) - profileModule.ActiveProfile.ApplySurface(surface); + profileModule.ActiveProfile.PopulateLeds(surface); } + + private void ActiveProfilesInstantiateProfileLayerElements() + { + var profileModules = _pluginService.GetPluginsOfType(); + foreach (var profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList()) + InstantiateProfileLayerElements(profileModule.ActiveProfile); + } + + #region Event handlers + + private void OnActiveSurfaceConfigurationChanged(object sender, SurfaceConfigurationEventArgs e) + { + ActiveProfilesPopulateLeds(e.Surface); + } + + private void OnSurfaceConfigurationUpdated(object sender, SurfaceConfigurationEventArgs e) + { + if (e.Surface.IsActive) + ActiveProfilesPopulateLeds(e.Surface); + } + + private void OnPluginLoaded(object sender, PluginEventArgs e) + { + if (e.PluginInfo.Instance is LayerElementProvider) + ActiveProfilesInstantiateProfileLayerElements(); + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Utilities/EnumUtilities.cs b/src/Artemis.Core/Utilities/EnumUtilities.cs new file mode 100644 index 000000000..94b8546ba --- /dev/null +++ b/src/Artemis.Core/Utilities/EnumUtilities.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; + +namespace Artemis.Core.Utilities +{ + public static class EnumUtilities + { + public static string Description(this Enum value) + { + var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); + if (attributes.Any()) + return (attributes.First() as DescriptionAttribute).Description; + + // If no description is found, the least we can do is replace underscores with spaces + // You can add your own custom default formatting logic here + var ti = CultureInfo.CurrentCulture.TextInfo; + return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " "))); + } + + public static IEnumerable GetAllValuesAndDescriptions(Type t) + { + if (!t.IsEnum) + throw new ArgumentException($"{nameof(t)} must be an enum type"); + + return Enum.GetValues(t).Cast().Select(e => new ValueDescription {Value = e, Description = e.Description()}).ToList(); + } + } + + public class ValueDescription + { + public object Value { get; set; } + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs b/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs index ade9daa5f..89dac09c2 100644 --- a/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs +++ b/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs @@ -1,4 +1,5 @@ -using Artemis.Core.Models.Profile; +using System; +using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.LayerElement; using SkiaSharp; @@ -7,7 +8,7 @@ namespace Artemis.Plugins.LayerElements.Animations { public class RotationLayerElement : LayerElement { - public RotationLayerElement(Layer layer, LayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, settings, descriptor) + public RotationLayerElement(Layer layer, Guid guid, LayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, guid, settings, descriptor) { } @@ -20,17 +21,20 @@ namespace Artemis.Plugins.LayerElements.Animations public override void Update(double deltaTime) { - Rotation += (float)(deltaTime * 100); + Rotation += (float) (deltaTime * 100); if (Rotation > 360) Rotation = 0; } public override SKShader RenderPostProcess(ArtemisSurface surface, SKBitmap bitmap, SKShader shader) { - var center = new SKPoint(Layer.AbsoluteRenderRectangle.MidX, Layer.AbsoluteRenderRectangle.MidY); - - // TODO Scale so that the rectangle is covered in every rotation, instead of just putting it at 2 - return SKShader.CreateLocalMatrix(SKShader.CreateLocalMatrix(shader, SKMatrix.MakeScale(2, 2, center.X, center.Y)), SKMatrix.MakeRotationDegrees(Rotation, center.X, center.Y)); + var rect = Layer.AbsoluteRenderRectangle; + var center = new SKPoint(rect.MidX, rect.MidY); + + var required = (float) Math.Sqrt(rect.Width * rect.Width + rect.Height * rect.Height); + var minSide = Math.Min(rect.Width, rect.Height); + var scale = required / minSide; + return SKShader.CreateLocalMatrix(SKShader.CreateLocalMatrix(shader, SKMatrix.MakeScale(scale, scale, center.X, center.Y)), SKMatrix.MakeRotationDegrees(Rotation, center.X, center.Y)); } } } \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs b/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs index 17f308e93..8f471d319 100644 --- a/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs +++ b/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs @@ -1,4 +1,5 @@ -using Artemis.Core.Models.Profile; +using System; +using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.LayerElement; using SkiaSharp; @@ -7,7 +8,7 @@ namespace Artemis.Plugins.LayerElements.Animations { public class SlideLayerElement : LayerElement { - public SlideLayerElement(Layer layer, LayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, settings, descriptor) + public SlideLayerElement(Layer layer, Guid guid, LayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, guid, settings, descriptor) { } diff --git a/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj b/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj index 7ef80e783..9eb1b9e6a 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj +++ b/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj @@ -113,6 +113,7 @@ MSBuild:Compile + echo Copying plugin to Artemis.UI output directory diff --git a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs index 607ed2f86..cc25b86de 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs +++ b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.LayerElement; @@ -11,9 +13,9 @@ namespace Artemis.Plugins.LayerElements.Brush private SKShader _shader; private List _testColors; - public BrushLayerElement(Layer layer, BrushLayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, settings, descriptor) + public BrushLayerElement(Layer layer, Guid guid, BrushLayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, guid, settings, descriptor) { - Settings = settings ?? new BrushLayerElementSettings(); + Settings = settings; _testColors = new List(); for (var i = 0; i < 9; i++) @@ -26,13 +28,31 @@ namespace Artemis.Plugins.LayerElements.Brush CreateShader(); Layer.RenderPropertiesUpdated += (sender, args) => CreateShader(); + Settings.PropertyChanged += (sender, args) => CreateShader(); } private void CreateShader() { var center = new SKPoint(Layer.AbsoluteRenderRectangle.MidX, Layer.AbsoluteRenderRectangle.MidY); - var shader = SKShader.CreateSweepGradient(center, _testColors.ToArray(), null, SKShaderTileMode.Clamp, 0, 360); - + SKShader shader; + switch (Settings.BrushType) + { + case BrushType.Solid: + shader = SKShader.CreateColor(_testColors.First()); + break; + case BrushType.LinearGradient: + shader = SKShader.CreateLinearGradient(new SKPoint(0, 0), new SKPoint(Layer.AbsoluteRenderRectangle.Width, 0), _testColors.ToArray(), SKShaderTileMode.Clamp); + break; + case BrushType.RadialGradient: + shader = SKShader.CreateRadialGradient(center, Math.Min(Layer.AbsoluteRenderRectangle.Width, Layer.AbsoluteRenderRectangle.Height), _testColors.ToArray(), SKShaderTileMode.Clamp); + break; + case BrushType.SweepGradient: + shader = SKShader.CreateSweepGradient(center, _testColors.ToArray(), null, SKShaderTileMode.Clamp, 0, 360); + break; + default: + throw new ArgumentOutOfRangeException(); + } + var oldShader = _shader; _shader = shader; diff --git a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementSettings.cs b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementSettings.cs index e2d40a752..ddaae693e 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementSettings.cs +++ b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementSettings.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel; using Artemis.Core.Plugins.LayerElement; using SkiaSharp; @@ -6,11 +7,40 @@ namespace Artemis.Plugins.LayerElements.Brush { public class BrushLayerElementSettings : LayerElementSettings { + private BrushType _brushType; + private List _colors; + public BrushLayerElementSettings() { + BrushType = BrushType.Solid; Colors = new List(); } - public List Colors { get; set; } + public BrushType BrushType + { + get => _brushType; + set => SetAndNotify(ref _brushType, value); + } + + public List Colors + { + get => _colors; + set => SetAndNotify(ref _colors, value); + } + } + + public enum BrushType + { + [Description("Solid")] + Solid, + + [Description("Linear Gradient")] + LinearGradient, + + [Description("Radial Gradient")] + RadialGradient, + + [Description("Sweep Gradient")] + SweepGradient } } \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementView.xaml b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementView.xaml index 8f7aa534d..b9bb5e796 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementView.xaml +++ b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementView.xaml @@ -20,35 +20,116 @@ - - - - + + - - - Setting title - Setting subtitle - - - - - + + + + + + + + + + + + Brush type + + + + + + + + + + + + + + + + + + + Setting title + Setting subtitle + + + + + + + + + + + + + + + + + + + Setting title + Setting subtitle + + + + + + - - Setting title - Setting subtitle - - - - - + + + + + + + + + + + Setting title + Setting subtitle + + + + + + + + + + + + + + + + + + + Setting title + Setting subtitle + + + + + + \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementViewModel.cs b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementViewModel.cs index ba3dab6a9..f47bdaf83 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementViewModel.cs +++ b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElementViewModel.cs @@ -1,4 +1,6 @@ -using Artemis.Core.Plugins.LayerElement; +using System.Collections.Generic; +using Artemis.Core.Plugins.LayerElement; +using Artemis.Core.Utilities; namespace Artemis.Plugins.LayerElements.Brush { @@ -10,5 +12,6 @@ namespace Artemis.Plugins.LayerElements.Brush } public new BrushLayerElement LayerElement { get; } + public IEnumerable BrushTypes => EnumUtilities.GetAllValuesAndDescriptions(typeof(BrushType)); } } \ No newline at end of file diff --git a/src/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Artemis.Plugins.Modules.General/GeneralModule.cs index c732dfd12..8d91df18a 100644 --- a/src/Artemis.Plugins.Modules.General/GeneralModule.cs +++ b/src/Artemis.Plugins.Modules.General/GeneralModule.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.Models; diff --git a/src/Artemis.Storage/Entities/Profile/LayerElementEntity.cs b/src/Artemis.Storage/Entities/Profile/LayerElementEntity.cs index c1dac0d09..d05fda48a 100644 --- a/src/Artemis.Storage/Entities/Profile/LayerElementEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/LayerElementEntity.cs @@ -4,6 +4,8 @@ namespace Artemis.Storage.Entities.Profile { public class LayerElementEntity { + public Guid Id { get; set; } + public Guid PluginGuid { get; set; } public string LayerElementType { get; set; } public string Configuration { get; set; } diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index d9a6e68e6..94e5414a0 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -172,6 +172,7 @@ + diff --git a/src/Artemis.UI/Converters/EnumToCollectionConverter.cs b/src/Artemis.UI/Converters/EnumToCollectionConverter.cs new file mode 100644 index 000000000..8ea483dd6 --- /dev/null +++ b/src/Artemis.UI/Converters/EnumToCollectionConverter.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Windows.Data; +using System.Windows.Markup; + +namespace Artemis.UI.Converters +{ + [ValueConversion(typeof(Enum), typeof(IEnumerable>))] + public class EnumToCollectionConverter : MarkupExtension, IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return GetAllValuesAndDescriptions(value.GetType()); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return null; + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + return this; + } + + private static string Description(Enum value) + { + var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); + if (attributes.Any()) + return (attributes.First() as DescriptionAttribute)?.Description; + + // If no description is found, the least we can do is replace underscores with spaces + var ti = CultureInfo.CurrentCulture.TextInfo; + return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " "))); + } + + private static IEnumerable> GetAllValuesAndDescriptions(Type t) + { + if (!t.IsEnum) + throw new ArgumentException($"{nameof(t)} must be an enum type"); + + return Enum.GetValues(t).Cast().Select(e => new Tuple(e, Description(e))).ToList(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesView.xaml index 43477e48e..118740370 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesView.xaml @@ -19,7 +19,9 @@ - + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesViewModel.cs index e72e0e612..0ec5ab7c5 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ElementProperties/ElementPropertiesViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using Artemis.Core.Plugins.LayerElement; using Artemis.UI.Services.Interfaces; @@ -19,7 +20,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ElementProperties private void OnSelectedLayerElementChanged(object sender, EventArgs e) { + if (LayerElementViewModel?.LayerElement?.Settings != null) + LayerElementViewModel.LayerElement.Settings.PropertyChanged -= SettingsOnPropertyChanged; + LayerElementViewModel = _profileEditorService.SelectedLayerElement?.GetViewModel(); + + if (LayerElementViewModel?.LayerElement?.Settings != null) + LayerElementViewModel.LayerElement.Settings.PropertyChanged += SettingsOnPropertyChanged; + } + + private void SettingsOnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + _profileEditorService.UpdateSelectedProfileElement(); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/Dialogs/AddLayerElementViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/Dialogs/AddLayerElementViewModel.cs index 9c4aec276..eb1af8461 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/Dialogs/AddLayerElementViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/Dialogs/AddLayerElementViewModel.cs @@ -31,12 +31,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerElements.Dialogs public void Accept() { + if (Session.IsEnded) + return; var layerElement = _layerService.InstantiateLayerElement(Layer, SelectedLayerElementDescriptor); Session.Close(layerElement); } public void Cancel() { + if (Session.IsEnded) + return; Session.Close(); } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/LayerElementsViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/LayerElementsViewModel.cs index 815e67af3..93ded24a4 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/LayerElementsViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerElements/LayerElementsViewModel.cs @@ -4,6 +4,7 @@ using System.Linq; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile.Abstract; using Artemis.Core.Plugins.LayerElement; +using Artemis.Core.Services.Interfaces; using Artemis.UI.Screens.Module.ProfileEditor.LayerElements.Dialogs; using Artemis.UI.Services.Interfaces; using Stylet; @@ -14,11 +15,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerElements { private readonly IDialogService _dialogService; private readonly IProfileEditorService _profileEditorService; + private readonly ILayerService _layerService; private LayerElementViewModel _selectedLayerElement; - public LayerElementsViewModel(IProfileEditorService profileEditorService, IDialogService dialogService) + public LayerElementsViewModel(IProfileEditorService profileEditorService, ILayerService layerService, IDialogService dialogService) { _profileEditorService = profileEditorService; + _layerService = layerService; _dialogService = dialogService; LayerElements = new BindableCollection(); @@ -74,8 +77,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerElements new Dictionary {{"layer", (Layer) SelectedProfileElement}} ); - if (result is LayerElement layerElement) - LayerElements.Add(new LayerElementViewModel(layerElement)); + if (!(result is LayerElement layerElement)) + return; + + LayerElements.Add(new LayerElementViewModel(layerElement)); + _profileEditorService.UpdateSelectedProfileElement(); } public async void DeleteSelectedLayerElement() @@ -88,8 +94,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerElements "Delete layer element", "Are you sure you want to delete the selected layer element?" ); + if (!result) return; + + var layerElement = SelectedLayerElement.LayerElement; + var layer = (Layer) SelectedProfileElement; + + LayerElements.Remove(SelectedLayerElement); + SelectedLayerElement = null; + + _layerService.RemoveLayerElement(layer, layerElement); + _profileEditorService.UpdateSelectedProfileElement(); } } } \ No newline at end of file