1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Timeline - Fix collapsed keyframes not showing

This commit is contained in:
Robert 2022-05-19 23:48:03 +02:00
parent 4809ebf969
commit a0260b53e5
12 changed files with 170 additions and 77 deletions

View File

@ -34,12 +34,14 @@
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelinePropertyView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/LayerEffectRenameView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreeGroupView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/Properties/Windows/EffectConfigurationWindowView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/ProfileEditorTitleBarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/ProfileEditor/ProfileEditorView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Root/SplashView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
@ -47,6 +49,8 @@
<entry key="Artemis.UI/Screens/Sidebar/SidebarCategoryView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/SidebarView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/VisualScripting/NodeView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugView.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" />

View File

@ -0,0 +1,20 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Provides data for layer property events.
/// </summary>
public class LayerPropertyKeyframeEventArgs : EventArgs
{
internal LayerPropertyKeyframeEventArgs(ILayerPropertyKeyframe keyframe)
{
Keyframe = keyframe;
}
/// <summary>
/// Gets the keyframe this event is related to
/// </summary>
public ILayerPropertyKeyframe Keyframe { get; }
}
}

View File

@ -144,11 +144,11 @@ namespace Artemis.Core
/// <summary>
/// Occurs when a new keyframe was added to the layer property
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? KeyframeAdded;
public event EventHandler<LayerPropertyKeyframeEventArgs>? KeyframeAdded;
/// <summary>
/// Occurs when a keyframe was removed from the layer property
/// </summary>
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
public event EventHandler<LayerPropertyKeyframeEventArgs>? KeyframeRemoved;
}
}

View File

@ -322,7 +322,7 @@ namespace Artemis.Core
SortKeyframes();
ReapplyUpdate();
OnKeyframeAdded();
OnKeyframeAdded(keyframe);
}
/// <inheritdoc />
@ -357,7 +357,7 @@ namespace Artemis.Core
SortKeyframes();
ReapplyUpdate();
OnKeyframeRemoved();
OnKeyframeRemoved(keyframe);
}
/// <summary>
@ -604,10 +604,10 @@ namespace Artemis.Core
public event EventHandler<LayerPropertyEventArgs>? KeyframesToggled;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? KeyframeAdded;
public event EventHandler<LayerPropertyKeyframeEventArgs>? KeyframeAdded;
/// <inheritdoc />
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
public event EventHandler<LayerPropertyKeyframeEventArgs>? KeyframeRemoved;
/// <summary>
/// Invokes the <see cref="Updated" /> event
@ -645,17 +645,19 @@ namespace Artemis.Core
/// <summary>
/// Invokes the <see cref="KeyframeAdded" /> event
/// </summary>
protected virtual void OnKeyframeAdded()
/// <param name="keyframe"></param>
protected virtual void OnKeyframeAdded(ILayerPropertyKeyframe keyframe)
{
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
KeyframeAdded?.Invoke(this, new LayerPropertyKeyframeEventArgs(keyframe));
}
/// <summary>
/// Invokes the <see cref="KeyframeRemoved" /> event
/// </summary>
protected virtual void OnKeyframeRemoved()
/// <param name="keyframe"></param>
protected virtual void OnKeyframeRemoved(ILayerPropertyKeyframe keyframe)
{
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
KeyframeRemoved?.Invoke(this, new LayerPropertyKeyframeEventArgs(keyframe));
}
#endregion

View File

@ -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<ILayerPropertyKeyframe> _keyframes = null!;
private IDisposable _keyframeSubscription = null!;
public PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory, IPropertyInputService propertyInputService)
{
_layerPropertyVmFactory = layerPropertyVmFactory;
_propertyInputService = propertyInputService;
Children = new ObservableCollection<ViewModelBase>();
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<ViewModelBase>();
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<ViewModelBase>();
LayerPropertyGroup = layerPropertyGroup;
TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this);
LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
_isVisible = !LayerPropertyGroup.IsHidden;
PopulateChildren();
}
public ObservableCollection<ViewModelBase> Children { get; }
public ObservableCollection<PropertyViewModelBase> 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<ILayerPropertyKeyframe> Keyframes => _keyframes;
public List<ILayerPropertyKeyframe> GetAllKeyframes(bool expandedOnly)
{
List<ILayerPropertyKeyframe> 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<ITimelineKeyframeViewModel> GetAllKeyframeViewModels(bool expandedOnly)
{
List<ITimelineKeyframeViewModel> 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<PropertyViewModelBase>();
// 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<ILayerPropertyKeyframe> 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();
}
}

View File

@ -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<ILayerPropertyKeyframe> _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<ILayerPropertyKeyframe>();
_keyframes.Edit(k => k.AddRange(LayerProperty.UntypedKeyframes));
_keyframes.Connect().Bind(out ReadOnlyObservableCollection<ILayerPropertyKeyframe> 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<ILayerPropertyKeyframe> 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);
}
/// <inheritdoc />
public void Dispose()
{
LayerProperty.VisibilityChanged -= LayerPropertyOnVisibilityChanged;
LayerProperty.KeyframeAdded -= LayerPropertyOnKeyframeAdded;
LayerProperty.KeyframeRemoved -= LayerPropertyOnKeyframeRemoved;
}
}

View File

@ -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<ILayerPropertyKeyframe> Keyframes { get; }
}

View File

@ -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<ILayerPropertyKeyframe> GetAllKeyframes();
List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels();
void WipeKeyframes(TimeSpan? start, TimeSpan? end);
void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount);

View File

@ -29,14 +29,14 @@ public class TimelineKeyframeViewModel<T> : 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);
});
}

View File

@ -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<double>? _keyframePositions;
private ObservableAsPropertyHelper<int>? _pixelsPerSecond;
private ReadOnlyObservableCollection<double> _keyframePositions;
private int _pixelsPerSecond;
public TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel, IProfileEditorService profileEditorService)
{
PropertyGroupViewModel = propertyGroupViewModel;
_keyframePositions = new ReadOnlyObservableCollection<double>(new ObservableCollection<double>());
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<double> keyframePositions)
.Subscribe()
.DisposeWith(d);
KeyframePositions = keyframePositions;
});
}
public int PixelsPerSecond => _pixelsPerSecond?.Value ?? 0;
public PropertyGroupViewModel PropertyGroupViewModel { get; }
public ObservableCollection<ViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null;
public ObservableCollection<PropertyViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null;
public ObservableCollection<double>? KeyframePositions
public ReadOnlyObservableCollection<double> KeyframePositions
{
get => _keyframePositions;
set => RaiseAndSetIfChanged(ref _keyframePositions, value);
}
private void UpdateKeyframePositions()
{
KeyframePositions = new ObservableCollection<double>(PropertyGroupViewModel
.GetAllKeyframeViewModels(false)
.Select(p => p.Position.TotalSeconds * PixelsPerSecond));
}
}

View File

@ -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<T> : ActivatableViewModelBase, ITimelinePropertyViewModel
{
private readonly IProfileEditorService _profileEditorService;
private readonly SourceList<LayerPropertyKeyframe<T>> _keyframes;
private ObservableAsPropertyHelper<bool>? _keyframesEnabled;
public TimelinePropertyViewModel(LayerProperty<T> layerProperty, PropertyViewModel propertyViewModel, IProfileEditorService profileEditorService)
{
_profileEditorService = profileEditorService;
LayerProperty = layerProperty;
PropertyViewModel = propertyViewModel;
KeyframeViewModels = new ObservableCollection<TimelineKeyframeViewModel<T>>();
_keyframes = new SourceList<LayerPropertyKeyframe<T>>();
_keyframes.Connect()
// Only show items when keyframes are enabled
.Filter(this.WhenAnyValue(vm => vm.KeyframesEnabled).Select(b => new Func<LayerPropertyKeyframe<T>, bool>(_ => b)))
.Transform(k => new TimelineKeyframeViewModel<T>(k, _profileEditorService))
.Bind(out ReadOnlyObservableCollection<TimelineKeyframeViewModel<T>> keyframeViewModels)
.Subscribe();
KeyframeViewModels = keyframeViewModels;
this.WhenActivated(d =>
{
Observable.FromEventPattern<LayerPropertyEventArgs>(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<LayerPropertyKeyframeEventArgs>(x => LayerProperty.KeyframeAdded += x, x => LayerProperty.KeyframeAdded -= x)
.Subscribe(e => _keyframes.Add((LayerPropertyKeyframe<T>) e.EventArgs.Keyframe))
.DisposeWith(d);
Observable.FromEventPattern<LayerPropertyEventArgs>(x => LayerProperty.KeyframeAdded += x, x => LayerProperty.KeyframeAdded -= x)
.Subscribe(_ => UpdateKeyframes())
.DisposeWith(d);
Observable.FromEventPattern<LayerPropertyEventArgs>(x => LayerProperty.KeyframeRemoved += x, x => LayerProperty.KeyframeRemoved -= x)
.Subscribe(_ => UpdateKeyframes())
Observable.FromEventPattern<LayerPropertyKeyframeEventArgs>(x => LayerProperty.KeyframeRemoved += x, x => LayerProperty.KeyframeRemoved -= x)
.Subscribe(e => _keyframes.Remove((LayerPropertyKeyframe<T>) e.EventArgs.Keyframe))
.DisposeWith(d);
UpdateKeyframes();
_keyframes.Edit(k =>
{
k.Clear();
k.AddRange(LayerProperty.Keyframes);
});
});
}
public LayerProperty<T> LayerProperty { get; }
public PropertyViewModel PropertyViewModel { get; }
public ObservableCollection<TimelineKeyframeViewModel<T>> KeyframeViewModels { get; }
public ReadOnlyObservableCollection<TimelineKeyframeViewModel<T>> KeyframeViewModels { get; }
public bool KeyframesEnabled => _keyframesEnabled?.Value ?? false;
private void UpdateKeyframes()
{
// Only show keyframes if they are enabled
if (LayerProperty.KeyframesEnabled)
{
List<LayerPropertyKeyframe<T>> keyframes = LayerProperty.Keyframes.ToList();
List<TimelineKeyframeViewModel<T>> toRemove = KeyframeViewModels.Where(t => !keyframes.Contains(t.LayerPropertyKeyframe)).ToList();
foreach (TimelineKeyframeViewModel<T> timelineKeyframeViewModel in toRemove)
KeyframeViewModels.Remove(timelineKeyframeViewModel);
List<TimelineKeyframeViewModel<T>> toAdd = keyframes.Where(k => KeyframeViewModels.All(t => t.LayerPropertyKeyframe != k))
.Select(k => new TimelineKeyframeViewModel<T>(k, _profileEditorService)).ToList();
foreach (TimelineKeyframeViewModel<T> timelineKeyframeViewModel in toAdd)
KeyframeViewModels.Add(timelineKeyframeViewModel);
}
else
{
KeyframeViewModels.Clear();
}
foreach (TimelineKeyframeViewModel<T> timelineKeyframeViewModel in KeyframeViewModels)
timelineKeyframeViewModel.Update();
}
#region Implementation of ITimelinePropertyViewModel
public List<ILayerPropertyKeyframe> GetAllKeyframes()
{
return LayerProperty.KeyframesEnabled ? new List<ILayerPropertyKeyframe>(LayerProperty.Keyframes) : new List<ILayerPropertyKeyframe>();
}
public List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels()
{
return KeyframeViewModels.Cast<ITimelineKeyframeViewModel>().ToList();
@ -79,7 +81,6 @@ public class TimelinePropertyViewModel<T> : ActivatableViewModelBase, ITimelineP
start ??= TimeSpan.Zero;
end ??= TimeSpan.MaxValue;
List<LayerPropertyKeyframe<T>> toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position < end).ToList();
foreach (LayerPropertyKeyframe<T> keyframe in toShift)
LayerProperty.RemoveKeyframe(keyframe);

View File

@ -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<ViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null;
public ObservableCollection<PropertyViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null;
public ReactiveCommand<Unit, Unit> OpenBrushSettings { get; }
public ReactiveCommand<Unit, Unit> OpenEffectSettings { get; }