From 60df649eb077cc22a03acba6741f2bb3d2d528eb Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 6 Apr 2021 20:56:18 +0200 Subject: [PATCH] Brush presets - Reset properties to default before applying preset Layer properties - Disable keyframes when resetting to default Device dialogs - Only dim unselected LEDs if a LED is selected Migrations - Renamed to stay in order Migrations - Added migration for color gradients --- .../Profile/LayerProperties/ILayerProperty.cs | 5 + .../Profile/LayerProperties/LayerProperty.cs | 298 +++++++++--------- .../Models/Profile/LayerPropertyGroup.cs | 113 +++---- src/Artemis.Core/Resources/intro-profile.json | 12 +- src/Artemis.Core/Services/CoreService.cs | 16 +- src/Artemis.Core/Utilities/IntroAnimation.cs | 1 + ...M0001AttributeBasedPropertiesMigration.cs} | 2 +- ...> M0002ProfileEntitiesEnabledMigration.cs} | 2 +- ...003PluginEntitiesIndexChangesMigration.cs} | 2 +- ...ileSegments.cs => M0004ProfileSegments.cs} | 2 +- ...ndingTypes.cs => M0005DataBindingTypes.cs} | 2 +- ...action.cs => M0006PredicateAbstraction.cs} | 2 +- ...uginFeatures.cs => M0008PluginFeatures.cs} | 2 +- ...libration.cs => M0009DeviceCalibration.cs} | 2 +- ...Bindings.cs => M0010BetterDataBindings.cs} | 2 +- .../Migrations/M0011ColorGradients.cs | 42 +++ .../Services/ProfileEditorService.cs | 162 +++++----- .../Dialogs/LayerBrushPresetViewModel.cs | 3 + .../Tree/TreePropertyViewModel.cs | 2 +- .../Visualization/ProfileViewModel.cs | 3 +- .../Settings/Device/DeviceDialogViewModel.cs | 2 +- .../Steps/LayoutStepViewModel.cs | 18 ++ 22 files changed, 376 insertions(+), 319 deletions(-) rename src/Artemis.Storage/Migrations/{M1AttributeBasedPropertiesMigration.cs => M0001AttributeBasedPropertiesMigration.cs} (86%) rename src/Artemis.Storage/Migrations/{M2ProfileEntitiesEnabledMigration.cs => M0002ProfileEntitiesEnabledMigration.cs} (94%) rename src/Artemis.Storage/Migrations/{M3PluginEntitiesIndexChangesMigration.cs => M0003PluginEntitiesIndexChangesMigration.cs} (89%) rename src/Artemis.Storage/Migrations/{M4ProfileSegments.cs => M0004ProfileSegments.cs} (96%) rename src/Artemis.Storage/Migrations/{M5DataBindingTypes.cs => M0005DataBindingTypes.cs} (94%) rename src/Artemis.Storage/Migrations/{M6PredicateAbstraction.cs => M0006PredicateAbstraction.cs} (96%) rename src/Artemis.Storage/Migrations/{M8PluginFeatures.cs => M0008PluginFeatures.cs} (99%) rename src/Artemis.Storage/Migrations/{M9DeviceCalibration.cs => M0009DeviceCalibration.cs} (92%) rename src/Artemis.Storage/Migrations/{M10BetterDataBindings.cs => M0010BetterDataBindings.cs} (97%) create mode 100644 src/Artemis.Storage/Migrations/M0011ColorGradients.cs diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs index 2b4202cb9..c9bdf5872 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs @@ -59,6 +59,11 @@ namespace Artemis.Core /// If succeeded the resulting keyframe, otherwise ILayerPropertyKeyframe? AddKeyframeEntity(KeyframeEntity keyframeEntity); + /// + /// Overrides the property value with the default value + /// + void ApplyDefaultValue(); + /// /// Updates the layer properties internal state /// diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index c35bc401d..afe35710d 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Linq.Expressions; using Artemis.Storage.Entities.Profile; using Newtonsoft.Json; @@ -39,6 +38,107 @@ namespace Artemis.Core _keyframes = new List>(); } + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + + foreach (IDataBinding dataBinding in _dataBindings) + dataBinding.Dispose(); + + Disposed?.Invoke(this, EventArgs.Empty); + } + } + + /// + /// Invokes the event + /// + protected virtual void OnUpdated() + { + Updated?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnCurrentValueSet() + { + CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this)); + LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnVisibilityChanged() + { + VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnKeyframesToggled() + { + KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnKeyframeAdded() + { + KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnKeyframeRemoved() + { + KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnDataBindingPropertyRegistered() + { + DataBindingPropertyRegistered?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnDataBindingPropertiesCleared() + { + DataBindingPropertiesCleared?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e) + { + DataBindingEnabled?.Invoke(this, e); + } + + /// + /// Invokes the event + /// + protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e) + { + DataBindingDisabled?.Invoke(this, e); + } + /// public PropertyDescriptionAttribute PropertyDescription { get; internal set; } @@ -62,28 +162,6 @@ namespace Artemis.Core OnUpdated(); } - #region IDisposable - - /// - /// Releases the unmanaged resources used by the object and optionally releases the managed resources. - /// - /// - /// to release both managed and unmanaged resources; - /// to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _disposed = true; - - foreach (IDataBinding dataBinding in _dataBindings) - dataBinding.Dispose(); - - Disposed?.Invoke(this, EventArgs.Empty); - } - } - /// public void Dispose() { @@ -91,7 +169,38 @@ namespace Artemis.Core GC.SuppressFinalize(this); } - #endregion + /// + public event EventHandler? Disposed; + + /// + public event EventHandler? Updated; + + /// + public event EventHandler? CurrentValueSet; + + /// + public event EventHandler? VisibilityChanged; + + /// + public event EventHandler? KeyframesToggled; + + /// + public event EventHandler? KeyframeAdded; + + /// + public event EventHandler? KeyframeRemoved; + + /// + public event EventHandler? DataBindingPropertyRegistered; + + /// + public event EventHandler? DataBindingPropertiesCleared; + + /// + public event EventHandler? DataBindingEnabled; + + /// + public event EventHandler? DataBindingDisabled; #region Hierarchy @@ -195,22 +304,25 @@ namespace Artemis.Core ReapplyUpdate(); } - /// - /// Overrides the property value with the default value - /// - public void ApplyDefaultValue(TimeSpan? time) + /// + public void ApplyDefaultValue() { if (_disposed) throw new ObjectDisposedException("LayerProperty"); string json = CoreJson.SerializeObject(DefaultValue, true); - SetCurrentValue(CoreJson.DeserializeObject(json)!, time); + KeyframesEnabled = false; + SetCurrentValue(CoreJson.DeserializeObject(json)!, null); } private void ReapplyUpdate() { - ProfileElement.Timeline.ClearDelta(); - Update(ProfileElement.Timeline); + // Create a timeline with the same position but a delta of zero + Timeline temporaryTimeline = new(); + temporaryTimeline.Override(ProfileElement.Timeline.Position, false); + temporaryTimeline.ClearDelta(); + + Update(temporaryTimeline); OnCurrentValueSet(); } @@ -440,10 +552,10 @@ namespace Artemis.Core if (_disposed) throw new ObjectDisposedException("LayerProperty"); - foreach (IDataBindingRegistration dataBindingRegistration in _dataBindingRegistrations) + foreach (IDataBindingRegistration dataBindingRegistration in _dataBindingRegistrations) dataBindingRegistration.ClearDataBinding(); _dataBindingRegistrations.Clear(); - + OnDataBindingPropertiesCleared(); } @@ -593,7 +705,7 @@ namespace Artemis.Core throw new ArtemisCoreException("Layer property is not yet initialized"); if (!IsLoadedFromStorage) - ApplyDefaultValue(null); + ApplyDefaultValue(); else try { @@ -657,123 +769,5 @@ namespace Artemis.Core } #endregion - - #region Events - - /// - public event EventHandler? Disposed; - - /// - public event EventHandler? Updated; - - /// - public event EventHandler? CurrentValueSet; - - /// - public event EventHandler? VisibilityChanged; - - /// - public event EventHandler? KeyframesToggled; - - /// - public event EventHandler? KeyframeAdded; - - /// - public event EventHandler? KeyframeRemoved; - - /// - public event EventHandler? DataBindingPropertyRegistered; - - /// - public event EventHandler? DataBindingPropertiesCleared; - - /// - public event EventHandler? DataBindingEnabled; - - /// - public event EventHandler? DataBindingDisabled; - - /// - /// Invokes the event - /// - protected virtual void OnUpdated() - { - Updated?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnCurrentValueSet() - { - CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this)); - LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnVisibilityChanged() - { - VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnKeyframesToggled() - { - KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnKeyframeAdded() - { - KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnKeyframeRemoved() - { - KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingPropertyRegistered() - { - DataBindingPropertyRegistered?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingPropertiesCleared() - { - DataBindingPropertiesCleared?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e) - { - DataBindingEnabled?.Invoke(this, e); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e) - { - DataBindingDisabled?.Invoke(this, e); - } - - #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs index dda5d03cb..7483733a9 100644 --- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs +++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs @@ -121,6 +121,31 @@ namespace Artemis.Core return result.AsReadOnly(); } + /// + /// Applies the default value to all layer properties + /// + public void ResetAllLayerProperties() + { + foreach (ILayerProperty layerProperty in GetAllLayerProperties()) + layerProperty.ApplyDefaultValue(); + } + + /// + /// Occurs when the property group has initialized all its children + /// + public event EventHandler? PropertyGroupInitialized; + + /// + /// Occurs when one of the current value of one of the layer properties in this group changes by some form of input + /// Note: Will not trigger on properties in child groups + /// + public event EventHandler? LayerPropertyOnCurrentValueSet; + + /// + /// Occurs when the value of the layer property was updated + /// + public event EventHandler? VisibilityChanged; + /// /// Called before property group is activated to allow you to populate on /// the properties you want @@ -145,6 +170,27 @@ namespace Artemis.Core PropertyGroupInitialized?.Invoke(this, EventArgs.Empty); } + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _disposed = true; + DisableProperties(); + + foreach (ILayerProperty layerProperty in _layerProperties) + layerProperty.Dispose(); + foreach (LayerPropertyGroup layerPropertyGroup in _layerPropertyGroups) + layerPropertyGroup.Dispose(); + } + } + internal void Initialize(RenderProfileElement profileElement, string path, PluginFeature feature) { if (path == null) throw new ArgumentNullException(nameof(path)); @@ -209,6 +255,17 @@ namespace Artemis.Core layerPropertyGroup.Update(timeline); } + internal virtual void OnVisibilityChanged() + { + VisibilityChanged?.Invoke(this, EventArgs.Empty); + } + + internal virtual void OnLayerPropertyOnCurrentValueSet(LayerPropertyEventArgs e) + { + Parent?.OnLayerPropertyOnCurrentValueSet(e); + LayerPropertyOnCurrentValueSet?.Invoke(this, e); + } + private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription) { string path = $"{Path}.{propertyInfo.Name}"; @@ -266,67 +323,11 @@ namespace Artemis.Core return entity; } - #region IDisposable - - /// - /// Releases the unmanaged resources used by the object and optionally releases the managed resources. - /// - /// - /// to release both managed and unmanaged resources; - /// to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _disposed = true; - DisableProperties(); - - foreach (ILayerProperty layerProperty in _layerProperties) - layerProperty.Dispose(); - foreach (LayerPropertyGroup layerPropertyGroup in _layerPropertyGroups) - layerPropertyGroup.Dispose(); - } - } - /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } - - #endregion - - #region Events - - /// - /// Occurs when the property group has initialized all its children - /// - public event EventHandler? PropertyGroupInitialized; - - /// - /// Occurs when one of the current value of one of the layer properties in this group changes by some form of input - /// Note: Will not trigger on properties in child groups - /// - public event EventHandler? LayerPropertyOnCurrentValueSet; - - /// - /// Occurs when the value of the layer property was updated - /// - public event EventHandler? VisibilityChanged; - - internal virtual void OnVisibilityChanged() - { - VisibilityChanged?.Invoke(this, EventArgs.Empty); - } - - internal virtual void OnLayerPropertyOnCurrentValueSet(LayerPropertyEventArgs e) - { - Parent?.OnLayerPropertyOnCurrentValueSet(e); - LayerPropertyOnCurrentValueSet?.Invoke(this, e); - } - - #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Resources/intro-profile.json b/src/Artemis.Core/Resources/intro-profile.json index e95c17153..dad8392dc 100644 --- a/src/Artemis.Core/Resources/intro-profile.json +++ b/src/Artemis.Core/Resources/intro-profile.json @@ -292,7 +292,7 @@ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", "FeatureId": null, "Path": "LayerBrush.GradientColor", - "Value": "{\"Stops\":[{\"Color\":\"#ff0b4a40\",\"Position\":0.0},{\"Color\":\"#ff00897c\",\"Position\":0.242},{\"Color\":\"#ffffffff\",\"Position\":1.0},{\"Color\":\"#ff00ffe6\",\"Position\":0.67391306}]}", + "Value": "[{\"Color\":\"#ff0b4a40\",\"Position\":0.0},{\"Color\":\"#ff00897c\",\"Position\":0.242},{\"Color\":\"#ffffffff\",\"Position\":1.0},{\"Color\":\"#ff00ffe6\",\"Position\":0.67391306}]", "KeyframesEnabled": false, "KeyframeEntities": { "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", @@ -592,7 +592,7 @@ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", "FeatureId": "Artemis.Plugins.LayerBrushes.Noise.NoiseBrushProvider-61cbbf01", "Path": "LayerBrush.GradientColor", - "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]}", + "Value": "[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]", "KeyframesEnabled": false, "KeyframeEntities": { "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", @@ -909,7 +909,7 @@ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", "FeatureId": null, "Path": "LayerBrush.Colors", - "Value": "{\"Stops\":[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffffdf00\",\"Position\":0.916},{\"Color\":\"#00ffcd00\",\"Position\":1.0},{\"Color\":\"#fff31900\",\"Position\":0.636},{\"Color\":\"#dbf41500\",\"Position\":0.5461956},{\"Color\":\"#00f60f00\",\"Position\":0.462},{\"Color\":\"#93f60d00\",\"Position\":0.3668478}]}", + "Value": "[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffffdf00\",\"Position\":0.916},{\"Color\":\"#00ffcd00\",\"Position\":1.0},{\"Color\":\"#fff31900\",\"Position\":0.636},{\"Color\":\"#dbf41500\",\"Position\":0.5461956},{\"Color\":\"#00f60f00\",\"Position\":0.462},{\"Color\":\"#93f60d00\",\"Position\":0.3668478}]", "KeyframesEnabled": false, "KeyframeEntities": { "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", @@ -1164,7 +1164,7 @@ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", "FeatureId": "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba", "Path": "LayerBrush.Colors", - "Value": "{\"Stops\":[{\"Color\":\"#ffcaff00\",\"Position\":0.709},{\"Color\":\"#ffff0000\",\"Position\":0.902},{\"Color\":\"#ffffffff\",\"Position\":0.5842391},{\"Color\":\"#ffff4100\",\"Position\":0.95108694},{\"Color\":\"#00ff4100\",\"Position\":0.98641306}]}", + "Value": "[{\"Color\":\"#ffcaff00\",\"Position\":0.709},{\"Color\":\"#ffff0000\",\"Position\":0.902},{\"Color\":\"#ffffffff\",\"Position\":0.5842391},{\"Color\":\"#ffff4100\",\"Position\":0.95108694},{\"Color\":\"#00ff4100\",\"Position\":0.98641306}]", "KeyframesEnabled": false, "KeyframeEntities": { "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", @@ -1496,7 +1496,7 @@ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", "FeatureId": null, "Path": "LayerBrush.Colors", - "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.125},{\"Color\":\"#ffedff00\",\"Position\":0.25},{\"Color\":\"#ff65ff00\",\"Position\":0.375},{\"Color\":\"#ff00ff22\",\"Position\":0.5},{\"Color\":\"#ff00ffaa\",\"Position\":0.625},{\"Color\":\"#ff00cbff\",\"Position\":0.75},{\"Color\":\"#ff0043ff\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]}", + "Value": "[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.125},{\"Color\":\"#ffedff00\",\"Position\":0.25},{\"Color\":\"#ff65ff00\",\"Position\":0.375},{\"Color\":\"#ff00ff22\",\"Position\":0.5},{\"Color\":\"#ff00ffaa\",\"Position\":0.625},{\"Color\":\"#ff00cbff\",\"Position\":0.75},{\"Color\":\"#ff0043ff\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]", "KeyframesEnabled": false, "KeyframeEntities": { "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", @@ -1751,7 +1751,7 @@ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", "FeatureId": "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba", "Path": "LayerBrush.Colors", - "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]}", + "Value": "[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffffbf00\",\"Position\":0.125},{\"Color\":\"#ff7fff00\",\"Position\":0.25},{\"Color\":\"#ff00ff3f\",\"Position\":0.375},{\"Color\":\"#ff00ffff\",\"Position\":0.5},{\"Color\":\"#ff003fff\",\"Position\":0.625},{\"Color\":\"#ff7f00ff\",\"Position\":0.75},{\"Color\":\"#ffff00bf\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}]", "KeyframesEnabled": false, "KeyframeEntities": { "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index 079bb18e2..28f590376 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -251,20 +251,16 @@ namespace Artemis.Core.Services // Draw a white overlay over the device void DrawOverlay(object? sender, FrameRenderingEventArgs args) { + if (intro.AnimationProfile.GetAllLayers().All(l => l.Timeline.IsFinished)) + { + FrameRendering -= DrawOverlay; + intro.AnimationProfile.Dispose(); + } + intro.Render(args.DeltaTime, args.Canvas); } FrameRendering += DrawOverlay; - - // Stop rendering after the profile finishes (take 1 second extra in case of slow updates) - TimeSpan introLength = intro.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length)!; - Task.Run(async () => - { - await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1))); - FrameRendering -= DrawOverlay; - - intro.AnimationProfile.Dispose(); - }); } public event EventHandler? Initialized; diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs index 9bb5456e4..9d295e499 100644 --- a/src/Artemis.Core/Utilities/IntroAnimation.cs +++ b/src/Artemis.Core/Utilities/IntroAnimation.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using Artemis.Core.Modules; diff --git a/src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs b/src/Artemis.Storage/Migrations/M0001AttributeBasedPropertiesMigration.cs similarity index 86% rename from src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs rename to src/Artemis.Storage/Migrations/M0001AttributeBasedPropertiesMigration.cs index b1697878a..4ac0e1979 100644 --- a/src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs +++ b/src/Artemis.Storage/Migrations/M0001AttributeBasedPropertiesMigration.cs @@ -3,7 +3,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M1AttributeBasedPropertiesMigration : IStorageMigration + public class M0001AttributeBasedPropertiesMigration : IStorageMigration { public int UserVersion => 1; diff --git a/src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs b/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs similarity index 94% rename from src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs rename to src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs index e37a48776..409e2ad23 100644 --- a/src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs +++ b/src/Artemis.Storage/Migrations/M0002ProfileEntitiesEnabledMigration.cs @@ -5,7 +5,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M2ProfileEntitiesEnabledMigration : IStorageMigration + public class M0002ProfileEntitiesEnabledMigration : IStorageMigration { public int UserVersion => 2; diff --git a/src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs b/src/Artemis.Storage/Migrations/M0003PluginEntitiesIndexChangesMigration.cs similarity index 89% rename from src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs rename to src/Artemis.Storage/Migrations/M0003PluginEntitiesIndexChangesMigration.cs index 0d38b175d..17560d181 100644 --- a/src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs +++ b/src/Artemis.Storage/Migrations/M0003PluginEntitiesIndexChangesMigration.cs @@ -3,7 +3,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M3PluginEntitiesIndexChangesMigration : IStorageMigration + public class M0003PluginEntitiesIndexChangesMigration : IStorageMigration { public int UserVersion => 3; diff --git a/src/Artemis.Storage/Migrations/M4ProfileSegments.cs b/src/Artemis.Storage/Migrations/M0004ProfileSegments.cs similarity index 96% rename from src/Artemis.Storage/Migrations/M4ProfileSegments.cs rename to src/Artemis.Storage/Migrations/M0004ProfileSegments.cs index c54e3dc0f..74e1c4f48 100644 --- a/src/Artemis.Storage/Migrations/M4ProfileSegments.cs +++ b/src/Artemis.Storage/Migrations/M0004ProfileSegments.cs @@ -7,7 +7,7 @@ using System.Linq; namespace Artemis.Storage.Migrations { - public class M4ProfileSegmentsMigration : IStorageMigration + public class M0004ProfileSegmentsMigration : IStorageMigration { public int UserVersion => 4; diff --git a/src/Artemis.Storage/Migrations/M5DataBindingTypes.cs b/src/Artemis.Storage/Migrations/M0005DataBindingTypes.cs similarity index 94% rename from src/Artemis.Storage/Migrations/M5DataBindingTypes.cs rename to src/Artemis.Storage/Migrations/M0005DataBindingTypes.cs index 7bc4bcff6..5371de084 100644 --- a/src/Artemis.Storage/Migrations/M5DataBindingTypes.cs +++ b/src/Artemis.Storage/Migrations/M0005DataBindingTypes.cs @@ -3,7 +3,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M5DataBindingTypes : IStorageMigration + public class M0005DataBindingTypes : IStorageMigration { public int UserVersion => 5; diff --git a/src/Artemis.Storage/Migrations/M6PredicateAbstraction.cs b/src/Artemis.Storage/Migrations/M0006PredicateAbstraction.cs similarity index 96% rename from src/Artemis.Storage/Migrations/M6PredicateAbstraction.cs rename to src/Artemis.Storage/Migrations/M0006PredicateAbstraction.cs index 339b42358..4e7567710 100644 --- a/src/Artemis.Storage/Migrations/M6PredicateAbstraction.cs +++ b/src/Artemis.Storage/Migrations/M0006PredicateAbstraction.cs @@ -4,7 +4,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M6PredicateAbstraction : IStorageMigration + public class M0006PredicateAbstraction : IStorageMigration { public int UserVersion => 6; diff --git a/src/Artemis.Storage/Migrations/M8PluginFeatures.cs b/src/Artemis.Storage/Migrations/M0008PluginFeatures.cs similarity index 99% rename from src/Artemis.Storage/Migrations/M8PluginFeatures.cs rename to src/Artemis.Storage/Migrations/M0008PluginFeatures.cs index d782ff2bd..5dc088531 100644 --- a/src/Artemis.Storage/Migrations/M8PluginFeatures.cs +++ b/src/Artemis.Storage/Migrations/M0008PluginFeatures.cs @@ -5,7 +5,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M8PluginFeatures : IStorageMigration + public class M0008PluginFeatures : IStorageMigration { private void Migrate(BsonValue bsonValue, Dictionary pluginMap) { diff --git a/src/Artemis.Storage/Migrations/M9DeviceCalibration.cs b/src/Artemis.Storage/Migrations/M0009DeviceCalibration.cs similarity index 92% rename from src/Artemis.Storage/Migrations/M9DeviceCalibration.cs rename to src/Artemis.Storage/Migrations/M0009DeviceCalibration.cs index fdb45dd8b..7d6aef3b8 100644 --- a/src/Artemis.Storage/Migrations/M9DeviceCalibration.cs +++ b/src/Artemis.Storage/Migrations/M0009DeviceCalibration.cs @@ -3,7 +3,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M9DeviceCalibration : IStorageMigration + public class M0009DeviceCalibration : IStorageMigration { public int UserVersion => 9; diff --git a/src/Artemis.Storage/Migrations/M10BetterDataBindings.cs b/src/Artemis.Storage/Migrations/M0010BetterDataBindings.cs similarity index 97% rename from src/Artemis.Storage/Migrations/M10BetterDataBindings.cs rename to src/Artemis.Storage/Migrations/M0010BetterDataBindings.cs index 4b7daf20f..ae33770c0 100644 --- a/src/Artemis.Storage/Migrations/M10BetterDataBindings.cs +++ b/src/Artemis.Storage/Migrations/M0010BetterDataBindings.cs @@ -3,7 +3,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class M10BetterDataBindings : IStorageMigration + public class M0010BetterDataBindings : IStorageMigration { private void Migrate(BsonValue bsonValue) { diff --git a/src/Artemis.Storage/Migrations/M0011ColorGradients.cs b/src/Artemis.Storage/Migrations/M0011ColorGradients.cs new file mode 100644 index 000000000..1b4f00bd2 --- /dev/null +++ b/src/Artemis.Storage/Migrations/M0011ColorGradients.cs @@ -0,0 +1,42 @@ +using Artemis.Storage.Migrations.Interfaces; +using LiteDB; + +namespace Artemis.Storage.Migrations +{ + public class M0011ColorGradients : IStorageMigration + { + private void Migrate(BsonValue bsonValue) + { + if (!bsonValue.IsDocument || !bsonValue.AsDocument.TryGetValue("PropertyEntities", out BsonValue propertyEntities)) + return; + + foreach (BsonValue propertyEntity in propertyEntities.AsArray) + { + string valueString = propertyEntity["Value"].AsString; + if (!valueString.StartsWith("{\"Stops\":[{") || !valueString.EndsWith("}]}")) + continue; + + valueString = valueString.Replace("{\"Stops\":[{", "[{"); + valueString = valueString.Replace("}]}", "}]"); + propertyEntity["Value"] = valueString; + } + } + + public int UserVersion => 11; + + public void Apply(LiteRepository repository) + { + ILiteCollection collection = repository.Database.GetCollection("ProfileEntity"); + foreach (BsonDocument bsonDocument in collection.FindAll()) + { + foreach (BsonValue bsonLayer in bsonDocument["Layers"].AsArray) + Migrate(bsonLayer); + + foreach (BsonValue bsonLayer in bsonDocument["Folders"].AsArray) + Migrate(bsonLayer); + + collection.Update(bsonDocument); + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs index fe72e1392..1cff68bfd 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Windows; -using System.Windows.Controls.Primitives; using Artemis.Core; using Artemis.Core.Modules; using Artemis.Core.Services; @@ -20,7 +19,6 @@ namespace Artemis.UI.Shared.Services { internal class ProfileEditorService : IProfileEditorService { - private readonly ICoreService _coreService; private readonly IKernel _kernel; private readonly ILogger _logger; private readonly IProfileService _profileService; @@ -29,6 +27,7 @@ namespace Artemis.UI.Shared.Services private readonly object _selectedProfileElementLock = new(); private readonly object _selectedProfileLock = new(); private TimeSpan _currentTime; + private bool _doTick; private int _pixelsPerSecond; public ProfileEditorService(IKernel kernel, ILogger logger, IProfileService profileService, ICoreService coreService, IRgbService rgbService) @@ -36,16 +35,63 @@ namespace Artemis.UI.Shared.Services _kernel = kernel; _logger = logger; _profileService = profileService; - _coreService = coreService; _rgbService = rgbService; _registeredPropertyEditors = new List(); - + coreService.FrameRendered += CoreServiceOnFrameRendered; PixelsPerSecond = 100; } + public event EventHandler? CurrentTimelineChanged; + + protected virtual void OnSelectedProfileChanged(ProfileEventArgs e) + { + ProfileSelected?.Invoke(this, e); + } + + protected virtual void OnSelectedProfileUpdated(ProfileEventArgs e) + { + SelectedProfileUpdated?.Invoke(this, e); + } + + protected virtual void OnSelectedProfileElementChanged(RenderProfileElementEventArgs e) + { + ProfileElementSelected?.Invoke(this, e); + } + + protected virtual void OnSelectedProfileElementUpdated(RenderProfileElementEventArgs e) + { + SelectedProfileElementUpdated?.Invoke(this, e); + } + + protected virtual void OnCurrentTimeChanged() + { + CurrentTimeChanged?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnCurrentTimelineChanged() + { + CurrentTimelineChanged?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnPixelsPerSecondChanged() + { + PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnProfilePreviewUpdated() + { + ProfilePreviewUpdated?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnSelectedDataBindingChanged() + { + SelectedDataBindingChanged?.Invoke(this, EventArgs.Empty); + } + private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e) { - _coreService.FrameRendered -= CoreServiceOnFrameRendered; + if (!_doTick) return; + _doTick = false; Execute.PostToUIThread(OnProfilePreviewUpdated); } @@ -73,6 +119,26 @@ namespace Artemis.UI.Shared.Services UpdateProfilePreview(); } + private void Tick() + { + if (SelectedProfile == null || _doTick) + return; + + // Stick to the main segment for any element that is not currently selected + foreach (Folder folder in SelectedProfile.GetAllFolders()) + folder.Timeline.Override(CurrentTime, folder.Timeline.PlayMode == TimelinePlayMode.Repeat); + foreach (Layer layer in SelectedProfile.GetAllLayers()) + layer.Timeline.Override(CurrentTime, (layer != SelectedProfileElement || layer.Timeline.Length < CurrentTime) && layer.Timeline.PlayMode == TimelinePlayMode.Repeat); + + _doTick = true; + } + + private void SelectedProfileOnDeactivated(object? sender, EventArgs e) + { + // Execute.PostToUIThread(() => ChangeSelectedProfile(null)); + ChangeSelectedProfile(null); + } + public ReadOnlyCollection RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly(); public bool Playing { get; set; } @@ -351,19 +417,14 @@ namespace Artemis.UI.Shared.Services .ToList(); } - private void Tick() - { - if (SelectedProfile == null) - return; - - // Stick to the main segment for any element that is not currently selected - foreach (Folder folder in SelectedProfile.GetAllFolders()) - folder.Timeline.Override(CurrentTime, folder.Timeline.PlayMode == TimelinePlayMode.Repeat); - foreach (Layer layer in SelectedProfile.GetAllLayers()) - layer.Timeline.Override(CurrentTime, (layer != SelectedProfileElement || layer.Timeline.Length < CurrentTime) && layer.Timeline.PlayMode == TimelinePlayMode.Repeat); - - _coreService.FrameRendered += CoreServiceOnFrameRendered; - } + public event EventHandler? ProfileSelected; + public event EventHandler? SelectedProfileUpdated; + public event EventHandler? ProfileElementSelected; + public event EventHandler? SelectedProfileElementUpdated; + public event EventHandler? SelectedDataBindingChanged; + public event EventHandler? CurrentTimeChanged; + public event EventHandler? PixelsPerSecondChanged; + public event EventHandler? ProfilePreviewUpdated; #region Copy/paste @@ -444,70 +505,5 @@ namespace Artemis.UI.Shared.Services } #endregion - - #region Events - - public event EventHandler? ProfileSelected; - public event EventHandler? SelectedProfileUpdated; - public event EventHandler? ProfileElementSelected; - public event EventHandler? SelectedProfileElementUpdated; - public event EventHandler? SelectedDataBindingChanged; - public event EventHandler? CurrentTimeChanged; - public event EventHandler? PixelsPerSecondChanged; - public event EventHandler? ProfilePreviewUpdated; - public event EventHandler? CurrentTimelineChanged; - - protected virtual void OnSelectedProfileChanged(ProfileEventArgs e) - { - ProfileSelected?.Invoke(this, e); - } - - protected virtual void OnSelectedProfileUpdated(ProfileEventArgs e) - { - SelectedProfileUpdated?.Invoke(this, e); - } - - protected virtual void OnSelectedProfileElementChanged(RenderProfileElementEventArgs e) - { - ProfileElementSelected?.Invoke(this, e); - } - - protected virtual void OnSelectedProfileElementUpdated(RenderProfileElementEventArgs e) - { - SelectedProfileElementUpdated?.Invoke(this, e); - } - - protected virtual void OnCurrentTimeChanged() - { - CurrentTimeChanged?.Invoke(this, EventArgs.Empty); - } - - protected virtual void OnCurrentTimelineChanged() - { - CurrentTimelineChanged?.Invoke(this, EventArgs.Empty); - } - - protected virtual void OnPixelsPerSecondChanged() - { - PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty); - } - - protected virtual void OnProfilePreviewUpdated() - { - ProfilePreviewUpdated?.Invoke(this, EventArgs.Empty); - } - - protected virtual void OnSelectedDataBindingChanged() - { - SelectedDataBindingChanged?.Invoke(this, EventArgs.Empty); - } - - private void SelectedProfileOnDeactivated(object? sender, EventArgs e) - { - // Execute.PostToUIThread(() => ChangeSelectedProfile(null)); - ChangeSelectedProfile(null); - } - - #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/LayerBrushPresetViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/LayerBrushPresetViewModel.cs index 600dc2e42..44d90cf25 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Dialogs/LayerBrushPresetViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Dialogs/LayerBrushPresetViewModel.cs @@ -7,10 +7,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs { public class LayerBrushPresetViewModel : DialogViewModelBase { + private readonly BaseLayerBrush _layerBrush; private ILayerBrushPreset _selectedPreset; public LayerBrushPresetViewModel(BaseLayerBrush layerBrush) { + _layerBrush = layerBrush; Presets = new BindableCollection(); Presets.AddRange(layerBrush.Presets); } @@ -29,6 +31,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs public void SelectPreset(ILayerBrushPreset preset) { + _layerBrush.BaseProperties?.ResetAllLayerProperties(); preset.Apply(); Execute.OnUIThreadAsync(async () => { diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs index c6dc54879..971746de6 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs @@ -47,7 +47,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree public void ResetToDefault() { - LayerProperty.ApplyDefaultValue(_profileEditorService.CurrentTime); + LayerProperty.ApplyDefaultValue(); _profileEditorService.UpdateSelectedProfileElement(); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index 2fc7d718f..1906c9cf8 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -321,7 +321,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta); // TODO: Only update when there are data bindings - _profileEditorService.UpdateProfilePreview(); + if (!_profileEditorService.Playing) + _profileEditorService.UpdateProfilePreview(); } private void HighlightSelectedLayerOnSettingChanged(object sender, EventArgs e) diff --git a/src/Artemis.UI/Screens/Settings/Device/DeviceDialogViewModel.cs b/src/Artemis.UI/Screens/Settings/Device/DeviceDialogViewModel.cs index 5f5c7ed91..9fa16037e 100644 --- a/src/Artemis.UI/Screens/Settings/Device/DeviceDialogViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Device/DeviceDialogViewModel.cs @@ -94,7 +94,7 @@ namespace Artemis.UI.Screens.Settings.Device private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e) { - if (SelectedLeds == null) + if (SelectedLeds == null || !SelectedLeds.Any()) return; using SKPaint highlightPaint = new() {Color = SKColors.White}; diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs index 4977b25b2..701451d48 100644 --- a/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/LayoutStepViewModel.cs @@ -25,5 +25,23 @@ namespace Artemis.UI.Screens.StartupWizard.Steps StartupWizardViewModel startupWizardViewModel = (StartupWizardViewModel) Parent; startupWizardViewModel.Continue(); } + + #region Overrides of Screen + + /// + protected override void OnActivate() + { + _rgbService.IsRenderPaused = true; + base.OnActivate(); + } + + /// + protected override void OnDeactivate() + { + _rgbService.IsRenderPaused = false; + base.OnDeactivate(); + } + + #endregion } } \ No newline at end of file