From 73f7bdbf1e5f1b77e615a44649f91a31f56b7afc Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 19 Feb 2020 21:33:51 +0100 Subject: [PATCH] Profile editor - Only save profile on drag-edit when mouse is released Profile editor - Format all numbers with 3 decimals Profile editor - Fixed drag-edit rounding errors with very small steps Logging - Output logs to debug Settings - Added logging level setting Nuget - Updated Serilog and the used sinks --- src/Artemis.Core/Artemis.Core.csproj | 5 +- src/Artemis.Core/Ninject/LoggerProvider.cs | 14 ++- src/Artemis.Core/Services/CoreService.cs | 17 +++- .../Services/Storage/ProfileService.cs | 15 +++- src/Artemis.Core/packages.config | 5 +- .../Artemis.UI.Shared.csproj | 8 -- src/Artemis.UI.Shared/DraggableFloat.xaml | 7 +- src/Artemis.UI.Shared/DraggableFloat.xaml.cs | 46 ++++++++-- src/Artemis.UI.Shared/UserControl1.xaml | 10 --- src/Artemis.UI.Shared/UserControl1.xaml.cs | 15 ---- src/Artemis.UI/Artemis.UI.csproj | 1 + .../PropertyInput/BrushPropertyInputView.xaml | 5 +- .../PropertyInput/EnumPropertyInputView.xaml | 4 +- .../PropertyInput/FloatPropertyInputView.xaml | 5 +- .../PropertyInput/IntPropertyInputView.xaml | 6 +- .../PropertyInput/PropertyInputViewModel.cs | 21 ++++- .../SKColorPropertyInputView.xaml | 2 - .../SKPointPropertyInputView.xaml | 9 +- .../SKSizePropertyInputView.xaml | 13 ++- .../Timeline/PropertyTimelineView.xaml | 8 +- .../Timeline/PropertyTimelineViewModel.cs | 61 +++++++------ .../PropertyTrackKeyframeViewModel.cs | 86 +++++++++++++------ .../Visualization/Tools/EditToolViewModel.cs | 8 +- .../Screens/Settings/SettingsView.xaml | 63 +++++++++----- .../Screens/Settings/SettingsViewModel.cs | 18 +++- src/Artemis.UI/Utilities/HitTestUtilities.cs | 32 +++++++ 26 files changed, 323 insertions(+), 161 deletions(-) delete mode 100644 src/Artemis.UI.Shared/UserControl1.xaml delete mode 100644 src/Artemis.UI.Shared/UserControl1.xaml.cs create mode 100644 src/Artemis.UI/Utilities/HitTestUtilities.cs diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 4919162c0..22a877977 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -90,8 +90,11 @@ ..\packages\Serilog.Enrichers.Demystify.1.0.0-dev-00019\lib\net45\Serilog.Enrichers.Demystify.dll + + ..\packages\Serilog.Sinks.Debug.1.0.1\lib\net46\Serilog.Sinks.Debug.dll + - ..\packages\Serilog.Sinks.File.4.0.0\lib\net45\Serilog.Sinks.File.dll + ..\packages\Serilog.Sinks.File.4.1.0\lib\net45\Serilog.Sinks.File.dll ..\packages\SkiaSharp.1.68.2-preview.29\lib\net45\SkiaSharp.dll diff --git a/src/Artemis.Core/Ninject/LoggerProvider.cs b/src/Artemis.Core/Ninject/LoggerProvider.cs index f279ec5e3..4ba1c8295 100644 --- a/src/Artemis.Core/Ninject/LoggerProvider.cs +++ b/src/Artemis.Core/Ninject/LoggerProvider.cs @@ -1,24 +1,30 @@ using Ninject.Activation; using Serilog; +using Serilog.Core; +using Serilog.Events; namespace Artemis.Core.Ninject { internal class LoggerProvider : Provider { - private static readonly ILogger _logger = new LoggerConfiguration() + internal static readonly LoggingLevelSwitch LoggingLevelSwitch = new LoggingLevelSwitch(LogEventLevel.Verbose); + + private static readonly ILogger Logger = new LoggerConfiguration() .Enrich.FromLogContext() .Enrich.WithDemystifiedStackTraces() .WriteTo.File("Logs/Artemis log-.txt", rollingInterval: RollingInterval.Day, - outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext:l}] {Message:lj}{NewLine}{Exception}") + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}") + .WriteTo.Debug() + .MinimumLevel.ControlledBy(LoggingLevelSwitch) .CreateLogger(); protected override ILogger CreateInstance(IContext context) { var requestingType = context.Request.ParentContext?.Plan?.Type; if (requestingType != null) - return _logger.ForContext(requestingType); - return _logger; + return Logger.ForContext(requestingType); + return Logger; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index af70048f2..4fbdecb35 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -4,12 +4,15 @@ using System.Threading.Tasks; using Artemis.Core.Events; using Artemis.Core.Exceptions; using Artemis.Core.JsonConverters; +using Artemis.Core.Ninject; using Artemis.Core.Plugins.Abstract; +using Artemis.Core.Plugins.Models; using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Storage.Interfaces; using Newtonsoft.Json; using RGB.NET.Core; using Serilog; +using Serilog.Events; using SkiaSharp; namespace Artemis.Core.Services @@ -25,16 +28,21 @@ namespace Artemis.Core.Services private readonly IRgbService _rgbService; private readonly ISurfaceService _surfaceService; private List _modules; + private PluginSetting _loggingLevel; - internal CoreService(ILogger logger, IPluginService pluginService, IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService) + internal CoreService(ILogger logger, ISettingsService settingsService, IPluginService pluginService, IRgbService rgbService, + ISurfaceService surfaceService, IProfileService profileService) { _logger = logger; _pluginService = pluginService; _rgbService = rgbService; _surfaceService = surfaceService; _profileService = profileService; + _loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information); + _rgbService.Surface.Updating += SurfaceOnUpdating; _rgbService.Surface.Updated += SurfaceOnUpdated; + _loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel(); _modules = _pluginService.GetPluginsOfType(); _pluginService.PluginEnabled += (sender, args) => _modules = _pluginService.GetPluginsOfType(); @@ -79,6 +87,7 @@ namespace Artemis.Core.Services throw new ArtemisCoreException("Cannot initialize the core as it is already initialized."); _logger.Information("Initializing Artemis Core version {version}", typeof(CoreService).Assembly.GetName().Version); + ApplyLoggingLevel(); // Initialize the services await Task.Run(() => _pluginService.CopyBuiltInPlugins()); @@ -95,6 +104,12 @@ namespace Artemis.Core.Services OnInitialized(); } + private void ApplyLoggingLevel() + { + _logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value); + LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value; + } + private void SurfaceOnUpdating(UpdatingEventArgs args) { try diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 15ba6c18e..b1a451cae 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -10,6 +10,7 @@ using Artemis.Core.Services.Storage.Interfaces; using Artemis.Storage.Entities.Profile; using Artemis.Storage.Repositories.Interfaces; using Newtonsoft.Json; +using Serilog; namespace Artemis.Core.Services.Storage { @@ -19,12 +20,14 @@ namespace Artemis.Core.Services.Storage public class ProfileService : IProfileService { private readonly ILayerService _layerService; + private readonly ILogger _logger; private readonly IPluginService _pluginService; private readonly IProfileRepository _profileRepository; private readonly ISurfaceService _surfaceService; - internal ProfileService(IPluginService pluginService, ISurfaceService surfaceService, ILayerService layerService, IProfileRepository profileRepository) + internal ProfileService(ILogger logger, IPluginService pluginService, ISurfaceService surfaceService, ILayerService layerService, IProfileRepository profileRepository) { + _logger = logger; _pluginService = pluginService; _surfaceService = surfaceService; _layerService = layerService; @@ -121,7 +124,10 @@ namespace Artemis.Core.Services.Storage public void UndoUpdateProfile(Profile profile, ProfileModule module) { if (!profile.UndoStack.Any()) + { + _logger.Debug("Undo profile update - Failed, undo stack empty"); return; + } ActivateProfile(module, null); var top = profile.UndoStack.Pop(); @@ -130,12 +136,17 @@ namespace Artemis.Core.Services.Storage profile.ProfileEntity = JsonConvert.DeserializeObject(top); profile.ApplyToProfile(); ActivateProfile(module, profile); + + _logger.Debug("Undo profile update - Success"); } public void RedoUpdateProfile(Profile profile, ProfileModule module) { if (!profile.RedoStack.Any()) + { + _logger.Debug("Redo profile update - Failed, redo empty"); return; + } ActivateProfile(module, null); var top = profile.RedoStack.Pop(); @@ -144,6 +155,8 @@ namespace Artemis.Core.Services.Storage profile.ProfileEntity = JsonConvert.DeserializeObject(top); profile.ApplyToProfile(); ActivateProfile(module, profile); + + _logger.Debug("Redo profile update - Success"); } private void InstantiateProfileLayerBrushes(Profile profile) diff --git a/src/Artemis.Core/packages.config b/src/Artemis.Core/packages.config index b37b39ea7..867b73146 100644 --- a/src/Artemis.Core/packages.config +++ b/src/Artemis.Core/packages.config @@ -1,5 +1,4 @@  - @@ -14,11 +13,13 @@ - + + + diff --git a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj index 17e92ea5a..aff7d6759 100644 --- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj +++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj @@ -91,20 +91,12 @@ Designer MSBuild:Compile - - MSBuild:Compile - Designer - ColorPicker.xaml - - UserControl1.xaml - Code - diff --git a/src/Artemis.UI.Shared/DraggableFloat.xaml b/src/Artemis.UI.Shared/DraggableFloat.xaml index c1be4cdc5..6f3c871f9 100644 --- a/src/Artemis.UI.Shared/DraggableFloat.xaml +++ b/src/Artemis.UI.Shared/DraggableFloat.xaml @@ -22,12 +22,13 @@ Height="17" Padding="1 0" Margin="0 4 0 0" - Text="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" + Text="{Binding Value, StringFormat=N3, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Cursor="/Resources/aero_drag_ew.cur" Foreground="{DynamicResource SecondaryAccentBrush}" MouseDown="InputMouseDown" MouseUp="InputMouseUp" - MouseMove="InputMouseMove" /> + MouseMove="InputMouseMove" + RequestBringIntoView="Input_OnRequestBringIntoView"/> @@ -36,7 +37,7 @@ Height="20" materialDesign:ValidationAssist.UsePopup="True" HorizontalAlignment="Left" - Text="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" + Text="{Binding Value, StringFormat=N3, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" LostFocus="InputLostFocus" KeyDown="InputKeyDown" RequestBringIntoView="Input_OnRequestBringIntoView" /> diff --git a/src/Artemis.UI.Shared/DraggableFloat.xaml.cs b/src/Artemis.UI.Shared/DraggableFloat.xaml.cs index 2a7b92050..c303a058b 100644 --- a/src/Artemis.UI.Shared/DraggableFloat.xaml.cs +++ b/src/Artemis.UI.Shared/DraggableFloat.xaml.cs @@ -24,9 +24,13 @@ namespace Artemis.UI.Shared typeof(RoutedPropertyChangedEventHandler), typeof(DraggableFloat)); + public event EventHandler DragStarted; + public event EventHandler DragEnded; + private bool _inCallback; private Point _mouseDragStartPoint; private float _startValue; + private bool _calledDragStarted; public DraggableFloat() { @@ -75,22 +79,33 @@ namespace Artemis.UI.Shared var position = e.GetPosition((IInputElement) sender); if (position == _mouseDragStartPoint) DisplayInput(); + else + { + OnDragEnded(); + _calledDragStarted = false; + } ((IInputElement) sender).ReleaseMouseCapture(); } private void InputMouseMove(object sender, MouseEventArgs e) { - if (e.LeftButton == MouseButtonState.Pressed) + if (e.LeftButton != MouseButtonState.Pressed) + return; + + if (!_calledDragStarted) { - // Use decimals for everything to avoid floating point errors - var startValue = new decimal(_startValue); - var startX = new decimal(_mouseDragStartPoint.X); - var x = new decimal(e.GetPosition((IInputElement) sender).X); - var stepSize = new decimal(StepSize); - - Value = (float) (Math.Round(startValue + stepSize * (x - startX) / stepSize) * stepSize); + OnDragStarted(); + _calledDragStarted = true; } + + // Use decimals for everything to avoid floating point errors + var startValue = new decimal(_startValue); + var startX = new decimal(_mouseDragStartPoint.X); + var x = new decimal(e.GetPosition((IInputElement) sender).X); + var stepSize = new decimal(StepSize); + + Value = (float) UltimateRoundingFunction(startValue + stepSize * (x - startX), stepSize, 0.5m); } private void InputLostFocus(object sender, RoutedEventArgs e) @@ -127,5 +142,20 @@ namespace Artemis.UI.Shared { e.Handled = true; } + + private static decimal UltimateRoundingFunction(decimal amountToRound, decimal nearstOf, decimal fairness) + { + return Math.Floor(amountToRound / nearstOf + fairness) * nearstOf; + } + + protected virtual void OnDragStarted() + { + DragStarted?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnDragEnded() + { + DragEnded?.Invoke(this, EventArgs.Empty); + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/UserControl1.xaml b/src/Artemis.UI.Shared/UserControl1.xaml deleted file mode 100644 index 9a4ccd683..000000000 --- a/src/Artemis.UI.Shared/UserControl1.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - \ No newline at end of file diff --git a/src/Artemis.UI.Shared/UserControl1.xaml.cs b/src/Artemis.UI.Shared/UserControl1.xaml.cs deleted file mode 100644 index 0e7afc80a..000000000 --- a/src/Artemis.UI.Shared/UserControl1.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Windows.Controls; - -namespace Artemis.UI.Shared -{ - /// - /// Interaction logic for UserControl1.xaml - /// - public partial class UserControl1 : UserControl - { - public UserControl1() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 9faed755c..4414933e6 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -227,6 +227,7 @@ + diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml index 70bb43b15..143a229bf 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml @@ -3,9 +3,7 @@ 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:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" - xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> @@ -19,8 +17,7 @@ ItemsSource="{Binding Path=EnumValues}" SelectedValuePath="Value" DisplayMemberPath="Description" - SelectedValue="{Binding Path=BrushInputValue}" - materialDesign:ComboBoxAssist.ClassicMode="True" /> + SelectedValue="{Binding Path=BrushInputValue}" /> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml index 489314187..7426a53d8 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml @@ -5,7 +5,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" - xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:EnumPropertyInputViewModel}"> @@ -20,8 +19,7 @@ ItemsSource="{Binding Path=EnumValues}" SelectedValuePath="Value" DisplayMemberPath="Description" - SelectedValue="{Binding Path=EnumInputValue}" - materialDesign:ComboBoxAssist.ClassicMode="True" /> + SelectedValue="{Binding Path=EnumInputValue}" /> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml index 815681cf1..bb8514f2e 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml @@ -11,7 +11,10 @@ d:DataContext="{d:DesignInstance local:FloatPropertyInputViewModel}"> - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml index 22df27bef..94b47cbb0 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml @@ -4,7 +4,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" - xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:s="https://github.com/canton7/Stylet" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" mc:Ignorable="d" @@ -12,7 +11,10 @@ d:DataContext="{d:DesignInstance local:IntPropertyInputViewModel}"> - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs index eb9ab657a..7aef6f5f4 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs @@ -13,10 +13,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P ProfileEditorService = profileEditorService; } - protected IProfileEditorService ProfileEditorService { get; set; } + protected IProfileEditorService ProfileEditorService { get; } public abstract List CompatibleTypes { get; } public bool Initialized { get; private set; } + public bool InputDragging { get; private set; } public LayerPropertyViewModel LayerPropertyViewModel { get; private set; } protected object InputValue @@ -56,7 +57,25 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P // Force the keyframe engine to update, the edited keyframe might affect the current keyframe progress LayerPropertyViewModel.LayerProperty.KeyframeEngine?.Update(0); + if (!InputDragging) + ProfileEditorService.UpdateSelectedProfileElement(); + else + ProfileEditorService.UpdateProfilePreview(); + } + + #region Event handlers + + public void InputDragStarted(object sender, EventArgs e) + { + InputDragging = true; + } + + public void InputDragEnded(object sender, EventArgs e) + { + InputDragging = false; ProfileEditorService.UpdateSelectedProfileElement(); } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml index 5884fc22f..1e26f6dfb 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml @@ -4,8 +4,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" - xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" - xmlns:s="https://github.com/canton7/Stylet" xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared" mc:Ignorable="d" diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml index 882bf4719..057d2e5a3 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml @@ -4,7 +4,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" - xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:s="https://github.com/canton7/Stylet" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" mc:Ignorable="d" @@ -14,11 +13,15 @@ + StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" + DragStarted="{s:Action InputDragStarted}" + DragEnded="{s:Action InputDragEnded}" /> , + StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" + DragStarted="{s:Action InputDragStarted}" + DragEnded="{s:Action InputDragEnded}" /> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml index 210cf7da8..7e9ec1852 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml @@ -4,7 +4,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" - xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:s="https://github.com/canton7/Stylet" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" mc:Ignorable="d" @@ -12,9 +11,17 @@ d:DataContext="{d:DesignInstance local:SKSizePropertyInputViewModel}"> - + , - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml index a3afcc31c..239fafe15 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml @@ -9,11 +9,11 @@ d:DesignHeight="25" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:PropertyTimelineViewModel}"> - - + @@ -28,7 +28,7 @@ - + - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs index 5cca8a591..ee0a8996d 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs @@ -7,6 +7,7 @@ using System.Windows.Media; using System.Windows.Shapes; using Artemis.UI.Ninject.Factories; using Artemis.UI.Services.Interfaces; +using Artemis.UI.Utilities; using Stylet; namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline @@ -95,26 +96,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline UpdateEndTime(); } - #region Keyframe movement - - public void MoveSelectedKeyframes(TimeSpan offset) - { - var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList(); - foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected)) - { - // TODO: Not ideal as this stacks them all if they get to 0, oh well - if (keyframeViewModel.Keyframe.Position + offset > TimeSpan.Zero) - { - keyframeViewModel.Keyframe.Position += offset; - keyframeViewModel.Update(LayerPropertiesViewModel.PixelsPerSecond); - } - } - - _profileEditorService.UpdateProfilePreview(); - } - - #endregion - private void CreateViewModels(LayerPropertyViewModel property) { PropertyTrackViewModels.Add(_propertyTrackVmFactory.Create(this, property)); @@ -122,6 +103,30 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline CreateViewModels(child); } + #region Keyframe movement + + public void MoveSelectedKeyframes(TimeSpan cursorTime) + { + // Ensure the selection rectangle doesn't show, the view isn't aware of different types of dragging + SelectionRectangle.Rect = new Rect(); + + var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList(); + foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected)) + keyframeViewModel.ApplyMovement(cursorTime); + + _profileEditorService.UpdateProfilePreview(); + } + + + public void ReleaseSelectedKeyframes() + { + var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList(); + foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected)) + keyframeViewModel.ReleaseMovement(); + } + + #endregion + #region Keyframe selection private Point _mouseDragStartPoint; @@ -130,6 +135,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline // ReSharper disable once UnusedMember.Global - Called from view public void TimelineCanvasMouseDown(object sender, MouseButtonEventArgs e) { + if (e.LeftButton == MouseButtonState.Released) + return; + ((IInputElement) sender).CaptureMouse(); SelectionRectangle.Rect = new Rect(); @@ -148,18 +156,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline var selectedRect = new Rect(_mouseDragStartPoint, position); SelectionRectangle.Rect = selectedRect; - // Find all keyframes in the rectangle - var selectedKeyframes = new List(); - var hitTestParams = new GeometryHitTestParameters(SelectionRectangle); - var resultCallback = new HitTestResultCallback(result => HitTestResultBehavior.Continue); - var filterCallback = new HitTestFilterCallback(element => - { - if (element is Ellipse ellipse) - selectedKeyframes.Add((PropertyTrackKeyframeViewModel) ellipse.DataContext); - return HitTestFilterBehavior.Continue; - }); - VisualTreeHelper.HitTest((Visual) sender, filterCallback, resultCallback, hitTestParams); - + var selectedKeyframes = HitTestUtilities.GetHitViewModels((Visual) sender, SelectionRectangle); var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList(); foreach (var keyframeViewModel in keyframeViewModels) keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs index 4844a7978..141d34b71 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs @@ -49,7 +49,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline if (e.LeftButton == MouseButtonState.Released) return; - ((IInputElement)sender).CaptureMouse(); + ((IInputElement) sender).CaptureMouse(); if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && !IsSelected) PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, true, false); else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) @@ -63,42 +63,45 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline public void KeyframeMouseUp(object sender, MouseButtonEventArgs e) { _profileEditorService.UpdateSelectedProfileElement(); + PropertyTrackViewModel.PropertyTimelineViewModel.ReleaseSelectedKeyframes(); - ((IInputElement)sender).ReleaseMouseCapture(); - e.Handled = true; + ((IInputElement) sender).ReleaseMouseCapture(); } public void KeyframeMouseMove(object sender, MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) - { - // Get the parent grid, need that for our position - var x = Math.Max(0, e.GetPosition(ParentView).X); - var newTime = TimeSpan.FromSeconds(x / _pixelsPerSecond); - - // Round the time to something that fits the current zoom level - if (_pixelsPerSecond < 200) - newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 5.0) * 5.0); - else if (_pixelsPerSecond < 500) - newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 2.0) * 2.0); - else - newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds)); - - // If shift is held, snap to the current time - // Take a tolerance of 5 pixels (half a keyframe width) - if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) - { - var tolerance = 1000f / _pixelsPerSecond * 5; - if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - newTime.TotalMilliseconds) < tolerance) - newTime = _profileEditorService.CurrentTime; - } - - PropertyTrackViewModel.PropertyTimelineViewModel.MoveSelectedKeyframes(newTime - Keyframe.Position); - } + PropertyTrackViewModel.PropertyTimelineViewModel.MoveSelectedKeyframes(GetCursorTime(e.GetPosition(ParentView))); e.Handled = true; } + private TimeSpan GetCursorTime(Point position) + { + // Get the parent grid, need that for our position + var x = Math.Max(0, position.X); + var time = TimeSpan.FromSeconds(x / _pixelsPerSecond); + + // Round the time to something that fits the current zoom level + if (_pixelsPerSecond < 200) + time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 5.0) * 5.0); + else if (_pixelsPerSecond < 500) + time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 2.0) * 2.0); + else + time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds)); + + // If shift is held, snap to the current time + // Take a tolerance of 5 pixels (half a keyframe width) + if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) + { + var tolerance = 1000f / _pixelsPerSecond * 5; + if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - time.TotalMilliseconds) < tolerance) + time = _profileEditorService.CurrentTime; + } + + return time; + } + #endregion #region Context menu actions @@ -137,5 +140,34 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline } #endregion + + #region Movement + + private bool _movementReleased = true; + private TimeSpan _startOffset; + + public void ApplyMovement(TimeSpan cursorTime) + { + if (_movementReleased) + { + _movementReleased = false; + _startOffset = cursorTime - Keyframe.Position; + } + else + { + Keyframe.Position = cursorTime - _startOffset; + if (Keyframe.Position < TimeSpan.Zero) + Keyframe.Position = TimeSpan.Zero; + + Update(_pixelsPerSecond); + } + } + + public void ReleaseMovement() + { + _movementReleased = true; + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs index b95553d00..828bf59c2 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs @@ -318,8 +318,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools // Scale down the resulting position and make it relative var scaled = _layerEditorService.GetScaledPoint(layer, position, true); - // Update the position property - layer.PositionProperty.SetCurrentValue(scaled, ProfileEditorService.CurrentTime); + // Round and update the position property + layer.PositionProperty.SetCurrentValue(RoundPoint(scaled, 3), ProfileEditorService.CurrentTime); ProfileEditorService.UpdateProfilePreview(); } @@ -338,13 +338,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools var scaled = _layerEditorService.GetScaledPoint(layer, countered[1], false); // Update the anchor point, this causes the shape to move - layer.AnchorPointProperty.SetCurrentValue(RoundPoint(scaled, 5), ProfileEditorService.CurrentTime); + layer.AnchorPointProperty.SetCurrentValue(RoundPoint(scaled, 3), ProfileEditorService.CurrentTime); // TopLeft is not updated yet and acts as a snapshot of the top-left before changing the anchor var path = _layerEditorService.GetLayerPath(layer, true, true, true); // Calculate the (scaled) difference between the old and now position var difference = _layerEditorService.GetScaledPoint(layer, _topLeft - path.Points[0], false); // Apply the difference so that the shape effectively stays in place - layer.PositionProperty.SetCurrentValue(RoundPoint(layer.PositionProperty.CurrentValue + difference, 5), ProfileEditorService.CurrentTime); + layer.PositionProperty.SetCurrentValue(RoundPoint(layer.PositionProperty.CurrentValue + difference, 3), ProfileEditorService.CurrentTime); ProfileEditorService.UpdateProfilePreview(); } diff --git a/src/Artemis.UI/Screens/Settings/SettingsView.xaml b/src/Artemis.UI/Screens/Settings/SettingsView.xaml index 45d1af187..449ba2f28 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsView.xaml +++ b/src/Artemis.UI/Screens/Settings/SettingsView.xaml @@ -36,7 +36,7 @@ - Start up with Windows + Start up with Windows @@ -54,7 +54,7 @@ - Start up with Windows minimized + Start up with Windows minimized @@ -72,8 +72,8 @@ - Debugger - + Debugger + Use the debugger to see the raw image Artemis is rendering on the surface. @@ -95,18 +95,17 @@ - Logs - - Opens the directory where logs are stored. + Application files + + Opens the directory where application files like plugins and settings are stored. - - @@ -119,17 +118,39 @@ - Application files - - Opens the directory where application files like plugins and settings are stored. + Logs + + Opens the directory where logs are stored. - + + + + + + + + + + + + + Log level + + Sets the logging level, a verbose logging level will result in more log files. + + + + + + + @@ -147,8 +168,8 @@ - Render scale - + Render scale + Sets the resolution Artemis renders at, higher scale means more CPU-usage, especially on large surfaces. @@ -168,8 +189,8 @@ - Target framerate - + Target framerate + Sets the FPS Artemis tries to render at, higher FPS means more CPU-usage but smoother animations. @@ -189,8 +210,8 @@ - LED sample size - + LED sample size + Sets the amount of samples that is taken to determine each LEDs color. This means a LED can be semi off if it is not completely covered by a color. diff --git a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs index f1324d1f7..06c247d1e 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs @@ -12,8 +12,10 @@ using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens.Settings.Debug; using Artemis.UI.Screens.Settings.Tabs.Devices; using Artemis.UI.Screens.Settings.Tabs.Plugins; +using Artemis.UI.Shared.Utilities; using MaterialDesignThemes.Wpf; using Ninject; +using Serilog.Events; using Stylet; namespace Artemis.UI.Screens.Settings @@ -26,6 +28,7 @@ namespace Artemis.UI.Screens.Settings private readonly ISettingsService _settingsService; private readonly ISurfaceService _surfaceService; private readonly IWindowManager _windowManager; + private object _test; public SettingsViewModel(IKernel kernel, ISurfaceService surfaceService, @@ -47,6 +50,8 @@ namespace Artemis.UI.Screens.Settings DeviceSettingsViewModels = new BindableCollection(); Plugins = new BindableCollection(); + + LogLevels = EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel)); RenderScales = new List> {new Tuple("10%", 0.1)}; for (var i = 25; i <= 100; i += 25) RenderScales.Add(new Tuple(i + "%", i / 100.0)); @@ -61,6 +66,8 @@ namespace Artemis.UI.Screens.Settings public List> TargetFrameRates { get; set; } public List> RenderScales { get; set; } + public IEnumerable LogLevels { get; private set; } + public List SampleSizes { get; set; } public BindableCollection DeviceSettingsViewModels { get; set; } public BindableCollection Plugins { get; set; } @@ -77,6 +84,16 @@ namespace Artemis.UI.Screens.Settings set => TargetFrameRate = value.Item2; } + public LogEventLevel SelectedLogLevel + { + get => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information).Value; + set + { + _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information).Value = value; + _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information).Save(); + } + } + public double RenderScale { get => _settingsService.GetSetting("Core.RenderScale", 1.0).Value; @@ -87,7 +104,6 @@ namespace Artemis.UI.Screens.Settings } } - public int TargetFrameRate { get => _settingsService.GetSetting("Core.TargetFrameRate", 25).Value; diff --git a/src/Artemis.UI/Utilities/HitTestUtilities.cs b/src/Artemis.UI/Utilities/HitTestUtilities.cs new file mode 100644 index 000000000..92a2ce499 --- /dev/null +++ b/src/Artemis.UI/Utilities/HitTestUtilities.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; + +namespace Artemis.UI.Utilities +{ + public static class HitTestUtilities + { + /// + /// Runs a hit test on children of the container within the rectangle matching all elements that have a data context of . + /// + /// + /// + /// + /// + public static List GetHitViewModels(Visual container, RectangleGeometry rectangleGeometry) + { + var result = new List(); + var hitTestParams = new GeometryHitTestParameters(rectangleGeometry); + var resultCallback = new HitTestResultCallback(r => HitTestResultBehavior.Continue); + var filterCallback = new HitTestFilterCallback(e => + { + if (e is FrameworkElement fe && fe.DataContext.GetType() == typeof(T) && !result.Contains((T) fe.DataContext)) + result.Add((T) fe.DataContext); + return HitTestFilterBehavior.Continue; + }); + VisualTreeHelper.HitTest(container, filterCallback, resultCallback, hitTestParams); + + return result; + } + } +} \ No newline at end of file