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:
parent
4809ebf969
commit
a0260b53e5
4
src/.idea/.idea.Artemis/.idea/avalonia.xml
generated
4
src/.idea/.idea.Artemis/.idea/avalonia.xml
generated
@ -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" />
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user