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

Data bindings - Fixed internal easing state

Data bindings - Some UI improvements :)
Data bindings - Added floor modifier
Layer properties - Fixed new base values not applying
This commit is contained in:
SpoinkyNL 2020-09-07 21:41:39 +02:00
parent 6b309cb1f3
commit 5d540530af
12 changed files with 148 additions and 115 deletions

View File

@ -17,7 +17,6 @@ namespace Artemis.Core
private readonly List<DataBindingModifier> _modifiers = new List<DataBindingModifier>();
private object _currentValue;
private bool _isInitialized;
private object _previousValue;
private TimeSpan _easingProgress;
@ -200,6 +199,9 @@ namespace Artemis.Core
/// <param name="deltaTime">The time in seconds that passed since the last update</param>
public void Update(double deltaTime)
{
// Data bindings cannot go back in time like brushes
deltaTime = Math.Max(0, deltaTime);
_easingProgress = _easingProgress.Add(TimeSpan.FromSeconds(deltaTime));
if (_easingProgress > EasingTime)
_easingProgress = EasingTime;
@ -256,11 +258,8 @@ namespace Artemis.Core
internal void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
{
if (_isInitialized)
throw new ArtemisCoreException("Data binding is already initialized");
// Source
if (Entity.SourceDataModelGuid != null)
if (Entity.SourceDataModelGuid != null && SourceDataModel == null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.SourceDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(Entity.SourcePropertyPath))
@ -270,8 +269,6 @@ namespace Artemis.Core
// Modifiers
foreach (var dataBindingModifier in Modifiers)
dataBindingModifier.Initialize(dataModelService, dataBindingService);
_isInitialized = true;
}
private void ApplyRegistration(DataBindingRegistration dataBindingRegistration)

View File

@ -4,8 +4,6 @@ using System.Linq.Expressions;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile.DataBindings;
using LiteDB.Engine;
using Microsoft.VisualBasic;
using Newtonsoft.Json;
namespace Artemis.Core
@ -16,10 +14,9 @@ namespace Artemis.Core
public class DataBindingModifier
{
private DataBinding _dataBinding;
private bool _isInitialized;
/// <summary>
/// Creates a new instance of the <see cref="DataBindingModifier"/> class
/// Creates a new instance of the <see cref="DataBindingModifier" /> class
/// </summary>
/// <param name="parameterType">The type of the parameter, can either be dynamic (based on a data model value) or static</param>
public DataBindingModifier(ProfileRightSideType parameterType)
@ -83,7 +80,7 @@ namespace Artemis.Core
public object ParameterStaticValue { get; private set; }
/// <summary>
/// A compiled expression tree that when given a matching data model returns the value of the modifiers parameter
/// A compiled expression tree that when given a matching data model returns the value of the modifiers parameter
/// </summary>
public Func<DataModel, object> CompiledParameterAccessor { get; set; }
@ -96,13 +93,19 @@ namespace Artemis.Core
/// <returns>The modified value</returns>
public object Apply(object currentValue)
{
if (ModifierType == null)
return currentValue;
if (!ModifierType.SupportsParameter)
return ModifierType.Apply(currentValue, null);
if (ParameterType == ProfileRightSideType.Dynamic && CompiledParameterAccessor != null)
{
var value = CompiledParameterAccessor(ParameterDataModel);
return ModifierType.Apply(currentValue, value);
}
if (ParameterType == ProfileRightSideType.Static && ModifierType != null)
if (ParameterType == ProfileRightSideType.Static)
return ModifierType.Apply(currentValue, ParameterStaticValue);
return currentValue;
@ -186,11 +189,8 @@ namespace Artemis.Core
internal void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
{
if (_isInitialized)
throw new ArtemisCoreException("Data binding modifier is already initialized");
// Modifier type
if (Entity.ModifierTypePluginGuid != null)
if (Entity.ModifierTypePluginGuid != null && ModifierType == null)
{
var modifierType = dataBindingService.GetModifierType(Entity.ModifierTypePluginGuid.Value, Entity.ModifierType);
if (modifierType != null)
@ -198,14 +198,14 @@ namespace Artemis.Core
}
// Dynamic parameter
if (ParameterType == ProfileRightSideType.Dynamic && Entity.ParameterDataModelGuid != null)
if (ParameterType == ProfileRightSideType.Dynamic && Entity.ParameterDataModelGuid != null && ParameterDataModel == null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.ParameterDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(Entity.ParameterPropertyPath))
UpdateParameter(dataModel, Entity.ParameterPropertyPath);
}
// Static parameter
else if (ParameterType == ProfileRightSideType.Static && Entity.ParameterStaticValue != null)
else if (ParameterType == ProfileRightSideType.Static && Entity.ParameterStaticValue != null && ParameterStaticValue == null)
{
// Use the target type so JSON.NET has a better idea what to do
var targetType = DataBinding.TargetProperty.PropertyType;
@ -224,41 +224,6 @@ namespace Artemis.Core
UpdateParameter(staticValue);
}
_isInitialized = true;
}
private void CreateExpression()
{
CompiledParameterAccessor = null;
if (ModifierType == null || DataBinding == null)
return;
if (ParameterType == ProfileRightSideType.Dynamic && ModifierType.SupportsParameter)
{
if (ParameterDataModel == null)
return;
// If the right side value is null, the constant type cannot be inferred and must be provided based on the data binding target
var parameterAccessor = CreateAccessor(ParameterDataModel, ParameterPropertyPath, "parameter", out var rightSideParameter);
var lambda = Expression.Lambda<Func<DataModel, object>>(Expression.Convert(parameterAccessor, typeof(object)), rightSideParameter);
CompiledParameterAccessor = lambda.Compile();
}
}
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
{
var listType = dataModel.GetListTypeInPath(path);
if (listType != null)
throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list");
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
return path.Split('.').Aggregate<string, Expression>(
Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
Expression.Property
);
}
internal void ApplyToEntity()
@ -287,5 +252,38 @@ namespace Artemis.Core
// Parameter is done during initialize
}
private void CreateExpression()
{
CompiledParameterAccessor = null;
if (ModifierType == null || DataBinding == null)
return;
if (ParameterType == ProfileRightSideType.Dynamic && ModifierType.SupportsParameter)
{
if (ParameterDataModel == null)
return;
// If the right side value is null, the constant type cannot be inferred and must be provided based on the data binding target
var parameterAccessor = CreateAccessor(ParameterDataModel, ParameterPropertyPath, "parameter", out var rightSideParameter);
var lambda = Expression.Lambda<Func<DataModel, object>>(Expression.Convert(parameterAccessor, typeof(object)), rightSideParameter);
CompiledParameterAccessor = lambda.Compile();
}
}
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
{
var listType = dataModel.GetListTypeInPath(path);
if (listType != null)
throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list");
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
return path.Split('.').Aggregate<string, Expression>(
Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
Expression.Property
);
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace Artemis.Core
{
internal class FloorModifierType : DataBindingModifierType
{
public FloorModifierType()
{
SupportsParameter = false;
}
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
public override string Description => "Floor";
public override string Icon => "ArrowDownDropCircleOutline";
public override object Apply(object currentValue, object parameterValue)
{
var floatValue = Convert.ToSingle(currentValue);
return Math.Floor(floatValue);
}
}
}

View File

@ -39,6 +39,7 @@ namespace Artemis.Core
return;
base.BaseValue = value;
Update();
OnBaseValueChanged();
}
}
@ -104,7 +105,7 @@ namespace Artemis.Core
// Force an update so that the base value is applied to the current value and
// keyframes/data bindings are applied using the new base value
Update(0);
Update();
}
/// <summary>
@ -195,7 +196,7 @@ namespace Artemis.Core
/// <summary>
/// Updates the property, applying keyframes and data bindings to the current value
/// </summary>
internal void Update(double deltaTime)
internal void Update(double deltaTime = 0)
{
CurrentValue = BaseValue;

View File

@ -104,6 +104,8 @@ namespace Artemis.Core.Services
private void RegisterBuiltInModifiers()
{
RegisterModifierType(Constants.CorePluginInfo, new MultiplicationModifierType());
RegisterModifierType(Constants.CorePluginInfo, new DivideModifierType());
RegisterModifierType(Constants.CorePluginInfo, new FloorModifierType());
}
}
}

View File

@ -111,6 +111,8 @@ namespace Artemis.UI.Shared.Services
var profileElementEvent = new RenderProfileElementEventArgs(profileElement, SelectedProfileElement);
SelectedProfileElement = profileElement;
OnSelectedProfileElementChanged(profileElementEvent);
ChangeSelectedDataBinding(null);
}
}

View File

@ -37,7 +37,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
SelectModifierTypeCommand = new DelegateCommand(ExecuteSelectModifierTypeCommand);
// Initialize async, no need to wait for it
Execute.PostToUIThread(Initialize);
Execute.PostToUIThread(Update);
}
public DelegateCommand SelectModifierTypeCommand { get; }
@ -62,30 +62,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
get => _staticInputViewModel;
private set => SetAndNotify(ref _staticInputViewModel, value);
}
private void Initialize()
{
var sourceType = Modifier.DataBinding.GetSourceType();
if (sourceType == null)
throw new ArtemisUIException("Cannot initialize a data binding modifier VM for a data binding without a source");
if (Modifier.ParameterType == ProfileRightSideType.Dynamic)
{
StaticInputViewModel = null;
DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected;
DynamicSelectionViewModel.FilterTypes = new[] {sourceType};
}
else
{
DynamicSelectionViewModel = null;
StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(sourceType);
StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated;
}
Update();
}
private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
Modifier.UpdateParameter(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.PropertyPath);
@ -99,6 +76,27 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private void Update()
{
var sourceType = Modifier.DataBinding.GetSourceType();
if (sourceType == null)
throw new ArtemisUIException("Cannot use a data binding modifier VM for a data binding without a source");
if (Modifier.ModifierType == null || !Modifier.ModifierType.SupportsParameter)
{
StaticInputViewModel = null;
DynamicSelectionViewModel = null;
}
else if (Modifier.ParameterType == ProfileRightSideType.Dynamic)
{
StaticInputViewModel = null;
DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected;
DynamicSelectionViewModel.FilterTypes = new[] { sourceType };
}
else
{
DynamicSelectionViewModel = null;
StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(sourceType);
StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated;
}
// Modifier type
ModifierTypes.Clear();
@ -135,7 +133,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
else
Modifier.UpdateParameter(null, null);
Initialize();
Update();
}
}
}

View File

@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Timers;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
@ -11,21 +10,20 @@ using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public class DataBindingViewModel : PropertyChangedBase
public class DataBindingViewModel : PropertyChangedBase, IDisposable
{
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
private readonly Timer _updateTimer;
private DataBinding _dataBinding;
private int _easingTime;
private bool _isDataBindingEnabled;
private bool _isEasingTimeEnabled;
private DataBindingMode _selectedDataBindingMode;
private TimelineEasingViewModel _selectedEasingViewModel;
private DataModelDynamicViewModel _targetSelectionViewModel;
private object _testInputValue;
private object _testResultValue;
private TimelineEasingViewModel _selectedEasingViewModel;
private DataBindingMode _selectedDataBindingMode;
private int _easingTime;
private bool _isEasingTimeEnabled;
private bool _updating;
public DataBindingViewModel(DataBindingRegistration registration,
@ -37,7 +35,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
_profileEditorService = profileEditorService;
_dataModelUIService = dataModelUIService;
_dataBindingsVmFactory = dataBindingsVmFactory;
_updateTimer = new Timer(500);
DisplayName = Registration.Property.Name.ToUpper();
@ -181,18 +178,16 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(v => new TimelineEasingViewModel(null, v)));
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;
_profileEditorService.ProfilePreviewUpdated += ProfileEditorServiceOnProfilePreviewUpdated;
Update();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
}
private void Update()
{
if (_updating)
return;
if (DataBinding == null)
{
TargetSelectionViewModel.IsEnabled = false;
@ -201,7 +196,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
}
_updating = true;
SelectedDataBindingMode = DataBinding.Mode;
EasingTime = (int) DataBinding.EasingTime.TotalMilliseconds;
SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == DataBinding.EasingFunction);
@ -229,22 +224,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
Update();
}
private void DataBindingOnModifiersUpdated(object sender, EventArgs e)
{
UpdateModifierViewModels();
}
private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
DataBinding.UpdateSource(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
UpdateTestResult();
}
private void UpdateTestResult()
{
var currentValue = TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue();
@ -264,5 +243,29 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
foreach (var dataBindingModifier in DataBinding.Modifiers)
ModifierViewModels.Add(_dataBindingsVmFactory.DataBindingModifierViewModel(dataBindingModifier));
}
private void ProfileEditorServiceOnProfilePreviewUpdated(object sender, EventArgs e)
{
UpdateTestResult();
}
private void DataBindingOnModifiersUpdated(object sender, EventArgs e)
{
UpdateModifierViewModels();
}
private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
{
DataBinding.UpdateSource(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.PropertyPath);
_profileEditorService.UpdateSelectedProfileElement();
}
public void Dispose()
{
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
TargetSelectionViewModel.PropertySelected -= TargetSelectionViewModelOnPropertySelected;
if (DataBinding != null)
DataBinding.ModifiersUpdated -= DataBindingOnModifiersUpdated;
}
}
}

View File

@ -1,11 +1,12 @@
using System.Linq;
using System;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Ninject.Factories;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public class DataBindingsViewModel : PropertyChangedBase
public class DataBindingsViewModel : PropertyChangedBase, IDisposable
{
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private DataBindingsTabsViewModel _dataBindingsTabsViewModel;
@ -34,6 +35,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private void Initialise()
{
DataBindingViewModel?.Dispose();
DataBindingViewModel = null;
DataBindingsTabsViewModel = null;
@ -52,5 +54,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
DataBindingsTabsViewModel.Tabs.Add(_dataBindingsVmFactory.DataBindingViewModel(registration));
}
}
public void Dispose()
{
DataBindingViewModel?.Dispose();
}
}
}

View File

@ -204,6 +204,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
MainTimelineSegmentViewModel = null;
EndTimelineSegmentViewModel?.Dispose();
EndTimelineSegmentViewModel = null;
DataBindingsViewModel?.Dispose();
DataBindingsViewModel = null;
base.OnClose();
}
@ -241,6 +243,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
if (ProfileEditorService.SelectedDataBinding != null)
{
RightSideIndex = 1;
DataBindingsViewModel?.Dispose();
DataBindingsViewModel = _dataBindingsVmFactory.DataBindingsViewModel(ProfileEditorService.SelectedDataBinding);
}
else

View File

@ -29,7 +29,6 @@ namespace Artemis.Plugins.Modules.General.DataModels
public class TimeDataModel : DataModel
{
public DateTimeOffset CurrentTime { get; set; }
public long SecondsSinceUnixEpoch { get; set; }
public TimeSpan TimeSinceMidnight { get; set; }
}
}

View File

@ -41,7 +41,6 @@ namespace Artemis.Plugins.Modules.General
public override void Update(double deltaTime)
{
DataModel.TimeDataModel.CurrentTime = DateTimeOffset.Now;
DataModel.TimeDataModel.SecondsSinceUnixEpoch = DateTimeOffset.Now.ToUnixTimeSeconds();
DataModel.TimeDataModel.TimeSinceMidnight = DateTimeOffset.Now - DateTimeOffset.Now.Date;
UpdateCurrentWindow();
}