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

Profile editor - Added option to always update all data bindings

Profile editor - Moved data binding updating to main update loop
This commit is contained in:
Robert 2020-12-18 22:44:17 +01:00
parent 8136e48116
commit 099f56f4fe
6 changed files with 106 additions and 58 deletions

View File

@ -6,12 +6,12 @@ namespace Artemis.Core
/// <inheritdoc /> /// <inheritdoc />
public class DataBinding<TLayerProperty, TProperty> : IDataBinding public class DataBinding<TLayerProperty, TProperty> : IDataBinding
{ {
private TProperty _currentValue = default!;
private bool _disposed; private bool _disposed;
private TimeSpan _easingProgress; private TimeSpan _easingProgress;
private bool _reapplyValue;
private TProperty _currentValue = default!;
private TProperty _previousValue = default!;
private TProperty _lastAppliedValue = default!; private TProperty _lastAppliedValue = default!;
private TProperty _previousValue = default!;
private bool _reapplyValue;
internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration) internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
{ {
@ -100,27 +100,6 @@ namespace Artemis.Core
return Registration?.PropertyExpression.ReturnType; return Registration?.PropertyExpression.ReturnType;
} }
/// <summary>
/// Updates the smoothing progress of the data binding
/// </summary>
/// <param name="delta">The delta to apply during update</param>
public void UpdateWithDelta(TimeSpan delta)
{
if (_disposed)
throw new ObjectDisposedException("DataBinding");
// Data bindings cannot go back in time like brushes
if (delta < TimeSpan.Zero)
delta = TimeSpan.Zero;
_easingProgress = _easingProgress.Add(delta);
if (_easingProgress > EasingTime)
_easingProgress = EasingTime;
// Tell Apply() to apply a new value next call
_reapplyValue = false;
}
private void ResetEasing(TProperty value) private void ResetEasing(TProperty value)
{ {
_previousValue = GetInterpolatedValue(); _previousValue = GetInterpolatedValue();
@ -165,12 +144,30 @@ namespace Artemis.Core
{ {
// Don't update data bindings if there is no delta, otherwise this creates an inconsistency between // Don't update data bindings if there is no delta, otherwise this creates an inconsistency between
// data bindings with easing and data bindings without easing (the ones with easing will seemingly not update) // data bindings with easing and data bindings without easing (the ones with easing will seemingly not update)
if (timeline.Delta == TimeSpan.Zero) if (timeline.Delta == TimeSpan.Zero)
return; return;
UpdateWithDelta(timeline.Delta); UpdateWithDelta(timeline.Delta);
} }
/// <inheritdoc />
public void UpdateWithDelta(TimeSpan delta)
{
if (_disposed)
throw new ObjectDisposedException("DataBinding");
// Data bindings cannot go back in time like brushes
if (delta < TimeSpan.Zero)
delta = TimeSpan.Zero;
_easingProgress = _easingProgress.Add(delta);
if (_easingProgress > EasingTime)
_easingProgress = EasingTime;
// Tell Apply() to apply a new value next call
_reapplyValue = false;
}
/// <inheritdoc /> /// <inheritdoc />
public void Apply() public void Apply()
{ {

View File

@ -9,6 +9,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public interface IDataBinding : IStorageModel, IUpdateModel, IDisposable public interface IDataBinding : IStorageModel, IUpdateModel, IDisposable
{ {
/// <summary>
/// Updates the smoothing progress of the data binding and recalculates the value next <see cref="Apply" /> call
/// </summary>
/// <param name="delta">The delta to apply during update</param>
void UpdateWithDelta(TimeSpan delta);
/// <summary> /// <summary>
/// Applies the data binding to the layer property /// Applies the data binding to the layer property
/// </summary> /// </summary>

View File

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using System.Timers; using System.Timers;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline; using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
using Artemis.UI.Shared; using Artemis.UI.Shared;
@ -13,8 +14,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
public sealed class DataBindingViewModel<TLayerProperty, TProperty> : Conductor<IDataBindingModeViewModel>, IDataBindingViewModel public sealed class DataBindingViewModel<TLayerProperty, TProperty> : Conductor<IDataBindingModeViewModel>, IDataBindingViewModel
{ {
private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
private readonly ICoreService _coreService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly Timer _updateTimer;
private int _easingTime; private int _easingTime;
private bool _isDataBindingEnabled; private bool _isDataBindingEnabled;
private bool _isEasingTimeEnabled; private bool _isEasingTimeEnabled;
@ -23,22 +24,26 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private bool _updating; private bool _updating;
private bool _updatingTestResult; private bool _updatingTestResult;
private DateTime _lastUpdate;
public DataBindingViewModel(DataBindingRegistration<TLayerProperty, TProperty> registration, public DataBindingViewModel(DataBindingRegistration<TLayerProperty, TProperty> registration,
ICoreService coreService,
ISettingsService settingsService,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,
IDataModelUIService dataModelUIService, IDataModelUIService dataModelUIService,
IDataBindingsVmFactory dataBindingsVmFactory) IDataBindingsVmFactory dataBindingsVmFactory)
{ {
Registration = registration; Registration = registration;
_coreService = coreService;
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_dataBindingsVmFactory = dataBindingsVmFactory; _dataBindingsVmFactory = dataBindingsVmFactory;
_updateTimer = new Timer(40);
if (Registration.Member != null) if (Registration.Member != null)
DisplayName = Registration.Member.Name.ToUpper(); DisplayName = Registration.Member.Name.ToUpper();
else else
DisplayName = Registration.LayerProperty.PropertyDescription.Name.ToUpper(); DisplayName = Registration.LayerProperty.PropertyDescription.Name.ToUpper();
AlwaysApplyDataBindings = settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType))); DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType)));
EasingViewModels = new BindableCollection<TimelineEasingViewModel>(); EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true); TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
@ -46,7 +51,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
} }
public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; } public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; }
public PluginSetting<bool> AlwaysApplyDataBindings { get; }
public BindableCollection<ValueDescription> DataBindingModes { get; } public BindableCollection<ValueDescription> DataBindingModes { get; }
public BindableCollection<TimelineEasingViewModel> EasingViewModels { get; } public BindableCollection<TimelineEasingViewModel> EasingViewModels { get; }
public DataModelDisplayViewModel TestInputValue { get; } public DataModelDisplayViewModel TestInputValue { get; }
@ -67,7 +72,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
get => _isDataBindingEnabled; get => _isDataBindingEnabled;
set => SetAndNotify(ref _isDataBindingEnabled, value); set => SetAndNotify(ref _isDataBindingEnabled, value);
} }
public TimelineEasingViewModel SelectedEasingViewModel public TimelineEasingViewModel SelectedEasingViewModel
{ {
get => _selectedEasingViewModel; get => _selectedEasingViewModel;
@ -108,9 +113,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{ {
EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(v => new TimelineEasingViewModel(v, false))); EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(v => new TimelineEasingViewModel(v, false)));
_updateTimer.Start(); _lastUpdate = DateTime.Now;
_updateTimer.Elapsed += OnUpdateTimerOnElapsed; _coreService.FrameRendered += OnFrameRendered;
CreateDataBindingModeModeViewModel(); CreateDataBindingModeModeViewModel();
Update(); Update();
} }
@ -205,7 +209,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
return; return;
_updatingTestResult = true; _updatingTestResult = true;
if (Registration.DataBinding == null || ActiveItem == null) if (Registration.DataBinding == null || ActiveItem == null)
{ {
TestInputValue.UpdateValue(default); TestInputValue.UpdateValue(default);
@ -214,8 +217,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
return; return;
} }
// While playing in preview data bindings aren't updated // If always update is disabled, do constantly update the data binding as long as the view model is open
Registration.DataBinding.UpdateWithDelta(TimeSpan.FromMilliseconds(40)); // If always update is enabled, this is done for all data bindings in ProfileViewModel
if (!AlwaysApplyDataBindings.Value)
{
Registration.DataBinding.UpdateWithDelta(DateTime.Now - _lastUpdate);
_profileEditorService.UpdateProfilePreview();
}
if (ActiveItem.SupportsTestValue) if (ActiveItem.SupportsTestValue)
{ {
@ -227,8 +235,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{ {
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(default) : default); TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(default) : default);
} }
_profileEditorService.UpdateProfilePreview();
_updatingTestResult = false; _updatingTestResult = false;
} }
@ -252,6 +259,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
_profileEditorService.UpdateSelectedProfileElement(); _profileEditorService.UpdateSelectedProfileElement();
} }
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
{
UpdateTestResult();
_lastUpdate = DateTime.Now;
}
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{ {
UpdateTestResult(); UpdateTestResult();
@ -262,8 +275,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_updateTimer.Dispose(); _coreService.FrameRendered -= OnFrameRendered;
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
} }
#endregion #endregion

View File

@ -126,15 +126,20 @@
</ItemsControl> </ItemsControl>
</Grid> </Grid>
<StackPanel ZIndex="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10"> <StackPanel ZIndex="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10" Cursor="Arrow">
<materialDesign:Card Padding="8"> <materialDesign:Card Padding="8">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding OnlyShowSelectedShape.Value}" IsEnabled="False"> <CheckBox Style="{StaticResource MaterialDesignCheckBox}"
Only show shape of selected layer (NYI) IsChecked="{Binding HighlightSelectedLayer.Value}"
</CheckBox> ToolTip="If selected, dims all LEDs that are not part of the selected layer">
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" Margin="10 0 0 0" IsChecked="{Binding HighlightSelectedLayer.Value}">
Highlight LEDs of selected layer Highlight LEDs of selected layer
</CheckBox> </CheckBox>
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
Margin="10 0 0 0"
IsChecked="{Binding AlwaysApplyDataBindings.Value}"
ToolTip="If selected, constantly updates all data bindings instead of only when playing">
Constantly apply data bindings in editor
</CheckBox>
</StackPanel> </StackPanel>
</materialDesign:Card> </materialDesign:Card>
</StackPanel> </StackPanel>

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
@ -18,6 +18,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
public class ProfileViewModel : Screen, IProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent> public class ProfileViewModel : Screen, IProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>, IHandle<MainWindowKeyEvent>
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly ICoreService _coreService;
private readonly IProfileLayerVmFactory _profileLayerVmFactory; private readonly IProfileLayerVmFactory _profileLayerVmFactory;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly ISurfaceService _surfaceService; private readonly ISurfaceService _surfaceService;
@ -31,13 +32,15 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
private BindableCollection<ArtemisDevice> _devices; private BindableCollection<ArtemisDevice> _devices;
private BindableCollection<ArtemisLed> _highlightedLeds; private BindableCollection<ArtemisLed> _highlightedLeds;
private PluginSetting<bool> _highlightSelectedLayer; private PluginSetting<bool> _highlightSelectedLayer;
private PluginSetting<bool> _onlyShowSelectedShape; private PluginSetting<bool> _alwaysApplyDataBindings;
private PanZoomViewModel _panZoomViewModel; private PanZoomViewModel _panZoomViewModel;
private Layer _previousSelectedLayer; private Layer _previousSelectedLayer;
private int _previousTool; private int _previousTool;
private BindableCollection<ArtemisLed> _selectedLeds; private BindableCollection<ArtemisLed> _selectedLeds;
private DateTime _lastUpdate;
public ProfileViewModel(IProfileEditorService profileEditorService, public ProfileViewModel(IProfileEditorService profileEditorService,
ICoreService coreService,
ISurfaceService surfaceService, ISurfaceService surfaceService,
ISettingsService settingsService, ISettingsService settingsService,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
@ -45,6 +48,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
IProfileLayerVmFactory profileLayerVmFactory) IProfileLayerVmFactory profileLayerVmFactory)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_coreService = coreService;
_surfaceService = surfaceService; _surfaceService = surfaceService;
_settingsService = settingsService; _settingsService = settingsService;
_visualizationToolVmFactory = visualizationToolVmFactory; _visualizationToolVmFactory = visualizationToolVmFactory;
@ -102,10 +106,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
set => SetAndNotify(ref _selectedLeds, value); set => SetAndNotify(ref _selectedLeds, value);
} }
public PluginSetting<bool> OnlyShowSelectedShape public PluginSetting<bool> AlwaysApplyDataBindings
{ {
get => _onlyShowSelectedShape; get => _alwaysApplyDataBindings;
set => SetAndNotify(ref _onlyShowSelectedShape, value); set => SetAndNotify(ref _alwaysApplyDataBindings, value);
} }
public PluginSetting<bool> HighlightSelectedLayer public PluginSetting<bool> HighlightSelectedLayer
@ -170,9 +174,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
{ {
ApplyActiveProfile(); ApplyActiveProfile();
OnlyShowSelectedShape = _settingsService.GetSetting("ProfileEditor.OnlyShowSelectedShape", true); AlwaysApplyDataBindings = _settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
HighlightSelectedLayer = _settingsService.GetSetting("ProfileEditor.HighlightSelectedLayer", true); HighlightSelectedLayer = _settingsService.GetSetting("ProfileEditor.HighlightSelectedLayer", true);
_lastUpdate = DateTime.Now;
_coreService.FrameRendered += OnFrameRendered;
HighlightSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged; HighlightSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged;
_surfaceService.ActiveSurfaceConfigurationSelected += OnActiveSurfaceConfigurationSelected; _surfaceService.ActiveSurfaceConfigurationSelected += OnActiveSurfaceConfigurationSelected;
_profileEditorService.ProfileSelected += OnProfileSelected; _profileEditorService.ProfileSelected += OnProfileSelected;
@ -181,9 +188,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
base.OnInitialActivate(); base.OnInitialActivate();
} }
protected override void OnClose() protected override void OnClose()
{ {
_coreService.FrameRendered -= OnFrameRendered;
HighlightSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged; HighlightSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged;
_surfaceService.ActiveSurfaceConfigurationSelected -= OnActiveSurfaceConfigurationSelected; _surfaceService.ActiveSurfaceConfigurationSelected -= OnActiveSurfaceConfigurationSelected;
_profileEditorService.ProfileSelected -= OnProfileSelected; _profileEditorService.ProfileSelected -= OnProfileSelected;
@ -192,7 +200,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
if (_previousSelectedLayer != null) if (_previousSelectedLayer != null)
_previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated; _previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
OnlyShowSelectedShape.Save(); AlwaysApplyDataBindings.Save();
HighlightSelectedLayer.Save(); HighlightSelectedLayer.Save();
foreach (CanvasViewModel canvasViewModel in CanvasViewModels) foreach (CanvasViewModel canvasViewModel in CanvasViewModels)
@ -355,6 +363,27 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
#region Event handlers #region Event handlers
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
{
TimeSpan delta = DateTime.Now - _lastUpdate;
_lastUpdate = DateTime.Now;
if (!AlwaysApplyDataBindings.Value || _profileEditorService.SelectedProfile == null)
return;
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllFolders()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllLayers()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
// TODO: Only update when there are data bindings
_profileEditorService.UpdateProfilePreview();
}
private void HighlightSelectedLayerOnSettingChanged(object sender, EventArgs e) private void HighlightSelectedLayerOnSettingChanged(object sender, EventArgs e)
{ {
UpdateLedsDimStatus(); UpdateLedsDimStatus();

View File

@ -103,16 +103,15 @@ namespace Artemis.UI.Services
/// <inheritdoc /> /// <inheritdoc />
public SKPoint GetScaledPoint(Layer layer, SKPoint point, bool absolute) public SKPoint GetScaledPoint(Layer layer, SKPoint point, bool absolute)
{ {
double renderScale = _settingsService.GetSetting("Core.RenderScale", 0.5).Value;
if (absolute) if (absolute)
return new SKPoint( return new SKPoint(
100f / layer.Bounds.Width * ((float) (point.X * renderScale) - layer.Bounds.Left) / 100f, 100f / layer.Bounds.Width * (point.X - layer.Bounds.Left) / 100f,
100f / layer.Bounds.Height * ((float) (point.Y * renderScale) - layer.Bounds.Top) / 100f 100f / layer.Bounds.Height * (point.Y - layer.Bounds.Top) / 100f
); );
return new SKPoint( return new SKPoint(
100f / layer.Bounds.Width * (float) (point.X * renderScale) / 100f, 100f / layer.Bounds.Width * point.X / 100f,
100f / layer.Bounds.Height * (float) (point.Y * renderScale) / 100f 100f / layer.Bounds.Height * point.Y / 100f
); );
} }