From a0260b53e582674c073d72a6305458372a60a16d Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 19 May 2022 23:48:03 +0200 Subject: [PATCH] Timeline - Fix collapsed keyframes not showing --- src/.idea/.idea.Artemis/.idea/avalonia.xml | 4 ++ .../LayerPropertyKeyframeEventArgs.cs | 20 +++++++ .../Profile/LayerProperties/ILayerProperty.cs | 4 +- .../Profile/LayerProperties/LayerProperty.cs | 18 +++--- .../Properties/PropertyGroupViewModel.cs | 51 ++++++++++++---- .../Panels/Properties/PropertyViewModel.cs | 28 ++++++++- .../Properties/PropertyViewModelBase.cs | 10 ++++ .../Timeline/ITimelinePropertyViewModel.cs | 2 + .../Keyframes/TimelineKeyframeViewModel.cs | 14 ++--- .../Timeline/TimelineGroupViewModel.cs | 35 ++++++----- .../Timeline/TimelinePropertyViewModel.cs | 59 ++++++++++--------- .../Properties/Tree/TreeGroupViewModel.cs | 2 +- 12 files changed, 170 insertions(+), 77 deletions(-) create mode 100644 src/Artemis.Core/Events/Profiles/LayerPropertyKeyframeEventArgs.cs create mode 100644 src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModelBase.cs diff --git a/src/.idea/.idea.Artemis/.idea/avalonia.xml b/src/.idea/.idea.Artemis/.idea/avalonia.xml index 5d5fcf5ff..b11284c50 100644 --- a/src/.idea/.idea.Artemis/.idea/avalonia.xml +++ b/src/.idea/.idea.Artemis/.idea/avalonia.xml @@ -34,12 +34,14 @@ + + @@ -47,6 +49,8 @@ + + diff --git a/src/Artemis.Core/Events/Profiles/LayerPropertyKeyframeEventArgs.cs b/src/Artemis.Core/Events/Profiles/LayerPropertyKeyframeEventArgs.cs new file mode 100644 index 000000000..962240239 --- /dev/null +++ b/src/Artemis.Core/Events/Profiles/LayerPropertyKeyframeEventArgs.cs @@ -0,0 +1,20 @@ +using System; + +namespace Artemis.Core +{ + /// + /// Provides data for layer property events. + /// + public class LayerPropertyKeyframeEventArgs : EventArgs + { + internal LayerPropertyKeyframeEventArgs(ILayerPropertyKeyframe keyframe) + { + Keyframe = keyframe; + } + + /// + /// Gets the keyframe this event is related to + /// + public ILayerPropertyKeyframe Keyframe { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs index 13e3ada28..009852cec 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs @@ -144,11 +144,11 @@ namespace Artemis.Core /// /// Occurs when a new keyframe was added to the layer property /// - public event EventHandler? KeyframeAdded; + public event EventHandler? KeyframeAdded; /// /// Occurs when a keyframe was removed from the layer property /// - public event EventHandler? KeyframeRemoved; + public event EventHandler? KeyframeRemoved; } } \ 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 fa1e96f0e..28e2d5db4 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -322,7 +322,7 @@ namespace Artemis.Core SortKeyframes(); ReapplyUpdate(); - OnKeyframeAdded(); + OnKeyframeAdded(keyframe); } /// @@ -357,7 +357,7 @@ namespace Artemis.Core SortKeyframes(); ReapplyUpdate(); - OnKeyframeRemoved(); + OnKeyframeRemoved(keyframe); } /// @@ -604,10 +604,10 @@ namespace Artemis.Core public event EventHandler? KeyframesToggled; /// - public event EventHandler? KeyframeAdded; + public event EventHandler? KeyframeAdded; /// - public event EventHandler? KeyframeRemoved; + public event EventHandler? KeyframeRemoved; /// /// Invokes the event @@ -645,17 +645,19 @@ namespace Artemis.Core /// /// Invokes the event /// - protected virtual void OnKeyframeAdded() + /// + protected virtual void OnKeyframeAdded(ILayerPropertyKeyframe keyframe) { - KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this)); + KeyframeAdded?.Invoke(this, new LayerPropertyKeyframeEventArgs(keyframe)); } /// /// Invokes the event /// - protected virtual void OnKeyframeRemoved() + /// + protected virtual void OnKeyframeRemoved(ILayerPropertyKeyframe keyframe) { - KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this)); + KeyframeRemoved?.Invoke(this, new LayerPropertyKeyframeEventArgs(keyframe)); } #endregion diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs index faa1e3841..85ca45af1 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs @@ -12,29 +12,32 @@ using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes; using Artemis.UI.Screens.ProfileEditor.Properties.Tree; using Artemis.UI.Shared; using Artemis.UI.Shared.Services.PropertyInput; +using DynamicData; +using DynamicData.Binding; namespace Artemis.UI.Screens.ProfileEditor.Properties; -public class PropertyGroupViewModel : ViewModelBase, IDisposable +public class PropertyGroupViewModel : PropertyViewModelBase, IDisposable { private readonly ILayerPropertyVmFactory _layerPropertyVmFactory; private readonly IPropertyInputService _propertyInputService; private bool _hasChildren; private bool _isExpanded; private bool _isVisible; + private ReadOnlyObservableCollection _keyframes = null!; + private IDisposable _keyframeSubscription = null!; public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService) { _layerPropertyVmFactory = layerPropertyVmFactory; _propertyInputService = propertyInputService; - Children = new ObservableCollection(); LayerPropertyGroup = layerPropertyGroup; TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this); TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this); LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged; _isVisible = !LayerPropertyGroup.IsHidden; - + PopulateChildren(); } @@ -43,14 +46,13 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable _layerPropertyVmFactory = layerPropertyVmFactory; _propertyInputService = propertyInputService; LayerBrush = layerBrush; - Children = new ObservableCollection(); LayerPropertyGroup = layerPropertyGroup; TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this); TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this); LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged; _isVisible = !LayerPropertyGroup.IsHidden; - + PopulateChildren(); } @@ -59,18 +61,17 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable _layerPropertyVmFactory = layerPropertyVmFactory; _propertyInputService = propertyInputService; LayerEffect = layerEffect; - Children = new ObservableCollection(); LayerPropertyGroup = layerPropertyGroup; TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this); TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this); LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged; _isVisible = !LayerPropertyGroup.IsHidden; - + PopulateChildren(); } - public ObservableCollection Children { get; } + public ObservableCollection Children { get; private set; } = null!; public LayerPropertyGroup LayerPropertyGroup { get; } public BaseLayerBrush? LayerBrush { get; } public BaseLayerEffect? LayerEffect { get; } @@ -96,13 +97,32 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable set => RaiseAndSetIfChanged(ref _hasChildren, value); } + public override ReadOnlyObservableCollection Keyframes => _keyframes; + + public List GetAllKeyframes(bool expandedOnly) + { + List result = new(); + if (expandedOnly && !IsExpanded) + return result; + + foreach (PropertyViewModelBase child in Children) + { + if (child is PropertyViewModel profileElementPropertyViewModel) + result.AddRange(profileElementPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframes()); + else if (child is PropertyGroupViewModel profileElementPropertyGroupViewModel) + result.AddRange(profileElementPropertyGroupViewModel.GetAllKeyframes(expandedOnly)); + } + + return result; + } + public List GetAllKeyframeViewModels(bool expandedOnly) { List result = new(); if (expandedOnly && !IsExpanded) return result; - foreach (ViewModelBase child in Children) + foreach (PropertyViewModelBase child in Children) { if (child is PropertyViewModel profileElementPropertyViewModel) result.AddRange(profileElementPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframeViewModels()); @@ -115,6 +135,8 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable private void PopulateChildren() { + Children = new ObservableCollection(); + // Get all properties and property groups and create VMs for them // The group has methods for getting this without reflection but then we lose the order of the properties as they are defined on the group // Sorting is done to ensure properties defined by the Core (such as on layers) are always on top. @@ -138,7 +160,14 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable } } - HasChildren = Children.Any(i => i is PropertyViewModel {IsVisible: true} || i is PropertyGroupViewModel {IsVisible: true}); + HasChildren = Children.Any(i => i is PropertyViewModel {IsVisible: true} or PropertyGroupViewModel {IsVisible: true}); + _keyframeSubscription = Children + .ToObservableChangeSet() + .TransformMany(c => c.Keyframes) + .Bind(out ReadOnlyObservableCollection keyframes) + .Subscribe(); + + _keyframes = keyframes; } private void LayerPropertyGroupOnVisibilityChanged(object? sender, EventArgs e) @@ -155,5 +184,7 @@ public class PropertyGroupViewModel : ViewModelBase, IDisposable if (viewModelBase is IDisposable disposable) disposable.Dispose(); } + + _keyframeSubscription.Dispose(); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs index 0dfa5ad45..988876941 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs @@ -1,17 +1,20 @@ using System; +using System.Collections.ObjectModel; using Artemis.Core; using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens.ProfileEditor.Properties.Timeline; using Artemis.UI.Screens.ProfileEditor.Properties.Tree; using Artemis.UI.Shared; +using DynamicData; namespace Artemis.UI.Screens.ProfileEditor.Properties; -public class PropertyViewModel : ViewModelBase, IDisposable +public class PropertyViewModel : PropertyViewModelBase, IDisposable { private bool _isExpanded; private bool _isHighlighted; private bool _isVisible; + private readonly SourceList _keyframes; public PropertyViewModel(ILayerProperty layerProperty, IPropertyVmFactory propertyVmFactory) { @@ -19,13 +22,22 @@ public class PropertyViewModel : ViewModelBase, IDisposable TreePropertyViewModel = propertyVmFactory.TreePropertyViewModel(LayerProperty, this); TimelinePropertyViewModel = propertyVmFactory.TimelinePropertyViewModel(LayerProperty, this); - LayerProperty.VisibilityChanged += LayerPropertyOnVisibilityChanged; _isVisible = !LayerProperty.IsHidden; + _keyframes = new SourceList(); + _keyframes.Edit(k => k.AddRange(LayerProperty.UntypedKeyframes)); + _keyframes.Connect().Bind(out ReadOnlyObservableCollection keyframes).Subscribe(); + + Keyframes = keyframes; + + LayerProperty.VisibilityChanged += LayerPropertyOnVisibilityChanged; + LayerProperty.KeyframeAdded += LayerPropertyOnKeyframeAdded; + LayerProperty.KeyframeRemoved += LayerPropertyOnKeyframeRemoved; } public ILayerProperty LayerProperty { get; } public ITreePropertyViewModel TreePropertyViewModel { get; } public ITimelinePropertyViewModel TimelinePropertyViewModel { get; } + public override ReadOnlyObservableCollection Keyframes { get; } public bool IsVisible { @@ -50,9 +62,21 @@ public class PropertyViewModel : ViewModelBase, IDisposable IsVisible = !LayerProperty.IsHidden; } + private void LayerPropertyOnKeyframeAdded(object? sender, LayerPropertyKeyframeEventArgs e) + { + _keyframes.Add(e.Keyframe); + } + + private void LayerPropertyOnKeyframeRemoved(object? sender, LayerPropertyKeyframeEventArgs e) + { + _keyframes.Remove(e.Keyframe); + } + /// public void Dispose() { LayerProperty.VisibilityChanged -= LayerPropertyOnVisibilityChanged; + LayerProperty.KeyframeAdded -= LayerPropertyOnKeyframeAdded; + LayerProperty.KeyframeRemoved -= LayerPropertyOnKeyframeRemoved; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModelBase.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModelBase.cs new file mode 100644 index 000000000..0aca9b1d7 --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModelBase.cs @@ -0,0 +1,10 @@ +using System.Collections.ObjectModel; +using Artemis.Core; +using Artemis.UI.Shared; + +namespace Artemis.UI.Screens.ProfileEditor.Properties; + +public abstract class PropertyViewModelBase : ViewModelBase +{ + public abstract ReadOnlyObservableCollection Keyframes { get; } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/ITimelinePropertyViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/ITimelinePropertyViewModel.cs index 4770f4cf9..786773223 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/ITimelinePropertyViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/ITimelinePropertyViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Artemis.Core; using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes; using ReactiveUI; @@ -7,6 +8,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline; public interface ITimelinePropertyViewModel : IReactiveObject { + List GetAllKeyframes(); List GetAllKeyframeViewModels(); void WipeKeyframes(TimeSpan? start, TimeSpan? end); void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount); diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs index 245cdc103..7a6e2618a 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs @@ -29,14 +29,14 @@ public class TimelineKeyframeViewModel : ActivatableViewModelBase, ITimelineK this.WhenActivated(d => { - _isSelected = profileEditorService.ConnectToKeyframes().ToCollection().Select(keyframes => keyframes.Contains(LayerPropertyKeyframe)).ToProperty(this, vm => vm.IsSelected).DisposeWith(d); + _isSelected = profileEditorService.ConnectToKeyframes() + .ToCollection() + .Select(keyframes => keyframes.Contains(LayerPropertyKeyframe)) + .ToProperty(this, vm => vm.IsSelected) + .DisposeWith(d); profileEditorService.ConnectToKeyframes(); - profileEditorService.PixelsPerSecond.Subscribe(p => - { - _pixelsPerSecond = p; - profileEditorService.PixelsPerSecond.Subscribe(_ => Update()).DisposeWith(d); - }).DisposeWith(d); - + profileEditorService.PixelsPerSecond.Subscribe(p => _pixelsPerSecond = p).DisposeWith(d); + profileEditorService.PixelsPerSecond.Subscribe(_ => Update()).DisposeWith(d); this.WhenAnyValue(vm => vm.LayerPropertyKeyframe.Position).Subscribe(_ => Update()).DisposeWith(d); }); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs index a8f8f35fc..62b39efe0 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs @@ -1,48 +1,47 @@ using System; using System.Collections.ObjectModel; -using System.Linq; using Artemis.UI.Shared; using Artemis.UI.Shared.Services.ProfileEditor; using Avalonia.Controls.Mixins; +using DynamicData; +using DynamicData.Binding; using ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline; public class TimelineGroupViewModel : ActivatableViewModelBase { - private ObservableCollection? _keyframePositions; - private ObservableAsPropertyHelper? _pixelsPerSecond; + private ReadOnlyObservableCollection _keyframePositions; + private int _pixelsPerSecond; public TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel, IProfileEditorService profileEditorService) { PropertyGroupViewModel = propertyGroupViewModel; + _keyframePositions = new ReadOnlyObservableCollection(new ObservableCollection()); this.WhenActivated(d => { - _pixelsPerSecond = profileEditorService.PixelsPerSecond.ToProperty(this, vm => vm.PixelsPerSecond).DisposeWith(d); - profileEditorService.PixelsPerSecond.Subscribe(p => UpdateKeyframePositions()).DisposeWith(d); + profileEditorService.PixelsPerSecond.Subscribe(p => _pixelsPerSecond = p).DisposeWith(d); + PropertyGroupViewModel.WhenAnyValue(vm => vm.IsExpanded).Subscribe(_ => this.RaisePropertyChanged(nameof(Children))).DisposeWith(d); - PropertyGroupViewModel.WhenAnyValue(vm => vm.IsExpanded).Subscribe(_ => UpdateKeyframePositions()).DisposeWith(d); - this.WhenAnyValue(vm => vm.PixelsPerSecond).Subscribe(_ => UpdateKeyframePositions()).DisposeWith(d); - UpdateKeyframePositions(); + PropertyGroupViewModel.Keyframes + .ToObservableChangeSet() + .AutoRefreshOnObservable(_ => profileEditorService.PixelsPerSecond) + .Transform(k => k.Position.TotalSeconds * _pixelsPerSecond, true) + .Bind(out ReadOnlyObservableCollection keyframePositions) + .Subscribe() + .DisposeWith(d); + KeyframePositions = keyframePositions; }); } - public int PixelsPerSecond => _pixelsPerSecond?.Value ?? 0; public PropertyGroupViewModel PropertyGroupViewModel { get; } - public ObservableCollection? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null; + public ObservableCollection? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null; - public ObservableCollection? KeyframePositions + public ReadOnlyObservableCollection KeyframePositions { get => _keyframePositions; set => RaiseAndSetIfChanged(ref _keyframePositions, value); } - - private void UpdateKeyframePositions() - { - KeyframePositions = new ObservableCollection(PropertyGroupViewModel - .GetAllKeyframeViewModels(false) - .Select(p => p.Position.TotalSeconds * PixelsPerSecond)); - } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelinePropertyViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelinePropertyViewModel.cs index 354b7c30b..a3f52aa3b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelinePropertyViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelinePropertyViewModel.cs @@ -8,6 +8,7 @@ using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes; using Artemis.UI.Shared; using Artemis.UI.Shared.Services.ProfileEditor; using Avalonia.Controls.Mixins; +using DynamicData; using ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline; @@ -15,60 +16,61 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline; public class TimelinePropertyViewModel : ActivatableViewModelBase, ITimelinePropertyViewModel { private readonly IProfileEditorService _profileEditorService; + private readonly SourceList> _keyframes; + private ObservableAsPropertyHelper? _keyframesEnabled; public TimelinePropertyViewModel(LayerProperty layerProperty, PropertyViewModel propertyViewModel, IProfileEditorService profileEditorService) { _profileEditorService = profileEditorService; LayerProperty = layerProperty; PropertyViewModel = propertyViewModel; - KeyframeViewModels = new ObservableCollection>(); + + _keyframes = new SourceList>(); + + _keyframes.Connect() + // Only show items when keyframes are enabled + .Filter(this.WhenAnyValue(vm => vm.KeyframesEnabled).Select(b => new Func, bool>(_ => b))) + .Transform(k => new TimelineKeyframeViewModel(k, _profileEditorService)) + .Bind(out ReadOnlyObservableCollection> keyframeViewModels) + .Subscribe(); + KeyframeViewModels = keyframeViewModels; this.WhenActivated(d => { - Observable.FromEventPattern(x => LayerProperty.KeyframesToggled += x, x => LayerProperty.KeyframesToggled -= x) - .Subscribe(_ => UpdateKeyframes()) + _keyframesEnabled = LayerProperty.WhenAnyValue(p => p.KeyframesEnabled).ToProperty(this, vm => vm.KeyframesEnabled).DisposeWith(d); + Observable.FromEventPattern(x => LayerProperty.KeyframeAdded += x, x => LayerProperty.KeyframeAdded -= x) + .Subscribe(e => _keyframes.Add((LayerPropertyKeyframe) e.EventArgs.Keyframe)) .DisposeWith(d); - Observable.FromEventPattern(x => LayerProperty.KeyframeAdded += x, x => LayerProperty.KeyframeAdded -= x) - .Subscribe(_ => UpdateKeyframes()) - .DisposeWith(d); - Observable.FromEventPattern(x => LayerProperty.KeyframeRemoved += x, x => LayerProperty.KeyframeRemoved -= x) - .Subscribe(_ => UpdateKeyframes()) + Observable.FromEventPattern(x => LayerProperty.KeyframeRemoved += x, x => LayerProperty.KeyframeRemoved -= x) + .Subscribe(e => _keyframes.Remove((LayerPropertyKeyframe) e.EventArgs.Keyframe)) .DisposeWith(d); - UpdateKeyframes(); + _keyframes.Edit(k => + { + k.Clear(); + k.AddRange(LayerProperty.Keyframes); + }); }); } public LayerProperty LayerProperty { get; } public PropertyViewModel PropertyViewModel { get; } - public ObservableCollection> KeyframeViewModels { get; } + public ReadOnlyObservableCollection> KeyframeViewModels { get; } + public bool KeyframesEnabled => _keyframesEnabled?.Value ?? false; private void UpdateKeyframes() { - // Only show keyframes if they are enabled - if (LayerProperty.KeyframesEnabled) - { - List> keyframes = LayerProperty.Keyframes.ToList(); - - List> toRemove = KeyframeViewModels.Where(t => !keyframes.Contains(t.LayerPropertyKeyframe)).ToList(); - foreach (TimelineKeyframeViewModel timelineKeyframeViewModel in toRemove) - KeyframeViewModels.Remove(timelineKeyframeViewModel); - List> toAdd = keyframes.Where(k => KeyframeViewModels.All(t => t.LayerPropertyKeyframe != k)) - .Select(k => new TimelineKeyframeViewModel(k, _profileEditorService)).ToList(); - foreach (TimelineKeyframeViewModel timelineKeyframeViewModel in toAdd) - KeyframeViewModels.Add(timelineKeyframeViewModel); - } - else - { - KeyframeViewModels.Clear(); - } - foreach (TimelineKeyframeViewModel timelineKeyframeViewModel in KeyframeViewModels) timelineKeyframeViewModel.Update(); } #region Implementation of ITimelinePropertyViewModel + public List GetAllKeyframes() + { + return LayerProperty.KeyframesEnabled ? new List(LayerProperty.Keyframes) : new List(); + } + public List GetAllKeyframeViewModels() { return KeyframeViewModels.Cast().ToList(); @@ -79,7 +81,6 @@ public class TimelinePropertyViewModel : ActivatableViewModelBase, ITimelineP start ??= TimeSpan.Zero; end ??= TimeSpan.MaxValue; - List> toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position < end).ToList(); foreach (LayerPropertyKeyframe keyframe in toShift) LayerProperty.RemoveKeyframe(keyframe); diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupViewModel.cs index f626de8d8..c870e291b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupViewModel.cs @@ -56,7 +56,7 @@ public class TreeGroupViewModel : ActivatableViewModelBase public BaseLayerBrush? LayerBrush => PropertyGroupViewModel.LayerBrush; public BaseLayerEffect? LayerEffect => PropertyGroupViewModel.LayerEffect; public LayerPropertyGroupType GroupType { get; private set; } - public ObservableCollection? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null; + public ObservableCollection? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null; public ReactiveCommand OpenBrushSettings { get; } public ReactiveCommand OpenEffectSettings { get; }