diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 6f183e770..8ec456c22 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Windows.Media.Animation; using Artemis.Core.Extensions; using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Models.Profile.LayerProperties.Attributes; @@ -37,6 +38,7 @@ namespace Artemis.Core.Models.Profile Transform = new LayerTransformProperties {IsCorePropertyGroup = true}; _leds = new List(); + General.PropertyGroupInitialized += GeneralOnPropertyGroupInitialized; } internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) @@ -52,6 +54,7 @@ namespace Artemis.Core.Models.Profile Transform = new LayerTransformProperties {IsCorePropertyGroup = true}; _leds = new List(); + General.PropertyGroupInitialized += GeneralOnPropertyGroupInitialized; } internal LayerEntity LayerEntity { get; set; } @@ -147,6 +150,18 @@ namespace Artemis.Core.Models.Profile #region Shape management + private void GeneralOnPropertyGroupInitialized(object sender, EventArgs e) + { + ApplyShapeType(); + General.ShapeType.BaseValueChanged -= ShapeTypeOnBaseValueChanged; + General.ShapeType.BaseValueChanged += ShapeTypeOnBaseValueChanged; + } + + private void ShapeTypeOnBaseValueChanged(object sender, EventArgs e) + { + ApplyShapeType(); + } + private void ApplyShapeType() { switch (General.ShapeType.CurrentValue) @@ -164,34 +179,21 @@ namespace Artemis.Core.Models.Profile #endregion - #region Properties - - internal void InitializeProperties(ILayerService layerService) - { - PropertiesInitialized = true; - - ApplyShapeType(); - } - - public bool PropertiesInitialized { get; private set; } - - #endregion - #region Rendering /// public override void Update(double deltaTime) { - if (LayerBrush == null) + if (LayerBrush == null || !LayerBrush.BaseProperties.PropertiesInitialized) return; - var properties = new List(General.GetAllLayerProperties()); - properties.AddRange(Transform.GetAllLayerProperties()); - properties.AddRange(LayerBrush.BaseProperties.GetAllLayerProperties()); + var properties = new List(General.GetAllLayerProperties().Where(p => p.BaseKeyframes.Any())); + properties.AddRange(Transform.GetAllLayerProperties().Where(p => p.BaseKeyframes.Any())); + properties.AddRange(LayerBrush.BaseProperties.GetAllLayerProperties().Where(p => p.BaseKeyframes.Any())); // For now, reset all keyframe engines after the last keyframe was hit // This is a placeholder method of repeating the animation until repeat modes are implemented - var timeLineEnd = properties.Max(p => p.BaseKeyframes.Max(k => k.Position)); + var timeLineEnd = properties.Any() ? properties.Max(p => p.BaseKeyframes.Max(k => k.Position)) : TimeSpan.MaxValue; if (properties.Any(p => p.TimelineProgress >= timeLineEnd)) { General.Override(TimeSpan.Zero); @@ -218,7 +220,7 @@ namespace Artemis.Core.Models.Profile /// public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) { - if (Path == null || LayerShape?.Path == null) + if (Path == null || LayerShape?.Path == null || !General.PropertiesInitialized || !Transform.PropertiesInitialized) return; canvas.Save(); @@ -263,7 +265,8 @@ namespace Artemis.Core.Models.Profile canvas.Scale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y); canvas.Translate(x, y); - LayerBrush?.Render(canvas, canvasInfo, new SKPath(LayerShape.Path), paint); + if (LayerBrush != null && LayerBrush.BaseProperties.PropertiesInitialized) + LayerBrush.Render(canvas, canvasInfo, new SKPath(LayerShape.Path), paint); } private void ClipRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint) @@ -405,6 +408,7 @@ namespace Artemis.Core.Models.Profile public event EventHandler RenderPropertiesUpdated; public event EventHandler ShapePropertiesUpdated; + public event EventHandler LayerBrushUpdated; private void OnRenderPropertiesUpdated() { @@ -416,6 +420,11 @@ namespace Artemis.Core.Models.Profile ShapePropertiesUpdated?.Invoke(this, EventArgs.Empty); } + internal void OnLayerBrushUpdated() + { + LayerBrushUpdated?.Invoke(this, EventArgs.Empty); + } + #endregion } diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs index 0abc5379a..bff3806a2 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs @@ -9,10 +9,17 @@ namespace Artemis.Core.Models.Profile.LayerProperties /// public abstract class BaseLayerProperty { + private bool _keyframesEnabled; + internal BaseLayerProperty() { } + /// + /// The layer this property applies to + /// + public Layer Layer { get; internal set; } + /// /// The parent group of this layer property, set after construction /// @@ -21,13 +28,22 @@ namespace Artemis.Core.Models.Profile.LayerProperties /// /// Gets whether keyframes are supported on this property /// - public bool KeyframesSupported { get; protected set; } + public bool KeyframesSupported { get; protected set; } = true; /// /// Gets or sets whether keyframes are enabled on this property, has no effect if is /// False /// - public bool KeyframesEnabled { get; set; } + public bool KeyframesEnabled + { + get => _keyframesEnabled; + set + { + if (_keyframesEnabled == value) return; + _keyframesEnabled = value; + OnKeyframesToggled(); + } + } /// /// Gets or sets whether the property is hidden in the UI @@ -57,17 +73,73 @@ namespace Artemis.Core.Models.Profile.LayerProperties internal PropertyEntity PropertyEntity { get; set; } internal LayerPropertyGroup LayerPropertyGroup { get; set; } + /// /// Applies the provided property entity to the layer property by deserializing the JSON base value and keyframe values /// /// /// - internal abstract void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup); + /// + internal abstract void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage); /// /// Saves the property to the underlying property entity that was configured when calling /// /// internal abstract void ApplyToEntity(); + + #region Events + + /// + /// Occurs once every frame when the layer property is updated + /// + public event EventHandler Updated; + + /// + /// Occurs when the base value of the layer property was updated + /// + public event EventHandler BaseValueChanged; + + /// + /// Occurs when keyframes are enabled/disabled + /// + public event EventHandler KeyframesToggled; + + /// + /// Occurs when a new keyframe was added to the layer property + /// + public event EventHandler KeyframeAdded; + + /// + /// Occurs when a keyframe was removed from the layer property + /// + public event EventHandler KeyframeRemoved; + + protected virtual void OnUpdated() + { + Updated?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnBaseValueChanged() + { + BaseValueChanged?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnKeyframesToggled() + { + KeyframesToggled?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnKeyframeAdded() + { + KeyframeAdded?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnKeyframeRemoved() + { + KeyframeRemoved?.Invoke(this, EventArgs.Empty); + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 0b284a23c..351593d3f 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -180,12 +180,13 @@ namespace Artemis.Core.Models.Profile.LayerProperties // The current keyframe is the last keyframe before the current time CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= TimelineProgress); - // The next keyframe is the first keyframe that's after the current time - NextKeyframe = _keyframes.FirstOrDefault(k => k.Position > TimelineProgress); + // Keyframes are sorted by position so we can safely assume the next keyframe's position is after the current + var nextIndex = _keyframes.IndexOf(CurrentKeyframe) + 1; + NextKeyframe = _keyframes.Count > nextIndex ? _keyframes[nextIndex] : null; // No need to update the current value if either of the keyframes are null if (CurrentKeyframe == null) - CurrentValue = BaseValue; + CurrentValue = _keyframes.Any() ? _keyframes[0].Value : BaseValue; else if (NextKeyframe == null) CurrentValue = CurrentKeyframe.Value; // Only determine progress and current value if both keyframes are present @@ -218,7 +219,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties _keyframes = _keyframes.OrderBy(k => k.Position).ToList(); } - internal override void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup) + internal override void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage) { // Doubt this will happen but let's make sure if (_isInitialized) @@ -231,8 +232,10 @@ namespace Artemis.Core.Models.Profile.LayerProperties try { - IsLoadedFromStorage = true; - BaseValue = JsonConvert.DeserializeObject(entity.Value); + if (entity.Value != null) + BaseValue = JsonConvert.DeserializeObject(entity.Value); + + IsLoadedFromStorage = fromStorage; CurrentValue = BaseValue; KeyframesEnabled = entity.KeyframesEnabled; @@ -259,7 +262,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties internal override void ApplyToEntity() { - if (_isInitialized) + if (!_isInitialized) throw new ArtemisCoreException("Layer property is not yet initialized"); PropertyEntity.Value = JsonConvert.SerializeObject(BaseValue); @@ -272,49 +275,5 @@ namespace Artemis.Core.Models.Profile.LayerProperties EasingFunction = (int) k.EasingFunction })); } - - #region Events - - /// - /// Occurs once every frame when the layer property is updated - /// - public event EventHandler Updated; - - /// - /// Occurs when the base value of the layer property was updated - /// - public event EventHandler BaseValueChanged; - - /// - /// Occurs when a new keyframe was added to the layer property - /// - public event EventHandler KeyframeAdded; - - /// - /// Occurs when a keyframe was removed from the layer property - /// - public event EventHandler KeyframeRemoved; - - protected virtual void OnUpdated() - { - Updated?.Invoke(this, EventArgs.Empty); - } - - protected virtual void OnBaseValueChanged() - { - BaseValueChanged?.Invoke(this, EventArgs.Empty); - } - - protected virtual void OnKeyframeAdded() - { - KeyframeAdded?.Invoke(this, EventArgs.Empty); - } - - protected virtual void OnKeyframeRemoved() - { - KeyframeRemoved?.Invoke(this, EventArgs.Empty); - } - - #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 cb4b87709..2ea9b5047 100644 --- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs +++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using Artemis.Core.Annotations; using Artemis.Core.Events; using Artemis.Core.Exceptions; using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Models.Profile.LayerProperties.Attributes; using Artemis.Core.Plugins.Exceptions; using Artemis.Core.Services.Interfaces; +using Artemis.Storage.Entities.Profile; namespace Artemis.Core.Models.Profile { @@ -23,6 +25,11 @@ namespace Artemis.Core.Models.Profile _layerPropertyGroups = new List(); } + /// + /// The layer this property group applies to + /// + public Layer Layer { get; internal set; } + /// /// The parent group of this layer property group, set after construction /// @@ -60,12 +67,16 @@ namespace Artemis.Core.Models.Profile { } - internal void InitializeProperties(ILayerService layerService, Layer layer, string path) + internal void InitializeProperties(ILayerService layerService, Layer layer, [NotNull] string path) { + if (path == null) + throw new ArgumentNullException(nameof(path)); // Doubt this will happen but let's make sure if (PropertiesInitialized) throw new ArtemisCoreException("Layer property group already initialized, wut"); + Layer = layer; + // Get all properties with a PropertyDescriptionAttribute foreach (var propertyInfo in GetType().GetProperties()) { @@ -77,7 +88,8 @@ namespace Artemis.Core.Models.Profile var instance = (BaseLayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true); instance.Parent = this; - InitializeProperty(layer, path, instance); + instance.Layer = layer; + InitializeProperty(layer, path + propertyInfo.Name, instance); propertyInfo.SetValue(this, instance); _layerProperties.Add(instance); } @@ -100,6 +112,8 @@ namespace Artemis.Core.Models.Profile OnPropertiesInitialized(); PropertiesInitialized = true; + + OnPropertyGroupInitialized(); } internal void ApplyToEntity() @@ -110,12 +124,16 @@ namespace Artemis.Core.Models.Profile var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute)); if (propertyDescription != null) { + var layerProperty = (BaseLayerProperty) propertyInfo.GetValue(this); + layerProperty.ApplyToEntity(); } else { var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute)); if (propertyGroupDescription != null) { + var layerPropertyGroup = (LayerPropertyGroup) propertyInfo.GetValue(this); + layerPropertyGroup.ApplyToEntity(); } } } @@ -157,14 +175,22 @@ namespace Artemis.Core.Models.Profile { var pluginGuid = IsCorePropertyGroup || instance.IsCoreProperty ? Constants.CorePluginInfo.Guid : layer.LayerBrush.PluginInfo.Guid; var entity = layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == pluginGuid && p.Path == path); - if (entity != null) - instance.ApplyToLayerProperty(entity, this); + var fromStorage = true; + if (entity == null) + { + fromStorage = false; + entity = new PropertyEntity {PluginGuid = pluginGuid, Path = path}; + layer.LayerEntity.PropertyEntities.Add(entity); + } + + instance.ApplyToLayerProperty(entity, this, fromStorage); } #region Events internal event EventHandler PropertyGroupUpdating; internal event EventHandler PropertyGroupOverriding; + public event EventHandler PropertyGroupInitialized; internal virtual void OnPropertyGroupUpdating(PropertyGroupUpdatingEventArgs e) { @@ -177,5 +203,10 @@ namespace Artemis.Core.Models.Profile } #endregion + + protected virtual void OnPropertyGroupInitialized() + { + PropertyGroupInitialized?.Invoke(this, EventArgs.Empty); + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs index a27fcdb19..6c518d3c4 100644 --- a/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs @@ -51,6 +51,7 @@ namespace Artemis.Core.Plugins.LayerBrush internal override void InitializeProperties(ILayerService layerService, string path) { + Properties = Activator.CreateInstance(); Properties.InitializeProperties(layerService, Layer, path); OnPropertiesInitialized(); PropertiesInitialized = true; diff --git a/src/Artemis.Core/Services/LayerService.cs b/src/Artemis.Core/Services/LayerService.cs index ae742b682..b080b8b5a 100644 --- a/src/Artemis.Core/Services/LayerService.cs +++ b/src/Artemis.Core/Services/LayerService.cs @@ -27,8 +27,8 @@ namespace Artemis.Core.Services var layer = new Layer(profile, parent, name); // Layers have two hardcoded property groups, instantiate them - layer.General.InitializeProperties(this, layer, null); - layer.Transform.InitializeProperties(this, layer, null); + layer.General.InitializeProperties(this, layer, "General."); + layer.Transform.InitializeProperties(this, layer, "Transform."); // With the properties loaded, the layer brush can be instantiated InstantiateLayerBrush(layer); @@ -58,11 +58,10 @@ namespace Artemis.Core.Services new ConstructorArgument("layer", layer), new ConstructorArgument("descriptor", descriptor) }; - var layerBrush = (BaseLayerBrush) _kernel.Get(descriptor.LayerBrushType, arguments); - layerBrush.InitializeProperties(this, null); - layer.LayerBrush = layerBrush; - - return layerBrush; + layer.LayerBrush = (BaseLayerBrush)_kernel.Get(descriptor.LayerBrushType, arguments); ; + layer.LayerBrush.InitializeProperties(this, "LayerBrush."); + layer.OnLayerBrushUpdated(); + return layer.LayerBrush; } public void RemoveLayerBrush(Layer layer) diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index d886939f3..cb258039c 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -93,8 +93,8 @@ namespace Artemis.Core.Services.Storage module.ChangeActiveProfile(profile, _surfaceService.ActiveSurface); if (profile != null) { - InitializeCoreProperties(profile); - InstantiateProfileLayerBrushes(profile); + InitializeLayerProperties(profile); + InstantiateLayerBrushes(profile); } } @@ -159,18 +159,18 @@ namespace Artemis.Core.Services.Storage _logger.Debug("Redo profile update - Success"); } - private void InitializeCoreProperties(Profile profile) + private void InitializeLayerProperties(Profile profile) { foreach (var layer in profile.GetAllLayers().Where(l => l.LayerBrush == null)) { if (!layer.General.PropertiesInitialized) - layer.General.InitializeProperties(_layerService, layer, null); + layer.General.InitializeProperties(_layerService, layer, "General."); if (!layer.Transform.PropertiesInitialized) - layer.Transform.InitializeProperties(_layerService, layer, null); + layer.Transform.InitializeProperties(_layerService, layer, "Transform."); }; } - private void InstantiateProfileLayerBrushes(Profile profile) + private void InstantiateLayerBrushes(Profile profile) { // Only instantiate brushes for layers without an existing brush instance foreach (var layer in profile.GetAllLayers().Where(l => l.LayerBrush == null)) @@ -188,7 +188,7 @@ namespace Artemis.Core.Services.Storage { var profileModules = _pluginService.GetPluginsOfType(); foreach (var profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList()) - InstantiateProfileLayerBrushes(profileModule.ActiveProfile); + InstantiateLayerBrushes(profileModule.ActiveProfile); } #region Event handlers diff --git a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml index 1da088f62..352ba72b4 100644 --- a/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml +++ b/src/Artemis.UI.Shared/Controls/DraggableFloat.xaml @@ -4,17 +4,25 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> + + + - + Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}"> + @@ -27,15 +35,14 @@ Foreground="{DynamicResource SecondaryAccentBrush}" MouseDown="InputMouseDown" MouseUp="InputMouseUp" - MouseMove="InputMouseMove" - RequestBringIntoView="Input_OnRequestBringIntoView"/> + MouseMove="InputMouseMove" + RequestBringIntoView="Input_OnRequestBringIntoView" /> HitTestResultBehavior.Continue); var filterCallback = new HitTestFilterCallback(e => { - if (e is FrameworkElement fe && fe.DataContext.GetType() == typeof(T) && !result.Contains((T) fe.DataContext)) - result.Add((T) fe.DataContext); + if (e is FrameworkElement fe && fe.DataContext is T context && !result.Contains(context)) + result.Add(context); return HitTestFilterBehavior.Continue; }); VisualTreeHelper.HitTest(container, filterCallback, resultCallback, hitTestParams); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index 466f08679..ae7320010 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -43,6 +43,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties set => ProfileEditorService.CurrentTime = TimeSpan.FromSeconds(value.Left / ProfileEditorService.PixelsPerSecond); } + public Layer SelectedLayer { get; set; } public BindableCollection LayerPropertyGroups { get; set; } public TreeViewModel TreeViewModel { get; set; } public TimelineViewModel TimelineViewModel { get; set; } @@ -86,9 +87,21 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties private void PopulateProperties(ProfileElement profileElement) { + if (SelectedLayer != null) + { + SelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated; + SelectedLayer = null; + } + + foreach (var layerPropertyGroupViewModel in LayerPropertyGroups) + layerPropertyGroupViewModel.Dispose(); LayerPropertyGroups.Clear(); + if (profileElement is Layer layer) { + SelectedLayer = layer; + SelectedLayer.LayerBrushUpdated += SelectedLayerOnLayerBrushUpdated; + // Add the built-in root groups of the layer var generalAttribute = Attribute.GetCustomAttribute( layer.GetType().GetProperty(nameof(layer.General)), @@ -113,11 +126,35 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(ProfileEditorService, layer.LayerBrush.BaseProperties, brushDescription)); } } + else + SelectedLayer = null; TreeViewModel = new TreeViewModel(this, LayerPropertyGroups); TimelineViewModel = new TimelineViewModel(this, LayerPropertyGroups); } + private void SelectedLayerOnLayerBrushUpdated(object sender, EventArgs e) + { + // Get rid of the old layer properties group + if (LayerPropertyGroups.Count == 3) + { + LayerPropertyGroups[2].Dispose(); + LayerPropertyGroups.RemoveAt(2); + } + + if (SelectedLayer.LayerBrush != null) + { + // Add the rout group of the brush + // The root group of the brush has no attribute so let's pull one out of our sleeve + var brushDescription = new PropertyGroupDescriptionAttribute + { + Name = SelectedLayer.LayerBrush.Descriptor.DisplayName, + Description = SelectedLayer.LayerBrush.Descriptor.Description + }; + LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(ProfileEditorService, SelectedLayer.LayerBrush.BaseProperties, brushDescription)); + } + } + #endregion #region Controls diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs index fb70e24b1..0c193a3d1 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs @@ -50,6 +50,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties public override void Dispose() { TreePropertyViewModel.Dispose(); + TimelinePropertyViewModel.Dispose(); } public void SetCurrentValue(T value, bool saveChanges) diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs index 4103779ee..8193f968a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs @@ -18,7 +18,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline EasingFunction = easingFunction; Description = easingFunction.Humanize(); - CreateGeometry(); + CreateEasingPoints(); } public Easings.Functions EasingFunction { get; } @@ -36,7 +36,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline } } - private void CreateGeometry() + private void CreateEasingPoints() { EasingPoints = new PointCollection(); for (var i = 1; i <= 10; i++) diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs index bb2d512f4..f979a5dc1 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs @@ -45,7 +45,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline #endregion } - public abstract class TimelineKeyframeViewModel + public abstract class TimelineKeyframeViewModel : PropertyChangedBase { private readonly IProfileEditorService _profileEditorService; private readonly TimelineViewModel _timelineViewModel; @@ -56,6 +56,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline _profileEditorService = profileEditorService; _timelineViewModel = timelineViewModel; BaseLayerPropertyKeyframe = baseLayerPropertyKeyframe; + EasingViewModels = new BindableCollection(); } public BaseLayerPropertyKeyframe BaseLayerPropertyKeyframe { get; } @@ -139,6 +140,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline #region Easing + public void ContextMenuOpening() + { + CreateEasingViewModels(); + } + + public void ContextMenuClosing() + { + EasingViewModels.Clear(); + } + private void CreateEasingViewModels() { EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast().Select(v => new TimelineEasingViewModel(this, v))); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml index 68822ff6c..9a54c20ec 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml @@ -12,72 +12,82 @@ d:DataContext="{d:DesignInstance local:TimelinePropertyGroupViewModel}"> - + + - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + VerticalContentAlignment="Stretch" + HorizontalContentAlignment="Stretch" + IsTabStop="False" /> + VerticalContentAlignment="Stretch" + HorizontalContentAlignment="Stretch" + IsTabStop="False" /> diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml index f780cea81..83f6e1afe 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml @@ -1,12 +1,12 @@  @@ -35,7 +35,9 @@ s:View.ActionTarget="{Binding}" MouseDown="{s:Action KeyframeMouseDown}" MouseUp="{s:Action KeyframeMouseUp}" - MouseMove="{s:Action KeyframeMouseMove}"> + MouseMove="{s:Action KeyframeMouseMove}" + ContextMenuOpening="{s:Action ContextMenuOpening}" + ContextMenuClosing="{s:Action ContextMenuClosing}"> - + + { + var drawingImage = new DrawingImage(new GeometryDrawing(new SolidColorBrush(Colors.Black), null, LayerGeometry)); + var image = new Image { Source = drawingImage }; + var bitmap = new RenderTargetBitmap( + (int)(LayerGeometry.Bounds.Width * 2.5), + (int)(LayerGeometry.Bounds.Height * 2.5), + 96, + 96, + PixelFormats.Pbgra32 + ); + image.Arrange(new Rect(0, 0, bitmap.Width, bitmap.Height)); + bitmap.Render(image); + bitmap.Freeze(); + LayerGeometryBitmap = bitmap; + }); + } private void CreateShapeGeometry() diff --git a/src/Artemis.UI/Services/ProfileEditorService.cs b/src/Artemis.UI/Services/ProfileEditorService.cs index cd55e46e7..392ba57e4 100644 --- a/src/Artemis.UI/Services/ProfileEditorService.cs +++ b/src/Artemis.UI/Services/ProfileEditorService.cs @@ -139,8 +139,7 @@ namespace Artemis.UI.Services UpdateProfilePreview(); OnSelectedProfileElementUpdated(new ProfileElementEventArgs(SelectedProfileElement)); } - - + public void UpdateProfilePreview() { if (SelectedProfile == null)