mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
Profile editor - Finished most of the refactor except databindings
This commit is contained in:
1 parent
7fff1a593f
commit
fea454ad12
56 files changed
+866
-459
No files matched your search
+7
-10
@@ -11,8 +11,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
{
|
||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private bool _alwaysFinishTimeline;
|
||||
private bool _displayContinuously;
|
||||
private RenderProfileElement _renderProfileElement;
|
||||
private int _transitionerIndex;
|
||||
|
||||
@@ -37,20 +35,22 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
|
||||
public bool DisplayContinuously
|
||||
{
|
||||
get => _displayContinuously;
|
||||
get => RenderProfileElement?.DisplayContinuously ?? false;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _displayContinuously, value)) return;
|
||||
if (RenderProfileElement == null || RenderProfileElement.DisplayContinuously == value) return;
|
||||
RenderProfileElement.DisplayContinuously = value;
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
|
||||
public bool AlwaysFinishTimeline
|
||||
{
|
||||
get => _alwaysFinishTimeline;
|
||||
get => RenderProfileElement?.AlwaysFinishTimeline ?? false;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _alwaysFinishTimeline, value)) return;
|
||||
if (RenderProfileElement == null || RenderProfileElement.AlwaysFinishTimeline == value) return;
|
||||
RenderProfileElement.AlwaysFinishTimeline = value;
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
@@ -70,12 +70,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e)
|
||||
{
|
||||
RenderProfileElement = e.RenderProfileElement;
|
||||
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
|
||||
|
||||
_displayContinuously = RenderProfileElement?.DisplayContinuously ?? false;
|
||||
NotifyOfPropertyChange(nameof(DisplayContinuously));
|
||||
_alwaysFinishTimeline = RenderProfileElement?.AlwaysFinishTimeline ?? false;
|
||||
NotifyOfPropertyChange(nameof(AlwaysFinishTimeline));
|
||||
NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled));
|
||||
|
||||
if (e.RenderProfileElement == null)
|
||||
{
|
||||
|
||||
+1
-5
@@ -4,14 +4,10 @@
|
||||
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:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
|
||||
xmlns:dd="urn:gong-wpf-dragdrop"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:DataBindingViewModel}">
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
||||
+16
-6
@@ -36,7 +36,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_dataBindingsVmFactory = dataBindingsVmFactory;
|
||||
|
||||
DisplayName = Registration.Property.Name.ToUpper();
|
||||
if (Registration.Member != null)
|
||||
DisplayName = Registration.Member.Name.ToUpper();
|
||||
else
|
||||
DisplayName = Registration.LayerProperty.PropertyDescription.Name.ToUpper();
|
||||
|
||||
DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingMode)));
|
||||
EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
|
||||
@@ -203,7 +206,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
|
||||
TargetSelectionViewModel.IsEnabled = true;
|
||||
TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DataBinding.SourceDataModel, DataBinding.SourcePropertyPath);
|
||||
TargetSelectionViewModel.FilterTypes = new[] {DataBinding.TargetProperty.PropertyType};
|
||||
TargetSelectionViewModel.FilterTypes = new[] {DataBinding.GetTargetType()};
|
||||
|
||||
UpdateModifierViewModels();
|
||||
|
||||
@@ -225,11 +228,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
|
||||
private void UpdateTestResult()
|
||||
{
|
||||
if (DataBinding == null)
|
||||
{
|
||||
TestInputValue = default;
|
||||
TestResultValue = default;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentValue = TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue();
|
||||
if (currentValue == null && Registration.Property.PropertyType.IsValueType)
|
||||
currentValue = Activator.CreateInstance(Registration.Property.PropertyType);
|
||||
|
||||
TestInputValue = currentValue is TProperty testInputValue ? testInputValue : default;
|
||||
if (currentValue == null)
|
||||
currentValue = default(TProperty);
|
||||
|
||||
TestInputValue = (TProperty) Convert.ChangeType(currentValue, typeof(TProperty));
|
||||
if (DataBinding != null)
|
||||
TestResultValue = DataBinding.GetValue(TestInputValue);
|
||||
else
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<TabControl ItemsSource="{Binding Items}" DisplayMemberPath="DisplayName" Style="{StaticResource MaterialDesignTabControl}">
|
||||
<TabControl ItemsSource="{Binding Items}" SelectedIndex="{Binding SelectedItemIndex}" DisplayMemberPath="DisplayName" Style="{StaticResource MaterialDesignTabControl}">
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
|
||||
|
||||
+9
@@ -9,6 +9,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
{
|
||||
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private int _selectedItemIndex;
|
||||
|
||||
public DataBindingsViewModel(IProfileEditorService profileEditorService, IDataBindingsVmFactory dataBindingsVmFactory)
|
||||
{
|
||||
@@ -19,6 +20,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
CreateDataBindingViewModels();
|
||||
}
|
||||
|
||||
public int SelectedItemIndex
|
||||
{
|
||||
get => _selectedItemIndex;
|
||||
set => SetAndNotify(ref _selectedItemIndex, value);
|
||||
}
|
||||
|
||||
private void CreateDataBindingViewModels()
|
||||
{
|
||||
Items.Clear();
|
||||
@@ -33,6 +40,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
// and creating the actual data bindings
|
||||
foreach (var registration in registrations)
|
||||
ActivateItem(_dataBindingsVmFactory.DataBindingViewModel(registration));
|
||||
|
||||
SelectedItemIndex = 0;
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
Think of things like blur, black & white but also audio visualization etc.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<ListBox ItemsSource="{Binding LayerEffectDescriptors}" SelectedItem="{Binding SelectedLayerEffectDescriptor}" HorizontalContentAlignment="Stretch"
|
||||
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedLayerEffectDescriptor}" HorizontalContentAlignment="Stretch"
|
||||
Visibility="{Binding HasLayerEffectDescriptors, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
|
||||
|
||||
@@ -310,7 +310,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
|
||||
foreach (var layerEffect in SelectedProfileElement.LayerEffects)
|
||||
{
|
||||
if (LayerPropertyGroups.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect))
|
||||
if (LayerPropertyGroups.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect) || layerEffect.BaseProperties == null)
|
||||
continue;
|
||||
|
||||
// TODO: wat?
|
||||
|
||||
+87
-66
@@ -11,6 +11,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
public class LayerPropertyGroupViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
||||
private bool _isVisible;
|
||||
|
||||
public LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, ILayerPropertyVmFactory layerPropertyVmFactory)
|
||||
{
|
||||
@@ -21,6 +22,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
|
||||
TreeGroupViewModel = layerPropertyVmFactory.TreeGroupViewModel(this);
|
||||
TimelineGroupViewModel = layerPropertyVmFactory.TimelineGroupViewModel(this);
|
||||
|
||||
LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged;
|
||||
IsVisible = !LayerPropertyGroup.IsHidden;
|
||||
|
||||
PopulateChildren();
|
||||
}
|
||||
|
||||
@@ -29,7 +34,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
public TimelineGroupViewModel TimelineGroupViewModel { get; }
|
||||
public BindableCollection<PropertyChangedBase> Children { get; }
|
||||
|
||||
public bool IsVisible => !LayerPropertyGroup.IsHidden;
|
||||
public bool IsVisible
|
||||
{
|
||||
get => _isVisible;
|
||||
set => SetAndNotify(ref _isVisible, value);
|
||||
}
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
@@ -41,6 +50,83 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LayerPropertyGroup.VisibilityChanged -= LayerPropertyGroupOnVisibilityChanged;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is IDisposable disposableChild)
|
||||
disposableChild.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateOrder(int order)
|
||||
{
|
||||
LayerPropertyGroup.LayerEffect.Order = order;
|
||||
NotifyOfPropertyChange(nameof(IsExpanded));
|
||||
}
|
||||
|
||||
public List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels(bool expandedOnly)
|
||||
{
|
||||
var result = new List<ITimelineKeyframeViewModel>();
|
||||
if (expandedOnly && !IsExpanded)
|
||||
return result;
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is LayerPropertyViewModel layerPropertyViewModel)
|
||||
result.AddRange(layerPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframeViewModels());
|
||||
else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
|
||||
result.AddRange(layerPropertyGroupViewModel.GetAllKeyframeViewModels(expandedOnly));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the keyframes between the <paramref name="start" /> and <paramref name="end" /> position from this property
|
||||
/// group
|
||||
/// </summary>
|
||||
/// <param name="start">The position at which to start removing keyframes, if null this will start at the first keyframe</param>
|
||||
/// <param name="end">The position at which to start removing keyframes, if null this will end at the last keyframe</param>
|
||||
public virtual void WipeKeyframes(TimeSpan? start, TimeSpan? end)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is LayerPropertyViewModel layerPropertyViewModel)
|
||||
layerPropertyViewModel.TimelinePropertyViewModel.WipeKeyframes(start, end);
|
||||
else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
|
||||
layerPropertyGroupViewModel.WipeKeyframes(start, end);
|
||||
}
|
||||
|
||||
TimelineGroupViewModel.UpdateKeyframePositions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shifts the keyframes between the <paramref name="start" /> and <paramref name="end" /> position by the provided
|
||||
/// <paramref name="amount" />
|
||||
/// </summary>
|
||||
/// <param name="start">The position at which to start shifting keyframes, if null this will start at the first keyframe</param>
|
||||
/// <param name="end">The position at which to start shifting keyframes, if null this will end at the last keyframe</param>
|
||||
/// <param name="amount">The amount to shift the keyframes for</param>
|
||||
public void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is LayerPropertyViewModel layerPropertyViewModel)
|
||||
layerPropertyViewModel.TimelinePropertyViewModel.ShiftKeyframes(start, end, amount);
|
||||
else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
|
||||
layerPropertyGroupViewModel.ShiftKeyframes(start, end, amount);
|
||||
}
|
||||
|
||||
TimelineGroupViewModel.UpdateKeyframePositions();
|
||||
}
|
||||
|
||||
private void LayerPropertyGroupOnVisibilityChanged(object sender, EventArgs e)
|
||||
{
|
||||
IsVisible = !LayerPropertyGroup.IsHidden;
|
||||
}
|
||||
|
||||
private void PopulateChildren()
|
||||
{
|
||||
// Get all properties and property groups and create VMs for them
|
||||
@@ -66,70 +152,5 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
Children.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerPropertyGroup));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is IDisposable disposableChild)
|
||||
disposableChild.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateOrder(int order)
|
||||
{
|
||||
LayerPropertyGroup.LayerEffect.Order = order;
|
||||
NotifyOfPropertyChange(nameof(IsExpanded));
|
||||
}
|
||||
|
||||
public List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels(bool expandedOnly)
|
||||
{
|
||||
var result = new List<ITimelineKeyframeViewModel>();
|
||||
if (expandedOnly == IsExpanded)
|
||||
return result;
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is LayerPropertyViewModel layerPropertyViewModel)
|
||||
result.AddRange(layerPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframeViewModels());
|
||||
else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
|
||||
result.AddRange(layerPropertyGroupViewModel.GetAllKeyframeViewModels(expandedOnly));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the keyframes between the <paramref name="start"/> and <paramref name="end"/> position from this property group
|
||||
/// </summary>
|
||||
/// <param name="start">The position at which to start removing keyframes, if null this will start at the first keyframe</param>
|
||||
/// <param name="end">The position at which to start removing keyframes, if null this will end at the last keyframe</param>
|
||||
public virtual void WipeKeyframes(TimeSpan? start, TimeSpan? end)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is LayerPropertyViewModel layerPropertyViewModel)
|
||||
layerPropertyViewModel.TimelinePropertyViewModel.WipeKeyframes(start, end);
|
||||
else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
|
||||
layerPropertyGroupViewModel.WipeKeyframes(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shifts the keyframes between the <paramref name="start"/> and <paramref name="end"/> position by the provided <paramref name="amount"/>
|
||||
/// </summary>
|
||||
/// <param name="start">The position at which to start shifting keyframes, if null this will start at the first keyframe</param>
|
||||
/// <param name="end">The position at which to start shifting keyframes, if null this will end at the last keyframe</param>
|
||||
/// <param name="amount">The amount to shift the keyframes for</param>
|
||||
public void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is LayerPropertyViewModel layerPropertyViewModel)
|
||||
layerPropertyViewModel.TimelinePropertyViewModel.ShiftKeyframes(start, end, amount);
|
||||
else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
|
||||
layerPropertyGroupViewModel.ShiftKeyframes(start, end, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,14 @@ using Artemis.Core;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
{
|
||||
public class LayerPropertyViewModel : PropertyChangedBase, IDisposable
|
||||
{
|
||||
private bool _isVisible;
|
||||
|
||||
public LayerPropertyViewModel(ILayerProperty layerProperty, IPropertyVmFactory propertyVmFactory)
|
||||
{
|
||||
LayerProperty = layerProperty;
|
||||
@@ -23,8 +23,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
public ITreePropertyViewModel TreePropertyViewModel { get; }
|
||||
public ITimelinePropertyViewModel TimelinePropertyViewModel { get; }
|
||||
|
||||
public bool IsVisible => !LayerProperty.IsHidden;
|
||||
|
||||
public bool IsVisible
|
||||
{
|
||||
get => _isVisible;
|
||||
set => SetAndNotify(ref _isVisible, value);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TreePropertyViewModel?.Dispose();
|
||||
|
||||
+29
-10
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Services;
|
||||
@@ -19,24 +20,17 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
KeyframePositions = new BindableCollection<double>();
|
||||
|
||||
_profileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
LayerPropertyGroupViewModel.PropertyChanged += LayerPropertyGroupViewModelOnPropertyChanged;
|
||||
|
||||
UpdateKeyframePositions();
|
||||
}
|
||||
|
||||
|
||||
public LayerPropertyGroupViewModel LayerPropertyGroupViewModel { get; }
|
||||
public LayerPropertyGroup LayerPropertyGroup { get; }
|
||||
|
||||
public BindableCollection<double> KeyframePositions { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e)
|
||||
{
|
||||
UpdateKeyframePositions();
|
||||
}
|
||||
|
||||
public void UpdateKeyframePositions()
|
||||
{
|
||||
KeyframePositions.Clear();
|
||||
@@ -44,5 +38,30 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
.GetAllKeyframeViewModels(false)
|
||||
.Select(p => p.Position.TotalSeconds * _profileEditorService.PixelsPerSecond));
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
LayerPropertyGroupViewModel.PropertyChanged -= LayerPropertyGroupViewModelOnPropertyChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void LayerPropertyGroupViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(LayerPropertyGroupViewModel.IsExpanded))
|
||||
UpdateKeyframePositions();
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnPixelsPerSecondChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdateKeyframePositions();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+19
-19
@@ -20,16 +20,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
LayerPropertyKeyframe = layerPropertyKeyframe;
|
||||
EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
|
||||
|
||||
_profileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
LayerPropertyKeyframe.PropertyChanged += LayerPropertyKeyframeOnPropertyChanged;
|
||||
}
|
||||
|
||||
public LayerPropertyKeyframe<T> LayerPropertyKeyframe { get; }
|
||||
|
||||
public BindableCollection<TimelineEasingViewModel> EasingViewModels
|
||||
{
|
||||
get => _easingViewModels;
|
||||
set => SetAndNotify(ref _easingViewModels, value);
|
||||
}
|
||||
public LayerPropertyKeyframe<T> LayerPropertyKeyframe { get; }
|
||||
public BindableCollection<TimelineEasingViewModel> EasingViewModels { get; }
|
||||
|
||||
public double X
|
||||
{
|
||||
@@ -53,6 +52,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
LayerPropertyKeyframe.PropertyChanged -= LayerPropertyKeyframeOnPropertyChanged;
|
||||
|
||||
foreach (var timelineEasingViewModel in EasingViewModels)
|
||||
@@ -65,6 +65,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
Timestamp = $"{Math.Floor(LayerPropertyKeyframe.Position.TotalSeconds):00}.{LayerPropertyKeyframe.Position.Milliseconds:000}";
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
private void LayerPropertyKeyframeOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(LayerPropertyKeyframe.Position))
|
||||
@@ -73,7 +78,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
#region Easing
|
||||
|
||||
public void CreateEasingViewModels()
|
||||
public void PopulateEasingViewModels()
|
||||
{
|
||||
if (EasingViewModels.Any())
|
||||
return;
|
||||
@@ -86,6 +91,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
timelineEasingViewModel.EasingModeSelected += TimelineEasingViewModelOnEasingModeSelected;
|
||||
}
|
||||
|
||||
public void ClearEasingViewModels()
|
||||
{
|
||||
EasingViewModels.Clear();
|
||||
}
|
||||
|
||||
private void TimelineEasingViewModelOnEasingModeSelected(object sender, EventArgs e)
|
||||
{
|
||||
SelectEasingMode((TimelineEasingViewModel) sender);
|
||||
@@ -150,18 +160,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
#region Context menu actions
|
||||
|
||||
public void ContextMenuOpening()
|
||||
{
|
||||
CreateEasingViewModels();
|
||||
}
|
||||
|
||||
public void ContextMenuClosing()
|
||||
{
|
||||
foreach (var timelineEasingViewModel in EasingViewModels)
|
||||
timelineEasingViewModel.EasingModeSelected -= TimelineEasingViewModelOnEasingModeSelected;
|
||||
EasingViewModels.Clear();
|
||||
}
|
||||
|
||||
public void Copy()
|
||||
{
|
||||
var newKeyframe = new LayerPropertyKeyframe<T>(
|
||||
@@ -211,6 +209,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
#region Context menu actions
|
||||
|
||||
void PopulateEasingViewModels();
|
||||
void ClearEasingViewModels();
|
||||
void Copy();
|
||||
void Delete();
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@
|
||||
MouseDown="{s:Action KeyframeMouseDown}"
|
||||
MouseUp="{s:Action KeyframeMouseUp}"
|
||||
MouseMove="{s:Action KeyframeMouseMove}"
|
||||
ContextMenuOpening="{s:Action ContextMenuOpening}"
|
||||
ContextMenuOpening="{s:Action ContextMenuOpening}"
|
||||
ContextMenuClosing="{s:Action ContextMenuClosing}">
|
||||
<Ellipse.Style>
|
||||
<Style TargetType="{x:Type Ellipse}">
|
||||
|
||||
+25
-3
@@ -19,6 +19,25 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
LayerProperty = layerProperty;
|
||||
LayerPropertyViewModel = layerPropertyViewModel;
|
||||
|
||||
LayerProperty.KeyframesToggled += LayerPropertyOnKeyframesToggled;
|
||||
LayerProperty.KeyframeAdded += LayerPropertyOnKeyframeAdded;
|
||||
LayerProperty.KeyframeRemoved += LayerPropertyOnKeyframeRemoved;
|
||||
UpdateKeyframes();
|
||||
}
|
||||
|
||||
private void LayerPropertyOnKeyframesToggled(object sender, LayerPropertyEventArgs<T> e)
|
||||
{
|
||||
UpdateKeyframes();
|
||||
}
|
||||
|
||||
private void LayerPropertyOnKeyframeRemoved(object sender, LayerPropertyEventArgs<T> e)
|
||||
{
|
||||
UpdateKeyframes();
|
||||
}
|
||||
|
||||
private void LayerPropertyOnKeyframeAdded(object sender, LayerPropertyEventArgs<T> e)
|
||||
{
|
||||
UpdateKeyframes();
|
||||
}
|
||||
|
||||
@@ -55,8 +74,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
start ??= TimeSpan.Zero;
|
||||
end ??= TimeSpan.MaxValue;
|
||||
|
||||
var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position <= end).ToList();
|
||||
foreach (var keyframe in toShift)
|
||||
var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position < end).ToList();
|
||||
foreach (var keyframe in toShift)
|
||||
LayerProperty.RemoveKeyframe(keyframe);
|
||||
|
||||
UpdateKeyframes();
|
||||
@@ -67,7 +86,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
start ??= TimeSpan.Zero;
|
||||
end ??= TimeSpan.MaxValue;
|
||||
|
||||
var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position <= end).ToList();
|
||||
var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position < end).ToList();
|
||||
foreach (var keyframe in toShift)
|
||||
keyframe.Position += amount;
|
||||
|
||||
@@ -76,6 +95,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LayerProperty.KeyframesToggled -= LayerPropertyOnKeyframesToggled;
|
||||
LayerProperty.KeyframeAdded -= LayerPropertyOnKeyframeAdded;
|
||||
LayerProperty.KeyframeRemoved -= LayerPropertyOnKeyframeRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+121
-88
@@ -17,6 +17,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
private bool _showDisableButton;
|
||||
private bool _showRepeatButton;
|
||||
private bool _showSegmentName;
|
||||
private TimeSpan _segmentLength;
|
||||
private double _segmentWidth;
|
||||
private bool _segmentEnabled;
|
||||
private double _segmentStartPosition;
|
||||
|
||||
public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups,
|
||||
IProfileEditorService profileEditorService)
|
||||
@@ -24,55 +28,54 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
ProfileEditorService = profileEditorService;
|
||||
Segment = segment;
|
||||
LayerPropertyGroups = layerPropertyGroups;
|
||||
SelectedProfileElement = ProfileEditorService.SelectedProfileElement;
|
||||
|
||||
switch (Segment)
|
||||
{
|
||||
case SegmentViewModelType.Start:
|
||||
ToolTip = "This segment is played when a layer starts displaying because it's conditions are met";
|
||||
break;
|
||||
case SegmentViewModelType.Main:
|
||||
ToolTip = "This segment is played while a condition is met, either once or on a repeating loop";
|
||||
break;
|
||||
case SegmentViewModelType.End:
|
||||
ToolTip = "This segment is played once a condition is no longer met";
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(segment));
|
||||
}
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
ToolTip = "This segment is played when a layer starts displaying because it's conditions are met";
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
ToolTip = "This segment is played while a condition is met, either once or on a repeating loop";
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
ToolTip = "This segment is played once a condition is no longer met";
|
||||
IsMainSegment = Segment == SegmentViewModelType.Main;
|
||||
|
||||
UpdateDisplay();
|
||||
ProfileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
ProfileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
|
||||
if (ProfileEditorService.SelectedProfileElement != null)
|
||||
ProfileEditorService.SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
public RenderProfileElement SelectedProfileElement { get; }
|
||||
|
||||
public RenderProfileElement SelectedProfileElement => ProfileEditorService.SelectedProfileElement;
|
||||
public SegmentViewModelType Segment { get; }
|
||||
public BindableCollection<LayerPropertyGroupViewModel> LayerPropertyGroups { get; }
|
||||
public IProfileEditorService ProfileEditorService { get; }
|
||||
|
||||
public string ToolTip { get; }
|
||||
public bool IsMainSegment { get; }
|
||||
|
||||
public TimeSpan SegmentLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return Segment switch
|
||||
{
|
||||
SegmentViewModelType.Start => SelectedProfileElement?.StartSegmentLength ?? TimeSpan.Zero,
|
||||
SegmentViewModelType.Main => SelectedProfileElement?.MainSegmentLength ?? TimeSpan.Zero,
|
||||
SegmentViewModelType.End => SelectedProfileElement?.EndSegmentLength ?? TimeSpan.Zero,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
get => _segmentLength;
|
||||
set => SetAndNotify(ref _segmentLength, value);
|
||||
}
|
||||
|
||||
public double SegmentWidth => ProfileEditorService.PixelsPerSecond * SegmentLength.TotalSeconds;
|
||||
public double SegmentWidth
|
||||
{
|
||||
get => _segmentWidth;
|
||||
set => SetAndNotify(ref _segmentWidth, value);
|
||||
}
|
||||
|
||||
public bool SegmentEnabled => SegmentLength != TimeSpan.Zero;
|
||||
public bool IsMainSegment => Segment == SegmentViewModelType.Main;
|
||||
public double SegmentStartPosition
|
||||
{
|
||||
get => _segmentStartPosition;
|
||||
set => SetAndNotify(ref _segmentStartPosition, value);
|
||||
}
|
||||
|
||||
public bool SegmentEnabled
|
||||
{
|
||||
get => _segmentEnabled;
|
||||
set => SetAndNotify(ref _segmentEnabled, value);
|
||||
}
|
||||
|
||||
// Only the main segment supports this, for any other segment the getter always returns false and the setter does nothing
|
||||
public bool RepeatSegment
|
||||
@@ -95,23 +98,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
}
|
||||
}
|
||||
|
||||
public double SegmentStartPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedProfileElement == null)
|
||||
return 0;
|
||||
|
||||
return Segment switch
|
||||
{
|
||||
SegmentViewModelType.Start => 0,
|
||||
SegmentViewModelType.Main => ProfileEditorService.PixelsPerSecond * SelectedProfileElement.StartSegmentLength.TotalSeconds,
|
||||
SegmentViewModelType.End => ProfileEditorService.PixelsPerSecond * (SelectedProfileElement.StartSegmentLength.TotalSeconds + SelectedProfileElement.MainSegmentLength.TotalSeconds),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowSegmentName
|
||||
{
|
||||
get => _showSegmentName;
|
||||
@@ -130,14 +116,57 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
set => SetAndNotify(ref _showDisableButton, value);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
#region Updating
|
||||
|
||||
private void UpdateHeader()
|
||||
{
|
||||
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
if (SelectedProfileElement != null)
|
||||
SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
if (!IsMainSegment)
|
||||
ShowSegmentName = SegmentWidth > 60;
|
||||
else
|
||||
ShowSegmentName = SegmentWidth > 80;
|
||||
|
||||
ShowRepeatButton = SegmentWidth > 45 && IsMainSegment;
|
||||
ShowDisableButton = SegmentWidth > 25;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (SelectedProfileElement == null)
|
||||
{
|
||||
SegmentLength = TimeSpan.Zero;
|
||||
SegmentStartPosition = 0;
|
||||
SegmentWidth = 0;
|
||||
SegmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
{
|
||||
SegmentLength = SelectedProfileElement.StartSegmentLength;
|
||||
SegmentStartPosition = 0;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
{
|
||||
SegmentLength = SelectedProfileElement.MainSegmentLength;
|
||||
SegmentStartPosition = ProfileEditorService.PixelsPerSecond * SelectedProfileElement.StartSegmentLength.TotalSeconds;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
{
|
||||
SegmentLength = SelectedProfileElement.EndSegmentLength;
|
||||
SegmentStartPosition = ProfileEditorService.PixelsPerSecond * (SelectedProfileElement.StartSegmentLength.TotalSeconds +
|
||||
SelectedProfileElement.MainSegmentLength.TotalSeconds);
|
||||
}
|
||||
|
||||
SegmentWidth = ProfileEditorService.PixelsPerSecond * SegmentLength.TotalSeconds;
|
||||
SegmentEnabled = SegmentLength != TimeSpan.Zero;
|
||||
|
||||
UpdateHeader();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controls
|
||||
|
||||
public void DisableSegment()
|
||||
{
|
||||
var startSegmentEnd = SelectedProfileElement.StartSegmentLength;
|
||||
@@ -164,9 +193,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
SelectedProfileElement.EndSegmentLength = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
NotifyOfPropertyChange(nameof(SegmentEnabled));
|
||||
ShiftNextSegment(SegmentLength - oldSegmentLength);
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
Update();
|
||||
}
|
||||
|
||||
public void EnableSegment()
|
||||
@@ -179,23 +208,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
SelectedProfileElement.EndSegmentLength = TimeSpan.FromSeconds(1);
|
||||
|
||||
NotifyOfPropertyChange(nameof(SegmentEnabled));
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
UpdateDisplay();
|
||||
Update();
|
||||
}
|
||||
|
||||
public void ShiftNextSegment(TimeSpan amount)
|
||||
{
|
||||
var segmentEnd = TimeSpan.Zero;
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
segmentEnd = SelectedProfileElement.StartSegmentLength;
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
segmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
segmentEnd = SelectedProfileElement.TimelineLength;
|
||||
#endregion
|
||||
|
||||
ShiftKeyframes(segmentEnd, null, amount);
|
||||
}
|
||||
#region Mouse events
|
||||
|
||||
public void SegmentMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
@@ -260,18 +279,34 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
SelectedProfileElement.EndSegmentLength = newTime - SelectedProfileElement.StartSegmentLength - SelectedProfileElement.MainSegmentLength;
|
||||
}
|
||||
|
||||
NotifyOfPropertyChange(nameof(SegmentLength));
|
||||
NotifyOfPropertyChange(nameof(SegmentWidth));
|
||||
|
||||
ShiftNextSegment(SegmentLength - oldSegmentLength);
|
||||
UpdateDisplay();
|
||||
Update();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDIsposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
if (SelectedProfileElement != null)
|
||||
SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void ProfileEditorServiceOnProfileElementSelected(object? sender, RenderProfileElementEventArgs e)
|
||||
{
|
||||
if (e.PreviousRenderProfileElement != null)
|
||||
e.PreviousRenderProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
e.RenderProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
if (e.RenderProfileElement != null)
|
||||
e.RenderProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
private void SelectedProfileElementOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
@@ -279,31 +314,29 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
if (e.PropertyName == nameof(RenderProfileElement.StartSegmentLength) ||
|
||||
e.PropertyName == nameof(RenderProfileElement.MainSegmentLength) ||
|
||||
e.PropertyName == nameof(RenderProfileElement.EndSegmentLength))
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(SegmentStartPosition));
|
||||
NotifyOfPropertyChange(nameof(SegmentWidth));
|
||||
}
|
||||
Update();
|
||||
else if (e.PropertyName == nameof(RenderProfileElement.DisplayContinuously))
|
||||
NotifyOfPropertyChange(nameof(RepeatSegment));
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(SegmentWidth));
|
||||
NotifyOfPropertyChange(nameof(SegmentStartPosition));
|
||||
|
||||
UpdateDisplay();
|
||||
Update();
|
||||
}
|
||||
|
||||
private void UpdateDisplay()
|
||||
{
|
||||
if (!IsMainSegment)
|
||||
ShowSegmentName = SegmentWidth > 60;
|
||||
else
|
||||
ShowSegmentName = SegmentWidth > 80;
|
||||
#endregion
|
||||
|
||||
ShowRepeatButton = SegmentWidth > 45 && IsMainSegment;
|
||||
ShowDisableButton = SegmentWidth > 25;
|
||||
public void ShiftNextSegment(TimeSpan amount)
|
||||
{
|
||||
var segmentEnd = TimeSpan.Zero;
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
segmentEnd = SelectedProfileElement.StartSegmentLength;
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
segmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
segmentEnd = SelectedProfileElement.TimelineLength;
|
||||
|
||||
ShiftKeyframes(segmentEnd, null, amount);
|
||||
}
|
||||
|
||||
private void WipeKeyframes(TimeSpan? start, TimeSpan? end)
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
Y1="0"
|
||||
Y2="{Binding ActualHeight, ElementName=TimelineContainerGrid}"
|
||||
HorizontalAlignment="Left"
|
||||
Visibility="{Binding StartSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}" />
|
||||
Visibility="{Binding LayerPropertiesViewModel.StartTimelineSegmentViewModel.SegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}" />
|
||||
<Line Stroke="{StaticResource PrimaryHueDarkBrush}"
|
||||
Opacity="0.5"
|
||||
StrokeDashArray="4 2"
|
||||
@@ -65,7 +65,7 @@
|
||||
X2="{Binding EndSegmentEndPosition}"
|
||||
Y1="0"
|
||||
Y2="{Binding ActualHeight, ElementName=TimelineContainerGrid}"
|
||||
Visibility="{Binding EndSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}" />
|
||||
Visibility="{Binding LayerPropertiesViewModel.EndTimelineSegmentViewModel.SegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}" />
|
||||
|
||||
<!-- Multi-selection rectangle -->
|
||||
<Path x:Name="MultiSelectionPath"
|
||||
|
||||
+81
-61
@@ -15,15 +15,14 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
{
|
||||
public class TimelineViewModel : Screen, IDisposable
|
||||
{
|
||||
private readonly LayerPropertiesViewModel _layerPropertiesViewModel;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private RectangleGeometry _selectionRectangle;
|
||||
|
||||
public TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups, IProfileEditorService profileEditorService)
|
||||
{
|
||||
_layerPropertiesViewModel = layerPropertiesViewModel;
|
||||
_profileEditorService = profileEditorService;
|
||||
|
||||
LayerPropertiesViewModel = layerPropertiesViewModel;
|
||||
LayerPropertyGroups = layerPropertyGroups;
|
||||
SelectionRectangle = new RectangleGeometry();
|
||||
|
||||
@@ -31,10 +30,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
|
||||
if (_profileEditorService.SelectedProfileElement != null)
|
||||
_profileEditorService.SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
Update();
|
||||
}
|
||||
|
||||
public RenderProfileElement SelectedProfileElement => _profileEditorService.SelectedProfileElement;
|
||||
|
||||
public LayerPropertiesViewModel LayerPropertiesViewModel { get; }
|
||||
public BindableCollection<LayerPropertyGroupViewModel> LayerPropertyGroups { get; }
|
||||
|
||||
public RectangleGeometry SelectionRectangle
|
||||
@@ -43,68 +42,57 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
set => SetAndNotify(ref _selectionRectangle, value);
|
||||
}
|
||||
|
||||
public double StartSegmentWidth => _profileEditorService.PixelsPerSecond * (SelectedProfileElement?.StartSegmentLength.TotalSeconds ?? 0);
|
||||
public double StartSegmentEndPosition => StartSegmentWidth;
|
||||
public double MainSegmentWidth => _profileEditorService.PixelsPerSecond * (SelectedProfileElement?.MainSegmentLength.TotalSeconds ?? 0);
|
||||
public double MainSegmentEndPosition => StartSegmentWidth + MainSegmentWidth;
|
||||
public double EndSegmentWidth => _profileEditorService.PixelsPerSecond * (SelectedProfileElement?.EndSegmentLength.TotalSeconds ?? 0);
|
||||
public double EndSegmentEndPosition => StartSegmentWidth + MainSegmentWidth + EndSegmentWidth;
|
||||
public double TotalTimelineWidth => _profileEditorService.PixelsPerSecond * (SelectedProfileElement?.TimelineLength.TotalSeconds ?? 0);
|
||||
|
||||
public bool StartSegmentEnabled => SelectedProfileElement?.StartSegmentLength != TimeSpan.Zero;
|
||||
public bool EndSegmentEnabled => SelectedProfileElement?.EndSegmentLength != TimeSpan.Zero;
|
||||
|
||||
public void Dispose()
|
||||
public double StartSegmentEndPosition
|
||||
{
|
||||
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
if (_profileEditorService.SelectedProfileElement != null)
|
||||
_profileEditorService.SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
get => _startSegmentEndPosition;
|
||||
set => SetAndNotify(ref _startSegmentEndPosition, value);
|
||||
}
|
||||
|
||||
public double MainSegmentEndPosition
|
||||
{
|
||||
get => _mainSegmentEndPosition;
|
||||
set => SetAndNotify(ref _mainSegmentEndPosition, value);
|
||||
}
|
||||
|
||||
public double EndSegmentEndPosition
|
||||
{
|
||||
get => _endSegmentEndPosition;
|
||||
set => SetAndNotify(ref _endSegmentEndPosition, value);
|
||||
}
|
||||
|
||||
public double TotalTimelineWidth
|
||||
{
|
||||
get => _totalTimelineWidth;
|
||||
set => SetAndNotify(ref _totalTimelineWidth, value);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
StartSegmentEndPosition = LayerPropertiesViewModel.StartTimelineSegmentViewModel.SegmentWidth;
|
||||
MainSegmentEndPosition = StartSegmentEndPosition + LayerPropertiesViewModel.MainTimelineSegmentViewModel.SegmentWidth;
|
||||
EndSegmentEndPosition = MainSegmentEndPosition + LayerPropertiesViewModel.EndTimelineSegmentViewModel.SegmentWidth;
|
||||
|
||||
TotalTimelineWidth = EndSegmentEndPosition;
|
||||
}
|
||||
|
||||
private void SelectedProfileElementOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(_profileEditorService.SelectedProfileElement.StartSegmentLength))
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(StartSegmentWidth));
|
||||
NotifyOfPropertyChange(nameof(StartSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(StartSegmentEnabled));
|
||||
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
|
||||
}
|
||||
else if (e.PropertyName == nameof(_profileEditorService.SelectedProfileElement.MainSegmentLength))
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(MainSegmentWidth));
|
||||
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
|
||||
}
|
||||
else if (e.PropertyName == nameof(_profileEditorService.SelectedProfileElement.EndSegmentLength))
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(EndSegmentWidth));
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEnabled));
|
||||
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
|
||||
}
|
||||
Update();
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnPixelsPerSecondChanged(object sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(StartSegmentWidth));
|
||||
NotifyOfPropertyChange(nameof(StartSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(MainSegmentWidth));
|
||||
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(EndSegmentWidth));
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
|
||||
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
|
||||
Update();
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnProfileElementSelected(object? sender, RenderProfileElementEventArgs e)
|
||||
{
|
||||
if (e.PreviousRenderProfileElement != null)
|
||||
e.PreviousRenderProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
e.RenderProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
if (e.RenderProfileElement != null)
|
||||
e.RenderProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
#region Command handlers
|
||||
@@ -139,26 +127,42 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
public void KeyframeMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
var viewModel = (sender as Ellipse)?.DataContext as ITimelineKeyframeViewModel;
|
||||
if (viewModel == null)
|
||||
return;
|
||||
if (sender is Ellipse ellipse && ellipse.DataContext is ITimelineKeyframeViewModel viewModel)
|
||||
{
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
MoveSelectedKeyframes(GetCursorTime(e.GetPosition(View)), viewModel);
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
MoveSelectedKeyframes(GetCursorTime(e.GetPosition(View)), viewModel);
|
||||
|
||||
e.Handled = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
#region Context menu actions
|
||||
|
||||
public void ContextMenuOpening(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is Ellipse ellipse && ellipse.DataContext is ITimelineKeyframeViewModel viewModel)
|
||||
viewModel.PopulateEasingViewModels();
|
||||
}
|
||||
|
||||
public void ContextMenuClosing(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is Ellipse ellipse && ellipse.DataContext is ITimelineKeyframeViewModel viewModel)
|
||||
viewModel.ClearEasingViewModels();
|
||||
}
|
||||
|
||||
public void Copy(ITimelineKeyframeViewModel viewModel)
|
||||
{
|
||||
viewModel.Copy();
|
||||
// viewModel.Copy();
|
||||
var keyframeViewModels = GetAllKeyframeViewModels();
|
||||
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
|
||||
keyframeViewModel.Copy();
|
||||
}
|
||||
|
||||
public void Delete(ITimelineKeyframeViewModel viewModel)
|
||||
{
|
||||
viewModel.Delete();
|
||||
var keyframeViewModels = GetAllKeyframeViewModels();
|
||||
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
|
||||
keyframeViewModel.Delete();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -220,7 +224,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
|
||||
keyframeViewModel.ApplyOffsetToKeyframe(sourceKeyframeViewModel);
|
||||
|
||||
_layerPropertiesViewModel.ProfileEditorService.UpdateProfilePreview();
|
||||
_profileEditorService.UpdateProfilePreview();
|
||||
}
|
||||
|
||||
|
||||
@@ -237,6 +241,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
private Point _mouseDragStartPoint;
|
||||
private bool _mouseDragging;
|
||||
private double _startSegmentEndPosition;
|
||||
private double _mainSegmentEndPosition;
|
||||
private double _endSegmentEndPosition;
|
||||
private double _totalTimelineWidth;
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
public void TimelineCanvasMouseDown(object sender, MouseButtonEventArgs e)
|
||||
@@ -335,5 +343,17 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
if (_profileEditorService.SelectedProfileElement != null)
|
||||
_profileEditorService.SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
|
||||
PropertyInputViewModel = _profileEditorService.CreatePropertyInputViewModel(LayerProperty);
|
||||
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
LayerProperty.VisibilityChanged += LayerPropertyOnVisibilityChanged;
|
||||
|
||||
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
|
||||
}
|
||||
|
||||
public LayerProperty<T> LayerProperty { get; }
|
||||
@@ -51,6 +54,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
_propertyInputViewModel?.Dispose();
|
||||
_profileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
LayerProperty.VisibilityChanged -= LayerPropertyOnVisibilityChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -83,6 +87,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
NotifyOfPropertyChange(nameof(DataBindingsOpen));
|
||||
}
|
||||
|
||||
private void LayerPropertyOnVisibilityChanged(object? sender, LayerPropertyEventArgs<T> e)
|
||||
{
|
||||
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
@@ -10,8 +11,10 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
public FolderViewModel(ProfileElement folder,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
IProfileTreeVmFactory profileTreeVmFactory) :
|
||||
base(folder, profileEditorService, dialogService, profileTreeVmFactory)
|
||||
IProfileTreeVmFactory profileTreeVmFactory,
|
||||
ILayerBrushService layerBrushService,
|
||||
ISurfaceService surfaceService) :
|
||||
base(folder, profileEditorService, dialogService, profileTreeVmFactory, layerBrushService, surfaceService)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
@@ -9,8 +10,10 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
public LayerViewModel(ProfileElement layer,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
IProfileTreeVmFactory profileTreeVmFactory) :
|
||||
base(layer, profileEditorService, dialogService, profileTreeVmFactory)
|
||||
IProfileTreeVmFactory profileTreeVmFactory,
|
||||
ILayerBrushService layerBrushService,
|
||||
ISurfaceService surfaceService) :
|
||||
base(layer, profileEditorService, dialogService, profileTreeVmFactory, layerBrushService, surfaceService)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.Dialogs;
|
||||
@@ -16,16 +17,22 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IProfileTreeVmFactory _profileTreeVmFactory;
|
||||
private readonly ILayerBrushService _layerBrushService;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
private ProfileElement _profileElement;
|
||||
|
||||
protected TreeItemViewModel(ProfileElement profileElement,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
IProfileTreeVmFactory profileTreeVmFactory)
|
||||
IProfileTreeVmFactory profileTreeVmFactory,
|
||||
ILayerBrushService layerBrushService,
|
||||
ISurfaceService surfaceService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_dialogService = dialogService;
|
||||
_profileTreeVmFactory = profileTreeVmFactory;
|
||||
_layerBrushService = layerBrushService;
|
||||
_surfaceService = surfaceService;
|
||||
|
||||
ProfileElement = profileElement;
|
||||
|
||||
@@ -129,8 +136,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
if (!SupportsChildren)
|
||||
throw new ArtemisUIException("Cannot add a layer to a profile element of type " + ProfileElement.GetType().Name);
|
||||
|
||||
var _ = new Layer(ProfileElement, "New layer");
|
||||
var layer = new Layer(ProfileElement, "New layer");
|
||||
layer.ChangeLayerBrush(_layerBrushService.GetDefaultLayerBrush());
|
||||
layer.AddLeds(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds));
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
_profileEditorService.ChangeSelectedProfileElement(layer);
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
@@ -167,6 +177,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
parent.RemoveExistingElement(this);
|
||||
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
_profileEditorService.ChangeSelectedProfileElement(null);
|
||||
}
|
||||
|
||||
public void UpdateProfileElements()
|
||||
|
||||
+19
-12
@@ -1,8 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Properties;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
@@ -10,10 +12,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
|
||||
{
|
||||
public class SelectionToolViewModel : VisualizationToolViewModel
|
||||
{
|
||||
private readonly ILayerBrushService _layerBrushService;
|
||||
private Rect _dragRectangle;
|
||||
|
||||
public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService)
|
||||
public SelectionToolViewModel(ProfileViewModel profileViewModel,
|
||||
IProfileEditorService profileEditorService,
|
||||
ILayerBrushService layerBrushService) : base(profileViewModel, profileEditorService)
|
||||
{
|
||||
_layerBrushService = layerBrushService;
|
||||
using (var stream = new MemoryStream(Resources.aero_crosshair))
|
||||
{
|
||||
Cursor = new Cursor(stream);
|
||||
@@ -52,20 +58,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
|
||||
}
|
||||
// If no layer selected, apply it to a new layer in the selected folder
|
||||
else if (ProfileEditorService.SelectedProfileElement is Folder folder)
|
||||
{
|
||||
var newLayer = new Layer(folder, "New layer");
|
||||
newLayer.AddLeds(selectedLeds);
|
||||
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
CreateLayer(folder, selectedLeds);
|
||||
// If no folder selected, apply it to a new layer in the root folder
|
||||
else
|
||||
{
|
||||
var rootFolder = ProfileEditorService.SelectedProfile.GetRootFolder();
|
||||
var newLayer = new Layer(rootFolder, "New layer");
|
||||
newLayer.AddLeds(selectedLeds);
|
||||
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
CreateLayer(rootFolder, selectedLeds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,5 +87,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
|
||||
|
||||
DragRectangle = selectedRect;
|
||||
}
|
||||
|
||||
private void CreateLayer(Folder folder, List<ArtemisLed> selectedLeds)
|
||||
{
|
||||
var newLayer = new Layer(folder, "New layer");
|
||||
newLayer.ChangeLayerBrush(_layerBrushService.GetDefaultLayerBrush());
|
||||
newLayer.AddLeds(selectedLeds);
|
||||
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user