diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs index 0772ade79..5984e5432 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -6,12 +6,12 @@ namespace Artemis.Core /// public class DataBinding : IDataBinding { + private TProperty _currentValue = default!; private bool _disposed; private TimeSpan _easingProgress; - private bool _reapplyValue; - private TProperty _currentValue = default!; - private TProperty _previousValue = default!; private TProperty _lastAppliedValue = default!; + private TProperty _previousValue = default!; + private bool _reapplyValue; internal DataBinding(DataBindingRegistration dataBindingRegistration) { @@ -100,27 +100,6 @@ namespace Artemis.Core return Registration?.PropertyExpression.ReturnType; } - /// - /// Updates the smoothing progress of the data binding - /// - /// The delta to apply during update - 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) { _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 // 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; - + UpdateWithDelta(timeline.Delta); } + /// + 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; + } + /// public void Apply() { diff --git a/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs index a609b1b3a..c2913c85f 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs @@ -9,6 +9,12 @@ namespace Artemis.Core /// public interface IDataBinding : IStorageModel, IUpdateModel, IDisposable { + /// + /// Updates the smoothing progress of the data binding and recalculates the value next call + /// + /// The delta to apply during update + void UpdateWithDelta(TimeSpan delta); + /// /// Applies the data binding to the layer property /// diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs index 3f8b67c94..7adf0622e 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Timers; using Artemis.Core; +using Artemis.Core.Services; using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline; using Artemis.UI.Shared; @@ -13,8 +14,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings public sealed class DataBindingViewModel : Conductor, IDataBindingViewModel { private readonly IDataBindingsVmFactory _dataBindingsVmFactory; + private readonly ICoreService _coreService; private readonly IProfileEditorService _profileEditorService; - private readonly Timer _updateTimer; private int _easingTime; private bool _isDataBindingEnabled; private bool _isEasingTimeEnabled; @@ -23,22 +24,26 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings private bool _updating; private bool _updatingTestResult; + private DateTime _lastUpdate; public DataBindingViewModel(DataBindingRegistration registration, + ICoreService coreService, + ISettingsService settingsService, IProfileEditorService profileEditorService, IDataModelUIService dataModelUIService, IDataBindingsVmFactory dataBindingsVmFactory) { Registration = registration; + _coreService = coreService; _profileEditorService = profileEditorService; _dataBindingsVmFactory = dataBindingsVmFactory; - _updateTimer = new Timer(40); if (Registration.Member != null) DisplayName = Registration.Member.Name.ToUpper(); else DisplayName = Registration.LayerProperty.PropertyDescription.Name.ToUpper(); + AlwaysApplyDataBindings = settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true); DataBindingModes = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType))); EasingViewModels = new BindableCollection(); TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true); @@ -46,7 +51,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings } public DataBindingRegistration Registration { get; } - + public PluginSetting AlwaysApplyDataBindings { get; } public BindableCollection DataBindingModes { get; } public BindableCollection EasingViewModels { get; } public DataModelDisplayViewModel TestInputValue { get; } @@ -67,7 +72,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings get => _isDataBindingEnabled; set => SetAndNotify(ref _isDataBindingEnabled, value); } - + public TimelineEasingViewModel SelectedEasingViewModel { get => _selectedEasingViewModel; @@ -108,9 +113,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast().Select(v => new TimelineEasingViewModel(v, false))); - _updateTimer.Start(); - _updateTimer.Elapsed += OnUpdateTimerOnElapsed; - + _lastUpdate = DateTime.Now; + _coreService.FrameRendered += OnFrameRendered; CreateDataBindingModeModeViewModel(); Update(); } @@ -205,7 +209,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings return; _updatingTestResult = true; - if (Registration.DataBinding == null || ActiveItem == null) { TestInputValue.UpdateValue(default); @@ -214,8 +217,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings return; } - // While playing in preview data bindings aren't updated - Registration.DataBinding.UpdateWithDelta(TimeSpan.FromMilliseconds(40)); + // If always update is disabled, do constantly update the data binding as long as the view model is open + // 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) { @@ -227,8 +235,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(default) : default); } - - _profileEditorService.UpdateProfilePreview(); + _updatingTestResult = false; } @@ -252,6 +259,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings _profileEditorService.UpdateSelectedProfileElement(); } + private void OnFrameRendered(object sender, FrameRenderedEventArgs e) + { + UpdateTestResult(); + _lastUpdate = DateTime.Now; + } + private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e) { UpdateTestResult(); @@ -262,8 +275,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings /// public void Dispose() { - _updateTimer.Dispose(); - _updateTimer.Elapsed -= OnUpdateTimerOnElapsed; + _coreService.FrameRendered -= OnFrameRendered; } #endregion diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml index 52c232a23..aea990eaf 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileView.xaml @@ -126,15 +126,20 @@ - + - - Only show shape of selected layer (NYI) - - + Highlight LEDs of selected layer + + Constantly apply data bindings in editor + diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index ccd778935..c2f4f935c 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Windows; @@ -18,6 +18,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization public class ProfileViewModel : Screen, IProfileEditorPanelViewModel, IHandle, IHandle { private readonly IProfileEditorService _profileEditorService; + private readonly ICoreService _coreService; private readonly IProfileLayerVmFactory _profileLayerVmFactory; private readonly ISettingsService _settingsService; private readonly ISurfaceService _surfaceService; @@ -31,13 +32,15 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization private BindableCollection _devices; private BindableCollection _highlightedLeds; private PluginSetting _highlightSelectedLayer; - private PluginSetting _onlyShowSelectedShape; + private PluginSetting _alwaysApplyDataBindings; private PanZoomViewModel _panZoomViewModel; private Layer _previousSelectedLayer; private int _previousTool; private BindableCollection _selectedLeds; + private DateTime _lastUpdate; public ProfileViewModel(IProfileEditorService profileEditorService, + ICoreService coreService, ISurfaceService surfaceService, ISettingsService settingsService, IEventAggregator eventAggregator, @@ -45,6 +48,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization IProfileLayerVmFactory profileLayerVmFactory) { _profileEditorService = profileEditorService; + _coreService = coreService; _surfaceService = surfaceService; _settingsService = settingsService; _visualizationToolVmFactory = visualizationToolVmFactory; @@ -102,10 +106,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization set => SetAndNotify(ref _selectedLeds, value); } - public PluginSetting OnlyShowSelectedShape + public PluginSetting AlwaysApplyDataBindings { - get => _onlyShowSelectedShape; - set => SetAndNotify(ref _onlyShowSelectedShape, value); + get => _alwaysApplyDataBindings; + set => SetAndNotify(ref _alwaysApplyDataBindings, value); } public PluginSetting HighlightSelectedLayer @@ -170,9 +174,12 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization { ApplyActiveProfile(); - OnlyShowSelectedShape = _settingsService.GetSetting("ProfileEditor.OnlyShowSelectedShape", true); + AlwaysApplyDataBindings = _settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true); HighlightSelectedLayer = _settingsService.GetSetting("ProfileEditor.HighlightSelectedLayer", true); + _lastUpdate = DateTime.Now; + _coreService.FrameRendered += OnFrameRendered; + HighlightSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged; _surfaceService.ActiveSurfaceConfigurationSelected += OnActiveSurfaceConfigurationSelected; _profileEditorService.ProfileSelected += OnProfileSelected; @@ -181,9 +188,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization base.OnInitialActivate(); } - + protected override void OnClose() { + _coreService.FrameRendered -= OnFrameRendered; HighlightSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged; _surfaceService.ActiveSurfaceConfigurationSelected -= OnActiveSurfaceConfigurationSelected; _profileEditorService.ProfileSelected -= OnProfileSelected; @@ -192,7 +200,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization if (_previousSelectedLayer != null) _previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated; - OnlyShowSelectedShape.Save(); + AlwaysApplyDataBindings.Save(); HighlightSelectedLayer.Save(); foreach (CanvasViewModel canvasViewModel in CanvasViewModels) @@ -355,6 +363,27 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization #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) { UpdateLedsDimStatus(); diff --git a/src/Artemis.UI/Services/LayerEditorService.cs b/src/Artemis.UI/Services/LayerEditorService.cs index 52b12adde..7fad79c3d 100644 --- a/src/Artemis.UI/Services/LayerEditorService.cs +++ b/src/Artemis.UI/Services/LayerEditorService.cs @@ -103,16 +103,15 @@ namespace Artemis.UI.Services /// public SKPoint GetScaledPoint(Layer layer, SKPoint point, bool absolute) { - double renderScale = _settingsService.GetSetting("Core.RenderScale", 0.5).Value; if (absolute) return new SKPoint( - 100f / layer.Bounds.Width * ((float) (point.X * renderScale) - layer.Bounds.Left) / 100f, - 100f / layer.Bounds.Height * ((float) (point.Y * renderScale) - layer.Bounds.Top) / 100f + 100f / layer.Bounds.Width * (point.X - layer.Bounds.Left) / 100f, + 100f / layer.Bounds.Height * (point.Y - layer.Bounds.Top) / 100f ); return new SKPoint( - 100f / layer.Bounds.Width * (float) (point.X * renderScale) / 100f, - 100f / layer.Bounds.Height * (float) (point.Y * renderScale) / 100f + 100f / layer.Bounds.Width * point.X / 100f, + 100f / layer.Bounds.Height * point.Y / 100f ); }