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

Profile editor - Reimplemented more of the timeline

This commit is contained in:
SpoinkyNL 2020-09-11 22:30:57 +02:00
parent 13a006ba48
commit db9d9fb4e6
24 changed files with 294 additions and 296 deletions

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
@ -672,6 +673,13 @@ namespace Artemis.Core
if (descriptor == null) if (descriptor == null)
throw new ArgumentNullException(nameof(descriptor)); throw new ArgumentNullException(nameof(descriptor));
if (LayerBrush != null)
{
var brush = LayerBrush;
LayerBrush = null;
brush.Dispose();
}
// Ensure the brush reference matches the brush // Ensure the brush reference matches the brush
var current = General.BrushReference.CurrentValue; var current = General.BrushReference.CurrentValue;
if (current.BrushPluginGuid != descriptor.LayerBrushProvider.PluginInfo.Guid || current.BrushType != descriptor.LayerBrushType.Name) if (current.BrushPluginGuid != descriptor.LayerBrushProvider.PluginInfo.Guid || current.BrushType != descriptor.LayerBrushType.Name)
@ -721,7 +729,7 @@ namespace Artemis.Core
} }
#endregion #endregion
#region Event handlers #region Event handlers
private void LayerBrushStoreOnLayerBrushRemoved(object sender, LayerBrushStoreEvent e) private void LayerBrushStoreOnLayerBrushRemoved(object sender, LayerBrushStoreEvent e)

View File

@ -19,7 +19,7 @@ namespace Artemis.Core
public abstract class LayerProperty<T> : ILayerProperty public abstract class LayerProperty<T> : ILayerProperty
{ {
private bool _disposed; private bool _disposed;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="LayerProperty{T}" /> class /// Creates a new instance of the <see cref="LayerProperty{T}" /> class
/// </summary> /// </summary>
@ -274,19 +274,6 @@ namespace Artemis.Core
OnKeyframeRemoved(); OnKeyframeRemoved();
} }
/// <summary>
/// Removes all keyframes from the layer property
/// </summary>
public void ClearKeyframes()
{
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
var keyframes = new List<LayerPropertyKeyframe<T>>(_keyframes);
foreach (var layerPropertyKeyframe in keyframes)
RemoveKeyframe(layerPropertyKeyframe);
}
/// <summary> /// <summary>
/// Sorts the keyframes in ascending order by position /// Sorts the keyframes in ascending order by position
/// </summary> /// </summary>
@ -343,6 +330,11 @@ namespace Artemis.Core
return (DataBindingRegistration<T, TProperty>) match; return (DataBindingRegistration<T, TProperty>) match;
} }
public List<IDataBindingRegistration> GetAllDataBindingRegistrations()
{
return _dataBindingRegistrations;
}
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda, DataBindingConverter<T, TProperty> converter) public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda, DataBindingConverter<T, TProperty> converter)
{ {
if (_disposed) if (_disposed)
@ -593,7 +585,7 @@ namespace Artemis.Core
{ {
_disposed = true; _disposed = true;
foreach (var dataBinding in _dataBindings) foreach (var dataBinding in _dataBindings)
dataBinding.Dispose(); dataBinding.Dispose();
} }
} }

View File

@ -89,10 +89,7 @@ namespace Artemis.UI.Shared.Input
private void Initialize() private void Initialize()
{ {
// Get the data models // Get the data models
DataModelViewModel = _dataModelUIService.GetMainDataModelVisualization(); DataModelViewModel = _dataModelUIService.GetPluginDataModelVisualization(_module, true);
if (!_dataModelUIService.GetPluginExtendsDataModel(_module))
DataModelViewModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_module));
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested; DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
_updateTimer.Start(); _updateTimer.Start();

View File

@ -32,7 +32,7 @@ namespace Artemis.UI.Shared.Services
public DataModelPropertiesViewModel GetMainDataModelVisualization() public DataModelPropertiesViewModel GetMainDataModelVisualization()
{ {
var viewModel = new DataModelPropertiesViewModel(null, null, null); var viewModel = new DataModelPropertiesViewModel(null, null, null);
foreach (var dataModelExpansion in _dataModelService.DataModelExpansions) foreach (var dataModelExpansion in _dataModelService.GetDataModels())
viewModel.Children.Add(new DataModelPropertiesViewModel(dataModelExpansion, viewModel, null)); viewModel.Children.Add(new DataModelPropertiesViewModel(dataModelExpansion, viewModel, null));
// Update to populate children // Update to populate children
@ -41,8 +41,23 @@ namespace Artemis.UI.Shared.Services
return viewModel; return viewModel;
} }
public DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin) public DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool includeMainDataModel)
{ {
if (includeMainDataModel)
{
var mainDataModel = GetMainDataModelVisualization();
// If the main data model already includes the plugin data model we're done
if (mainDataModel.Children.Any(c => c.DataModel.PluginInfo.Instance == plugin))
return mainDataModel;
// Otherwise get just the plugin data model and add it
var pluginDataModel = GetPluginDataModelVisualization(plugin, false);
if (pluginDataModel != null)
mainDataModel.Children.Add(pluginDataModel);
return mainDataModel;
}
var dataModel = _dataModelService.GetPluginDataModel(plugin); var dataModel = _dataModelService.GetPluginDataModel(plugin);
if (dataModel == null) if (dataModel == null)
return null; return null;
@ -55,12 +70,7 @@ namespace Artemis.UI.Shared.Services
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this); viewModel.UpdateRequested += (sender, args) => viewModel.Update(this);
return viewModel; return viewModel;
} }
public bool GetPluginExtendsDataModel(Plugin plugin)
{
return _dataModelService.GetPluginExtendsDataModel(plugin);
}
public DataModelVisualizationRegistration RegisterDataModelInput<T>(PluginInfo pluginInfo, IReadOnlyCollection<Type> compatibleConversionTypes = null) where T : DataModelInputViewModel public DataModelVisualizationRegistration RegisterDataModelInput<T>(PluginInfo pluginInfo, IReadOnlyCollection<Type> compatibleConversionTypes = null) where T : DataModelInputViewModel
{ {
if (compatibleConversionTypes == null) if (compatibleConversionTypes == null)

View File

@ -12,14 +12,7 @@ namespace Artemis.UI.Shared.Services
IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelEditors { get; } IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelEditors { get; }
IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelDisplays { get; } IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelDisplays { get; }
DataModelPropertiesViewModel GetMainDataModelVisualization(); DataModelPropertiesViewModel GetMainDataModelVisualization();
DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin); DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool includeMainDataModel);
/// <summary>
/// Determines whether the given plugin expands the main data model
/// </summary>
/// <param name="plugin"></param>
/// <returns></returns>
bool GetPluginExtendsDataModel(Plugin plugin);
DataModelVisualizationRegistration RegisterDataModelInput<T>(PluginInfo pluginInfo, IReadOnlyCollection<Type> compatibleConversionTypes) where T : DataModelInputViewModel; DataModelVisualizationRegistration RegisterDataModelInput<T>(PluginInfo pluginInfo, IReadOnlyCollection<Type> compatibleConversionTypes) where T : DataModelInputViewModel;
DataModelVisualizationRegistration RegisterDataModelDisplay<T>(PluginInfo pluginInfo) where T : DataModelDisplayViewModel; DataModelVisualizationRegistration RegisterDataModelDisplay<T>(PluginInfo pluginInfo) where T : DataModelDisplayViewModel;

View File

@ -71,21 +71,21 @@ namespace Artemis.UI.Ninject.Factories
public interface IDataBindingsVmFactory : IVmFactory public interface IDataBindingsVmFactory : IVmFactory
{ {
DataBindingsViewModel DataBindingsViewModel(BaseLayerProperty layerProperty); IDataBindingViewModel DataBindingViewModel(IDataBindingRegistration registration);
DataBindingViewModel DataBindingViewModel(DataBindingRegistration registration); DataBindingModifierViewModel<TLayerProperty, TProperty> DataBindingModifierViewModel<TLayerProperty, TProperty>(DataBindingModifier<TLayerProperty, TProperty> modifier);
DataBindingModifierViewModel DataBindingModifierViewModel(DataBindingModifier modifier);
} }
public interface ILayerPropertyVmFactory : IVmFactory public interface ILayerPropertyVmFactory : IVmFactory
{ {
LayerPropertyViewModel LayerPropertyViewModel(ILayerProperty layerProperty); LayerPropertyViewModel LayerPropertyViewModel(ILayerProperty layerProperty);
LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup); LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup);
TreePropertyViewModel<T> LayerPropertyGroupViewModel<T>(LayerProperty<T> layerProperty);
TreeGroupViewModel TreeGroupViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel); TreeGroupViewModel TreeGroupViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel);
TimelineGroupViewModel TimelineGroupViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel); TimelineGroupViewModel TimelineGroupViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel);
LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription);
TreeViewModel TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups); TreeViewModel TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups);
EffectsViewModel EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel); EffectsViewModel EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel);
TimelineViewModel TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups); TimelineViewModel TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups);
TimelineSegmentViewModel TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups);
} }
} }

View File

@ -11,13 +11,12 @@ namespace Artemis.UI.PropertyInput
public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference> public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference>
{ {
private readonly IPluginService _pluginService; private readonly IPluginService _pluginService;
private readonly IRenderElementService _renderElementService;
private List<LayerBrushDescriptor> _descriptors; private List<LayerBrushDescriptor> _descriptors;
public BrushPropertyInputViewModel(LayerProperty<LayerBrushReference> layerProperty, IProfileEditorService profileEditorService, public BrushPropertyInputViewModel(LayerProperty<LayerBrushReference> layerProperty,
IRenderElementService renderElementService, IPluginService pluginService) : base(layerProperty, profileEditorService) IProfileEditorService profileEditorService,
IPluginService pluginService) : base(layerProperty, profileEditorService)
{ {
_renderElementService = renderElementService;
_pluginService = pluginService; _pluginService = pluginService;
_pluginService.PluginEnabled += PluginServiceOnPluginLoaded; _pluginService.PluginEnabled += PluginServiceOnPluginLoaded;
@ -54,11 +53,8 @@ namespace Artemis.UI.PropertyInput
protected override void OnInputValueApplied() protected override void OnInputValueApplied()
{ {
if (LayerProperty.ProfileElement is Layer layer) if (LayerProperty.ProfileElement is Layer layer)
{ layer.ChangeLayerBrush(SelectedDescriptor);
_renderElementService.RemoveLayerBrush(layer);
_renderElementService.InstantiateLayerBrush(layer);
}
} }
private void SetBrushByDescriptor(LayerBrushDescriptor value) private void SetBrushByDescriptor(LayerBrushDescriptor value)

View File

@ -308,10 +308,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
if (DisplayConditionListPredicate.ListDataModel == null || DisplayConditionListPredicate.ListPropertyPath == null) if (DisplayConditionListPredicate.ListDataModel == null || DisplayConditionListPredicate.ListPropertyPath == null)
throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list"); throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list");
var dataModel = _dataModelUIService.GetMainDataModelVisualization(); var dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
if (!_dataModelUIService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
dataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath( var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath(
DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid, DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid,
DisplayConditionListPredicate.ListPropertyPath DisplayConditionListPredicate.ListPropertyPath

View File

@ -109,10 +109,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public void Initialize() public void Initialize()
{ {
// Get the data models // Get the data models
TargetDataModel = _dataModelUIService.GetMainDataModelVisualization(); TargetDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
if (!_dataModelUIService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
TargetDataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
TargetDataModel.UpdateRequested += TargetDataModelUpdateRequested; TargetDataModel.UpdateRequested += TargetDataModelUpdateRequested;
Update(); Update();

View File

@ -168,14 +168,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public void Initialize() public void Initialize()
{ {
// Get the data models // Get the data models
LeftSideDataModel = _dataModelUIService.GetMainDataModelVisualization(); LeftSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
RightSideDataModel = _dataModelUIService.GetMainDataModelVisualization(); RightSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
if (!_dataModelUIService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule()))
{
LeftSideDataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
RightSideDataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule()));
}
// Determine which types are currently supported // Determine which types are currently supported
var editors = _dataModelUIService.RegisteredDataModelEditors; var editors = _dataModelUIService.RegisteredDataModelEditors;
_supportedInputTypes = editors.Select(e => e.SupportedType).ToList(); _supportedInputTypes = editors.Select(e => e.SupportedType).ToList();

View File

@ -1,6 +1,4 @@
using System; using Artemis.Core;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Exceptions; using Artemis.UI.Exceptions;
using Artemis.UI.Shared; using Artemis.UI.Shared;
@ -10,16 +8,16 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{ {
public class DataBindingModifierViewModel : PropertyChangedBase public class DataBindingModifierViewModel<TLayerProperty, TProperty> : PropertyChangedBase
{ {
private readonly IDataBindingService _dataBindingService; private readonly IDataBindingService _dataBindingService;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private DataBindingModifierType _selectedModifierType;
private DataModelDynamicViewModel _dynamicSelectionViewModel; private DataModelDynamicViewModel _dynamicSelectionViewModel;
private DataBindingModifierType _selectedModifierType;
private DataModelStaticViewModel _staticInputViewModel; private DataModelStaticViewModel _staticInputViewModel;
public DataBindingModifierViewModel(DataBindingModifier modifier, public DataBindingModifierViewModel(DataBindingModifier<TLayerProperty, TProperty> modifier,
IDataBindingService dataBindingService, IDataBindingService dataBindingService,
ISettingsService settingsService, ISettingsService settingsService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
@ -42,7 +40,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
public DelegateCommand SelectModifierTypeCommand { get; } public DelegateCommand SelectModifierTypeCommand { get; }
public PluginSetting<bool> ShowDataModelValues { get; } public PluginSetting<bool> ShowDataModelValues { get; }
public DataBindingModifier Modifier { get; } public DataBindingModifier<TLayerProperty, TProperty> Modifier { get; }
public BindableCollection<DataBindingModifierType> ModifierTypes { get; } public BindableCollection<DataBindingModifierType> ModifierTypes { get; }
public DataBindingModifierType SelectedModifierType public DataBindingModifierType SelectedModifierType
@ -62,7 +60,22 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
get => _staticInputViewModel; get => _staticInputViewModel;
private set => SetAndNotify(ref _staticInputViewModel, value); private set => SetAndNotify(ref _staticInputViewModel, value);
} }
public void Delete()
{
Modifier.DataBinding.RemoveModifier(Modifier);
}
public void SwapType()
{
if (Modifier.ParameterType == ProfileRightSideType.Dynamic)
Modifier.UpdateParameter(Modifier.DataBinding.GetSourceType().GetDefault());
else
Modifier.UpdateParameter(null, null);
Update();
}
private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{ {
Modifier.UpdateParameter(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.PropertyPath); Modifier.UpdateParameter(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.PropertyPath);
@ -89,7 +102,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
StaticInputViewModel = null; StaticInputViewModel = null;
DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected; DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected;
DynamicSelectionViewModel.FilterTypes = new[] { sourceType }; DynamicSelectionViewModel.FilterTypes = new[] {sourceType};
} }
else else
{ {
@ -120,20 +133,5 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
Update(); Update();
} }
public void Delete()
{
Modifier.DataBinding.RemoveModifier(Modifier);
}
public void SwapType()
{
if (Modifier.ParameterType == ProfileRightSideType.Dynamic)
Modifier.UpdateParameter(Modifier.DataBinding.GetSourceType().GetDefault());
else
Modifier.UpdateParameter(null, null);
Update();
}
} }
} }

View File

@ -10,23 +10,23 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{ {
public class DataBindingViewModel : PropertyChangedBase, IDisposable public class DataBindingViewModel<TLayerProperty, TProperty> : Screen, IDataBindingViewModel
{ {
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IDataModelUIService _dataModelUIService; private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private DataBinding _dataBinding; private DataBinding<TLayerProperty, TProperty> _dataBinding;
private int _easingTime; private int _easingTime;
private bool _isDataBindingEnabled; private bool _isDataBindingEnabled;
private bool _isEasingTimeEnabled; private bool _isEasingTimeEnabled;
private DataBindingMode _selectedDataBindingMode; private DataBindingMode _selectedDataBindingMode;
private TimelineEasingViewModel _selectedEasingViewModel; private TimelineEasingViewModel _selectedEasingViewModel;
private DataModelDynamicViewModel _targetSelectionViewModel; private DataModelDynamicViewModel _targetSelectionViewModel;
private object _testInputValue; private TProperty _testInputValue;
private object _testResultValue; private TProperty _testResultValue;
private bool _updating; private bool _updating;
public DataBindingViewModel(DataBindingRegistration registration, public DataBindingViewModel(DataBindingRegistration<TLayerProperty, TProperty> registration,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
IDataBindingsVmFactory dataBindingsVmFactory) IDataBindingsVmFactory dataBindingsVmFactory)
@ -40,7 +40,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingMode))); DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingMode)));
EasingViewModels = new BindableCollection<TimelineEasingViewModel>(); EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
ModifierViewModels = new BindableCollection<DataBindingModifierViewModel>(); ModifierViewModels = new BindableCollection<DataBindingModifierViewModel<TLayerProperty, TProperty>>();
DataBinding = Registration.DataBinding; DataBinding = Registration.DataBinding;
if (DataBinding != null) if (DataBinding != null)
@ -52,12 +52,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
Execute.PostToUIThread(Initialize); Execute.PostToUIThread(Initialize);
} }
public DataBindingRegistration Registration { get; } public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; }
public string DisplayName { get; }
public BindableCollection<ValueDescription> DataBindingModes { get; } public BindableCollection<ValueDescription> DataBindingModes { get; }
public BindableCollection<TimelineEasingViewModel> EasingViewModels { get; } public BindableCollection<TimelineEasingViewModel> EasingViewModels { get; }
public BindableCollection<DataBindingModifierViewModel> ModifierViewModels { get; } public BindableCollection<DataBindingModifierViewModel<TLayerProperty, TProperty>> ModifierViewModels { get; }
public DataBindingMode SelectedDataBindingMode public DataBindingMode SelectedDataBindingMode
{ {
@ -114,7 +113,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
} }
} }
public DataBinding DataBinding public DataBinding<TLayerProperty, TProperty> DataBinding
{ {
get => _dataBinding; get => _dataBinding;
set set
@ -124,13 +123,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
} }
} }
public object TestInputValue public TProperty TestInputValue
{ {
get => _testInputValue; get => _testInputValue;
set => SetAndNotify(ref _testInputValue, value); set => SetAndNotify(ref _testInputValue, value);
} }
public object TestResultValue public TProperty TestResultValue
{ {
get => _testResultValue; get => _testResultValue;
set => SetAndNotify(ref _testResultValue, value); set => SetAndNotify(ref _testResultValue, value);
@ -167,7 +166,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
if (DataBinding == null) if (DataBinding == null)
return; return;
var modifier = new DataBindingModifier(ProfileRightSideType.Dynamic); var modifier = new DataBindingModifier<TLayerProperty, TProperty>(ProfileRightSideType.Dynamic);
DataBinding.AddModifier(modifier); DataBinding.AddModifier(modifier);
_profileEditorService.UpdateSelectedProfileElement(); _profileEditorService.UpdateSelectedProfileElement();
@ -175,7 +174,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private void Initialize() private void Initialize()
{ {
EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(v => new TimelineEasingViewModel(null, v))); EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(v => new TimelineEasingViewModel(v, false)));
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected; TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;
_profileEditorService.ProfilePreviewUpdated += ProfileEditorServiceOnProfilePreviewUpdated; _profileEditorService.ProfilePreviewUpdated += ProfileEditorServiceOnProfilePreviewUpdated;
@ -229,9 +228,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
var currentValue = TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue(); var currentValue = TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue();
if (currentValue == null && Registration.Property.PropertyType.IsValueType) if (currentValue == null && Registration.Property.PropertyType.IsValueType)
currentValue = Activator.CreateInstance(Registration.Property.PropertyType); currentValue = Activator.CreateInstance(Registration.Property.PropertyType);
TestInputValue = Convert.ChangeType(currentValue, Registration.Property.PropertyType); TestInputValue = currentValue is TProperty testInputValue ? testInputValue : default;
TestResultValue = DataBinding?.GetValue(TestInputValue); if (DataBinding != null)
TestResultValue = DataBinding.GetValue(TestInputValue);
else
TestInputValue = default;
} }
private void UpdateModifierViewModels() private void UpdateModifierViewModels()
@ -268,4 +270,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
DataBinding.ModifiersUpdated -= DataBindingOnModifiersUpdated; DataBinding.ModifiersUpdated -= DataBindingOnModifiersUpdated;
} }
} }
public interface IDataBindingViewModel : IScreen, IDisposable
{
}
} }

View File

@ -1,16 +0,0 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DataBindingsTabsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<TabControl ItemsSource="{Binding Tabs}" DisplayMemberPath="DisplayName" Style="{StaticResource MaterialDesignTabControl}">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</UserControl>

View File

@ -1,14 +0,0 @@
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public class DataBindingsTabsViewModel : PropertyChangedBase
{
public DataBindingsTabsViewModel()
{
Tabs = new BindableCollection<DataBindingViewModel>();
}
public BindableCollection<DataBindingViewModel> Tabs { get; set; }
}
}

View File

@ -3,14 +3,14 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800">
d:DataContext="{d:DesignInstance local:DataBindingsViewModel}"> <TabControl ItemsSource="{Binding Items}" DisplayMemberPath="DisplayName" Style="{StaticResource MaterialDesignTabControl}">
<Grid> <TabControl.ContentTemplate>
<!-- Use tabs for multiple properties only, only one of these VMs is set --> <DataTemplate>
<ContentControl s:View.Model="{Binding DataBindingsTabsViewModel}" /> <ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
<ContentControl s:View.Model="{Binding DataBindingViewModel}" /> </DataTemplate>
</Grid> </TabControl.ContentTemplate>
</TabControl>
</UserControl> </UserControl>

View File

@ -6,58 +6,27 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{ {
public class DataBindingsViewModel : PropertyChangedBase, IDisposable public class DataBindingsViewModel<T> : Conductor<IDataBindingViewModel>.Collection.AllActive
{ {
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private DataBindingsTabsViewModel _dataBindingsTabsViewModel;
private DataBindingViewModel _dataBindingViewModel; public DataBindingsViewModel(LayerProperty<T> layerProperty, IDataBindingsVmFactory dataBindingsVmFactory)
public DataBindingsViewModel(BaseLayerProperty layerProperty, IDataBindingsVmFactory dataBindingsVmFactory)
{ {
_dataBindingsVmFactory = dataBindingsVmFactory; _dataBindingsVmFactory = dataBindingsVmFactory;
LayerProperty = layerProperty; LayerProperty = layerProperty;
Initialise(); Initialise();
} }
public BaseLayerProperty LayerProperty { get; } public LayerProperty<T> LayerProperty { get; }
public DataBindingViewModel DataBindingViewModel
{
get => _dataBindingViewModel;
set => SetAndNotify(ref _dataBindingViewModel, value);
}
public DataBindingsTabsViewModel DataBindingsTabsViewModel
{
get => _dataBindingsTabsViewModel;
set => SetAndNotify(ref _dataBindingsTabsViewModel, value);
}
private void Initialise() private void Initialise()
{ {
DataBindingViewModel?.Dispose(); var registrations = LayerProperty.GetAllDataBindingRegistrations();
DataBindingViewModel = null;
DataBindingsTabsViewModel = null;
var registrations = LayerProperty.DataBindingRegistrations;
if (registrations == null || registrations.Count == 0)
return;
// Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving // Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving
// and creating the actual data bindings // and creating the actual data bindings
if (registrations.Count == 1) foreach (var registration in registrations)
DataBindingViewModel = _dataBindingsVmFactory.DataBindingViewModel(registrations.First()); ActivateItem(_dataBindingsVmFactory.DataBindingViewModel(registration));
else
{
DataBindingsTabsViewModel = new DataBindingsTabsViewModel();
foreach (var registration in registrations)
DataBindingsTabsViewModel.Tabs.Add(_dataBindingsVmFactory.DataBindingViewModel(registration));
}
}
public void Dispose()
{
DataBindingViewModel?.Dispose();
} }
} }
} }

View File

@ -289,28 +289,26 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
SelectedLayer.LayerBrushUpdated += SelectedLayerOnLayerBrushUpdated; SelectedLayer.LayerBrushUpdated += SelectedLayerOnLayerBrushUpdated;
// Add the built-in root groups of the layer // Add the built-in root groups of the layer
var generalAttribute = Attribute.GetCustomAttribute( LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.General));
SelectedLayer.GetType().GetProperty(nameof(SelectedLayer.General)), LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.Transform));
typeof(PropertyGroupDescriptionAttribute)
);
var transformAttribute = Attribute.GetCustomAttribute(
SelectedLayer.GetType().GetProperty(nameof(SelectedLayer.Transform)),
typeof(PropertyGroupDescriptionAttribute)
);
LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.General, (PropertyGroupDescriptionAttribute) generalAttribute));
LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.Transform, (PropertyGroupDescriptionAttribute) transformAttribute));
} }
TreeViewModel = _layerPropertyVmFactory.TreeViewModel(this, LayerPropertyGroups); TreeViewModel = _layerPropertyVmFactory.TreeViewModel(this, LayerPropertyGroups);
TimelineViewModel?.Dispose(); DeactivateItem(TimelineViewModel);
DeactivateItem(StartTimelineSegmentViewModel);
DeactivateItem(MainTimelineSegmentViewModel);
DeactivateItem(EndTimelineSegmentViewModel);
TimelineViewModel = _layerPropertyVmFactory.TimelineViewModel(this, LayerPropertyGroups); TimelineViewModel = _layerPropertyVmFactory.TimelineViewModel(this, LayerPropertyGroups);
ActivateItem(TimelineViewModel);
StartTimelineSegmentViewModel?.Dispose(); StartTimelineSegmentViewModel?.Dispose();
StartTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.Start); StartTimelineSegmentViewModel = _layerPropertyVmFactory.TimelineSegmentViewModel(SegmentViewModelType.Start, LayerPropertyGroups);
MainTimelineSegmentViewModel?.Dispose(); MainTimelineSegmentViewModel?.Dispose();
MainTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.Main); MainTimelineSegmentViewModel = _layerPropertyVmFactory.TimelineSegmentViewModel(SegmentViewModelType.Main, LayerPropertyGroups);
EndTimelineSegmentViewModel?.Dispose(); EndTimelineSegmentViewModel?.Dispose();
EndTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.End); EndTimelineSegmentViewModel = _layerPropertyVmFactory.TimelineSegmentViewModel(SegmentViewModelType.End, LayerPropertyGroups);
ApplyLayerBrush(); ApplyLayerBrush();
ApplyEffects(); ApplyEffects();
@ -346,19 +344,19 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
if (SelectedLayer.LayerBrush != null) if (SelectedLayer.LayerBrush != null)
{ {
// Add the rout group of the brush // TODO: wat?
// Add the root group of the brush
// The root group of the brush has no attribute so let's pull one out of our sleeve // The root group of the brush has no attribute so let's pull one out of our sleeve
var brushDescription = new PropertyGroupDescriptionAttribute var brushDescription = new PropertyGroupDescriptionAttribute
{ {
Name = SelectedLayer.LayerBrush.Descriptor.DisplayName, Name = SelectedLayer.LayerBrush.Descriptor.DisplayName,
Description = SelectedLayer.LayerBrush.Descriptor.Description Description = SelectedLayer.LayerBrush.Descriptor.Description
}; };
_brushPropertyGroup = _layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.LayerBrush.BaseProperties, brushDescription); _brushPropertyGroup = _layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.LayerBrush.BaseProperties);
LayerPropertyGroups.Add(_brushPropertyGroup); LayerPropertyGroups.Add(_brushPropertyGroup);
} }
SortProperties(); SortProperties();
TimelineViewModel.Update();
} }
private void ApplyEffects() private void ApplyEffects()
@ -382,18 +380,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
if (LayerPropertyGroups.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect)) if (LayerPropertyGroups.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect))
continue; continue;
// Add the rout group of the brush // TODO: wat?
// Add the root group of the brush
// The root group of the brush has no attribute so let's pull one out of our sleeve // The root group of the brush has no attribute so let's pull one out of our sleeve
var brushDescription = new PropertyGroupDescriptionAttribute var brushDescription = new PropertyGroupDescriptionAttribute
{ {
Name = layerEffect.Descriptor.DisplayName, Name = layerEffect.Descriptor.DisplayName,
Description = layerEffect.Descriptor.Description Description = layerEffect.Descriptor.Description
}; };
LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerEffect.BaseProperties, brushDescription)); LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerEffect.BaseProperties));
} }
SortProperties(); SortProperties();
TimelineViewModel.Update();
} }
private void SortProperties() private void SortProperties()
@ -422,7 +420,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
LayerPropertyGroups.Move(LayerPropertyGroups.IndexOf(layerPropertyGroupViewModel), index + nonEffectProperties.Count); LayerPropertyGroups.Move(LayerPropertyGroups.IndexOf(layerPropertyGroupViewModel), index + nonEffectProperties.Count);
} }
} }
#endregion #endregion
#region Drag and drop #region Drag and drop
@ -437,8 +435,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
var source = dropInfo.Data as LayerPropertyGroupViewModel; var source = dropInfo.Data as LayerPropertyGroupViewModel;
var target = dropInfo.TargetItem as LayerPropertyGroupViewModel; var target = dropInfo.TargetItem as LayerPropertyGroupViewModel;
if (source == target || if (source == target ||
target?.TreeGroupViewModel.GroupType != LayerEffectRoot || target?.TreeGroupViewModel.GroupType != LayerEffectRoot ||
source?.TreeGroupViewModel.GroupType != LayerEffectRoot) source?.TreeGroupViewModel.GroupType != LayerEffectRoot)
return; return;
@ -557,13 +555,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
private TimeSpan CalculateEndTime() private TimeSpan CalculateEndTime()
{ {
var keyframeTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframePositions(false)).ToList(); var keyframeViewModels = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframeViewModels(false)).ToList();
// If there are no keyframes, don't stop at all // If there are no keyframes, don't stop at all
if (!keyframeTimes.Any()) if (!keyframeViewModels.Any())
return TimeSpan.MaxValue; return TimeSpan.MaxValue;
// If there are keyframes, stop after the last keyframe + 10 sec // If there are keyframes, stop after the last keyframe + 10 sec
return keyframeTimes.Max().Add(TimeSpan.FromSeconds(10)); return keyframeViewModels.Max(k => k.Position).Add(TimeSpan.FromSeconds(10));
} }
private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e) private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)
@ -620,7 +618,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
// If holding down shift, snap to the closest segment or keyframe // If holding down shift, snap to the closest segment or keyframe
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{ {
var snapTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframePositions(true)).ToList(); var snapTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframeViewModels(true)).Select(k => k.Position).ToList();
var snappedTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), true, false, snapTimes); var snappedTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), true, false, snapTimes);
ProfileEditorService.CurrentTime = snappedTime; ProfileEditorService.CurrentTime = snappedTime;
return; return;

View File

@ -82,21 +82,54 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
NotifyOfPropertyChange(nameof(IsExpanded)); NotifyOfPropertyChange(nameof(IsExpanded));
} }
public List<TimeSpan> GetAllKeyframePositions(bool expandedOnly) public List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels(bool expandedOnly)
{ {
var result = new List<TimeSpan>(); var result = new List<ITimelineKeyframeViewModel>();
if (expandedOnly == IsExpanded) if (expandedOnly == IsExpanded)
return result; return result;
foreach (var child in Children) foreach (var child in Children)
{ {
if (child is LayerPropertyViewModel layerPropertyViewModel) if (child is LayerPropertyViewModel layerPropertyViewModel)
result.AddRange(layerPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframePositions()); result.AddRange(layerPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframeViewModels());
else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel) else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
result.AddRange(layerPropertyGroupViewModel.GetAllKeyframePositions(expandedOnly)); result.AddRange(layerPropertyGroupViewModel.GetAllKeyframeViewModels(expandedOnly));
} }
return result; 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);
}
}
} }
} }

View File

@ -41,8 +41,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
KeyframePositions.Clear(); KeyframePositions.Clear();
KeyframePositions.AddRange(LayerPropertyGroupViewModel KeyframePositions.AddRange(LayerPropertyGroupViewModel
.GetAllKeyframePositions(false) .GetAllKeyframeViewModels(false)
.Select(p => p.TotalSeconds * _profileEditorService.PixelsPerSecond)); .Select(p => p.Position.TotalSeconds * _profileEditorService.PixelsPerSecond));
} }
} }
} }

View File

@ -7,7 +7,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelineKeyframeViewModel<T> : Screen, IDisposable public class TimelineKeyframeViewModel<T> : Screen, ITimelineKeyframeViewModel
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
@ -31,12 +31,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
set => SetAndNotify(ref _easingViewModels, value); set => SetAndNotify(ref _easingViewModels, value);
} }
public bool IsSelected
{
get => _isSelected;
set => SetAndNotify(ref _isSelected, value);
}
public double X public double X
{ {
get => _x; get => _x;
@ -49,6 +43,14 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
set => SetAndNotify(ref _timestamp, value); set => SetAndNotify(ref _timestamp, value);
} }
public bool IsSelected
{
get => _isSelected;
set => SetAndNotify(ref _isSelected, value);
}
public TimeSpan Position => LayerPropertyKeyframe.Position;
public void Dispose() public void Dispose()
{ {
LayerPropertyKeyframe.PropertyChanged -= LayerPropertyKeyframeOnPropertyChanged; LayerPropertyKeyframe.PropertyChanged -= LayerPropertyKeyframeOnPropertyChanged;
@ -110,9 +112,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
_offset = null; _offset = null;
} }
public void SaveOffsetToKeyframe(TimelineKeyframeViewModel<T> keyframeViewModel) public void SaveOffsetToKeyframe(ITimelineKeyframeViewModel source)
{ {
if (keyframeViewModel == this) if (source == this)
{ {
_offset = null; _offset = null;
return; return;
@ -121,15 +123,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
if (_offset != null) if (_offset != null)
return; return;
_offset = LayerPropertyKeyframe.Position - keyframeViewModel.LayerPropertyKeyframe.Position; _offset = LayerPropertyKeyframe.Position - source.Position;
} }
public void ApplyOffsetToKeyframe(TimelineKeyframeViewModel<T> keyframeViewModel) public void ApplyOffsetToKeyframe(ITimelineKeyframeViewModel source)
{ {
if (keyframeViewModel == this || _offset == null) if (source == this || _offset == null)
return; return;
UpdatePosition(keyframeViewModel.LayerPropertyKeyframe.Position + _offset.Value); UpdatePosition(source.Position + _offset.Value);
} }
public void UpdatePosition(TimeSpan position) public void UpdatePosition(TimeSpan position)
@ -148,6 +150,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
#region Context menu actions #region Context menu actions
public void ContextMenuOpening()
{
CreateEasingViewModels();
}
public void ContextMenuClosing()
{
foreach (var timelineEasingViewModel in EasingViewModels)
timelineEasingViewModel.EasingModeSelected -= TimelineEasingViewModelOnEasingModeSelected;
EasingViewModels.Clear();
}
public void Copy() public void Copy()
{ {
var newKeyframe = new LayerPropertyKeyframe<T>( var newKeyframe = new LayerPropertyKeyframe<T>(
@ -180,4 +194,26 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
#endregion #endregion
} }
public interface ITimelineKeyframeViewModel : IScreen, IDisposable
{
bool IsSelected { get; set; }
TimeSpan Position { get; }
#region Movement
void SaveOffsetToKeyframe(ITimelineKeyframeViewModel source);
void ApplyOffsetToKeyframe(ITimelineKeyframeViewModel source);
void UpdatePosition(TimeSpan position);
void ReleaseMovement();
#endregion
#region Context menu actions
void Copy();
void Delete();
#endregion
}
} }

View File

@ -21,9 +21,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
LayerPropertyViewModel = layerPropertyViewModel; LayerPropertyViewModel = layerPropertyViewModel;
} }
public List<TimeSpan> GetAllKeyframePositions() public List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels()
{ {
return LayerProperty.Keyframes.Select(k => k.Position).ToList(); return Items.Cast<ITimelineKeyframeViewModel>().ToList();
} }
private void UpdateKeyframes() private void UpdateKeyframes()
@ -49,6 +49,30 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
timelineKeyframeViewModel.Update(); timelineKeyframeViewModel.Update();
} }
public void WipeKeyframes(TimeSpan? start, TimeSpan? end)
{
start ??= TimeSpan.Zero;
end ??= TimeSpan.MaxValue;
var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position <= end).ToList();
foreach (var keyframe in toShift)
LayerProperty.RemoveKeyframe(keyframe);
UpdateKeyframes();
}
public void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount)
{
start ??= TimeSpan.Zero;
end ??= TimeSpan.MaxValue;
var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position <= end).ToList();
foreach (var keyframe in toShift)
keyframe.Position += amount;
UpdateKeyframes();
}
public void Dispose() public void Dispose()
{ {
} }
@ -56,6 +80,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
public interface ITimelinePropertyViewModel : IScreen, IDisposable public interface ITimelinePropertyViewModel : IScreen, IDisposable
{ {
List<TimeSpan> GetAllKeyframePositions(); List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels();
void WipeKeyframes(TimeSpan? start, TimeSpan? end);
void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount);
} }
} }

View File

@ -18,10 +18,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
private bool _showRepeatButton; private bool _showRepeatButton;
private bool _showSegmentName; private bool _showSegmentName;
public TimelineSegmentViewModel(IProfileEditorService profileEditorService, SegmentViewModelType segment) public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups,
IProfileEditorService profileEditorService)
{ {
ProfileEditorService = profileEditorService; ProfileEditorService = profileEditorService;
Segment = segment; Segment = segment;
LayerPropertyGroups = layerPropertyGroups;
SelectedProfileElement = ProfileEditorService.SelectedProfileElement; SelectedProfileElement = ProfileEditorService.SelectedProfileElement;
switch (Segment) switch (Segment)
@ -47,6 +49,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
public RenderProfileElement SelectedProfileElement { get; } public RenderProfileElement SelectedProfileElement { get; }
public SegmentViewModelType Segment { get; } public SegmentViewModelType Segment { get; }
public BindableCollection<LayerPropertyGroupViewModel> LayerPropertyGroups { get; }
public IProfileEditorService ProfileEditorService { get; } public IProfileEditorService ProfileEditorService { get; }
public string ToolTip { get; } public string ToolTip { get; }
@ -130,7 +133,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
public void DisableSegment() public void DisableSegment()
{ {
var keyframes = SelectedProfileElement.GetAllKeyframes();
var startSegmentEnd = SelectedProfileElement.StartSegmentLength; var startSegmentEnd = SelectedProfileElement.StartSegmentLength;
var mainSegmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength; var mainSegmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
@ -139,22 +141,19 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
if (Segment == SegmentViewModelType.Start) if (Segment == SegmentViewModelType.Start)
{ {
// Remove keyframes that fall in this segment // Remove keyframes that fall in this segment
foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position <= startSegmentEnd)) WipeKeyframes(null, startSegmentEnd);
baseLayerPropertyKeyframe.Remove();
SelectedProfileElement.StartSegmentLength = TimeSpan.Zero; SelectedProfileElement.StartSegmentLength = TimeSpan.Zero;
} }
else if (Segment == SegmentViewModelType.Main) else if (Segment == SegmentViewModelType.Main)
{ {
// Remove keyframes that fall in this segment // Remove keyframes that fall in this segment
foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position > startSegmentEnd && k.Position <= mainSegmentEnd)) WipeKeyframes(startSegmentEnd, startSegmentEnd);
baseLayerPropertyKeyframe.Remove();
SelectedProfileElement.MainSegmentLength = TimeSpan.Zero; SelectedProfileElement.MainSegmentLength = TimeSpan.Zero;
} }
else if (Segment == SegmentViewModelType.End) else if (Segment == SegmentViewModelType.End)
{ {
// Remove keyframes that fall in this segment // Remove keyframes that fall in this segment
foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position > mainSegmentEnd)) WipeKeyframes(mainSegmentEnd, null);
baseLayerPropertyKeyframe.Remove();
SelectedProfileElement.EndSegmentLength = TimeSpan.Zero; SelectedProfileElement.EndSegmentLength = TimeSpan.Zero;
} }
@ -188,8 +187,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
else if (Segment == SegmentViewModelType.End) else if (Segment == SegmentViewModelType.End)
segmentEnd = SelectedProfileElement.TimelineLength; segmentEnd = SelectedProfileElement.TimelineLength;
foreach (var baseLayerPropertyKeyframe in SelectedProfileElement.GetAllKeyframes().Where(k => k.Position > segmentEnd)) ShiftKeyframes(segmentEnd, null, amount);
baseLayerPropertyKeyframe.Position += amount;
} }
public void SegmentMouseDown(object sender, MouseButtonEventArgs e) public void SegmentMouseDown(object sender, MouseButtonEventArgs e)
@ -227,7 +225,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
// If holding down shift, snap to the closest element on the timeline // If holding down shift, snap to the closest element on the timeline
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
newTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), false, true, true); {
var keyframeTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframeViewModels(true)).Select(k => k.Position).ToList();
newTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), false, true, keyframeTimes);
}
// If holding down control, round to the closest 50ms // If holding down control, round to the closest 50ms
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0); newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0);
@ -272,7 +273,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
NotifyOfPropertyChange(nameof(RepeatSegment)); NotifyOfPropertyChange(nameof(RepeatSegment));
} }
private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e) private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e)
{ {
NotifyOfPropertyChange(nameof(SegmentWidth)); NotifyOfPropertyChange(nameof(SegmentWidth));
@ -291,6 +291,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
ShowRepeatButton = SegmentWidth > 45 && IsMainSegment; ShowRepeatButton = SegmentWidth > 45 && IsMainSegment;
ShowDisableButton = SegmentWidth > 25; ShowDisableButton = SegmentWidth > 25;
} }
private void WipeKeyframes(TimeSpan? start, TimeSpan? end)
{
foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
layerPropertyGroupViewModel.WipeKeyframes(start, end);
}
private void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount)
{
foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
layerPropertyGroupViewModel.ShiftKeyframes(start, end, amount);
}
} }
public enum SegmentViewModelType public enum SegmentViewModelType

View File

@ -14,7 +14,7 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelineViewModel : PropertyChangedBase, IViewAware, IDisposable public class TimelineViewModel : Screen, IDisposable
{ {
private readonly LayerPropertiesViewModel _layerPropertiesViewModel; private readonly LayerPropertiesViewModel _layerPropertiesViewModel;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
@ -105,7 +105,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
// if (e.LeftButton == MouseButtonState.Released) // if (e.LeftButton == MouseButtonState.Released)
// return; // return;
var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel; var viewModel = (sender as Ellipse)?.DataContext as ITimelineKeyframeViewModel;
if (viewModel == null) if (viewModel == null)
return; return;
@ -130,7 +130,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
public void KeyframeMouseMove(object sender, MouseEventArgs e) public void KeyframeMouseMove(object sender, MouseEventArgs e)
{ {
var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel; var viewModel = (sender as Ellipse)?.DataContext as ITimelineKeyframeViewModel;
if (viewModel == null) if (viewModel == null)
return; return;
@ -142,24 +142,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
#region Context menu actions #region Context menu actions
public void ContextMenuOpening(object sender, ContextMenuEventArgs e) public void Copy(ITimelineKeyframeViewModel viewModel)
{
var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel;
viewModel?.CreateEasingViewModels();
}
public void ContextMenuClosing(object sender, ContextMenuEventArgs e)
{
var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel;
viewModel?.EasingViewModels.Clear();
}
public void Copy(TimelineKeyframeViewModel viewModel)
{ {
viewModel.Copy(); viewModel.Copy();
} }
public void Delete(TimelineKeyframeViewModel viewModel) public void Delete(ITimelineKeyframeViewModel viewModel)
{ {
viewModel.Delete(); viewModel.Delete();
} }
@ -198,7 +186,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
#region Keyframe movement #region Keyframe movement
public void MoveSelectedKeyframes(TimeSpan cursorTime, TimelineKeyframeViewModel sourceKeyframeViewModel) public void MoveSelectedKeyframes(TimeSpan cursorTime, ITimelineKeyframeViewModel sourceKeyframeViewModel)
{ {
// Ensure the selection rectangle doesn't show, the view isn't aware of different types of dragging // Ensure the selection rectangle doesn't show, the view isn't aware of different types of dragging
SelectionRectangle.Rect = new Rect(); SelectionRectangle.Rect = new Rect();
@ -214,8 +202,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 5), TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 5),
true, true,
false, false,
true, keyframeViewModels.Where(k => k != sourceKeyframeViewModel).Select(k => k.Position).ToList()
sourceKeyframeViewModel.BaseLayerPropertyKeyframe
); );
} }
@ -267,7 +254,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
SelectionRectangle.Rect = selectedRect; SelectionRectangle.Rect = selectedRect;
var keyframeViewModels = GetAllKeyframeViewModels(); var keyframeViewModels = GetAllKeyframeViewModels();
var selectedKeyframes = HitTestUtilities.GetHitViewModels<TimelineKeyframeViewModel>((Visual) sender, SelectionRectangle); var selectedKeyframes = HitTestUtilities.GetHitViewModels<ITimelineKeyframeViewModel>((Visual) sender, SelectionRectangle);
foreach (var keyframeViewModel in keyframeViewModels) foreach (var keyframeViewModel in keyframeViewModels)
keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel); keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel);
@ -287,7 +274,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
} }
} }
public void SelectKeyframe(TimelineKeyframeViewModel clicked, bool selectBetween, bool toggle) public void SelectKeyframe(ITimelineKeyframeViewModel clicked, bool selectBetween, bool toggle)
{ {
var keyframeViewModels = GetAllKeyframeViewModels(); var keyframeViewModels = GetAllKeyframeViewModels();
if (selectBetween) if (selectBetween)
@ -329,33 +316,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
} }
} }
private List<TimelineKeyframeViewModel> GetAllKeyframeViewModels() private List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels()
{ {
var viewModels = new List<LayerPropertyBaseViewModel>(); var viewModels = new List<ITimelineKeyframeViewModel>();
foreach (var layerPropertyGroupViewModel in LayerPropertyGroups) foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
viewModels.AddRange(layerPropertyGroupViewModel.GetAllChildren()); viewModels.AddRange(layerPropertyGroupViewModel.GetAllKeyframeViewModels(false));
var keyframes = viewModels.Where(vm => vm is LayerPropertyViewModel) return viewModels;
.SelectMany(vm => ((LayerPropertyViewModel) vm).TimelinePropertyBaseViewModel.TimelineKeyframeViewModels)
.ToList();
return keyframes;
} }
#endregion #endregion
#region IViewAware
public void AttachView(UIElement view)
{
if (View != null)
throw new InvalidOperationException(string.Format("Tried to attach View {0} to ViewModel {1}, but it already has a view attached", view.GetType().Name, GetType().Name));
View = view;
}
public UIElement View { get; set; }
#endregion
} }
} }

View File

@ -3,7 +3,6 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Properties; using Artemis.UI.Properties;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
@ -11,13 +10,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{ {
public class SelectionToolViewModel : VisualizationToolViewModel public class SelectionToolViewModel : VisualizationToolViewModel
{ {
private readonly IRenderElementService _renderElementService;
private Rect _dragRectangle; private Rect _dragRectangle;
public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, IRenderElementService renderElementService) public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService)
: base(profileViewModel, profileEditorService)
{ {
_renderElementService = renderElementService;
using (var stream = new MemoryStream(Resources.aero_crosshair)) using (var stream = new MemoryStream(Resources.aero_crosshair))
{ {
Cursor = new Cursor(stream); Cursor = new Cursor(stream);
@ -57,7 +53,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
// If no layer selected, apply it to a new layer in the selected folder // If no layer selected, apply it to a new layer in the selected folder
else if (ProfileEditorService.SelectedProfileElement is Folder folder) else if (ProfileEditorService.SelectedProfileElement is Folder folder)
{ {
var newLayer = _renderElementService.CreateLayer(folder.Profile, folder, "New layer"); var newLayer = new Layer(folder, "New layer");
newLayer.AddLeds(selectedLeds); newLayer.AddLeds(selectedLeds);
ProfileEditorService.ChangeSelectedProfileElement(newLayer); ProfileEditorService.ChangeSelectedProfileElement(newLayer);
ProfileEditorService.UpdateSelectedProfileElement(); ProfileEditorService.UpdateSelectedProfileElement();
@ -66,7 +62,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
else else
{ {
var rootFolder = ProfileEditorService.SelectedProfile.GetRootFolder(); var rootFolder = ProfileEditorService.SelectedProfile.GetRootFolder();
var newLayer = _renderElementService.CreateLayer(rootFolder.Profile, rootFolder, "New layer"); var newLayer = new Layer(rootFolder, "New layer");
newLayer.AddLeds(selectedLeds); newLayer.AddLeds(selectedLeds);
ProfileEditorService.ChangeSelectedProfileElement(newLayer); ProfileEditorService.ChangeSelectedProfileElement(newLayer);
ProfileEditorService.UpdateSelectedProfileElement(); ProfileEditorService.UpdateSelectedProfileElement();