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

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
This commit is contained in:
Robert 2020-02-19 21:33:51 +01:00
parent 05cc032271
commit 73f7bdbf1e
26 changed files with 323 additions and 161 deletions

View File

@ -90,8 +90,11 @@
<Reference Include="Serilog.Enrichers.Demystify, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Serilog.Enrichers.Demystify, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Serilog.Enrichers.Demystify.1.0.0-dev-00019\lib\net45\Serilog.Enrichers.Demystify.dll</HintPath> <HintPath>..\packages\Serilog.Enrichers.Demystify.1.0.0-dev-00019\lib\net45\Serilog.Enrichers.Demystify.dll</HintPath>
</Reference> </Reference>
<Reference Include="Serilog.Sinks.Debug, Version=1.0.1.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>..\packages\Serilog.Sinks.Debug.1.0.1\lib\net46\Serilog.Sinks.Debug.dll</HintPath>
</Reference>
<Reference Include="Serilog.Sinks.File, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL"> <Reference Include="Serilog.Sinks.File, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>..\packages\Serilog.Sinks.File.4.0.0\lib\net45\Serilog.Sinks.File.dll</HintPath> <HintPath>..\packages\Serilog.Sinks.File.4.1.0\lib\net45\Serilog.Sinks.File.dll</HintPath>
</Reference> </Reference>
<Reference Include="SkiaSharp, Version=1.68.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> <Reference Include="SkiaSharp, Version=1.68.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\SkiaSharp.1.68.2-preview.29\lib\net45\SkiaSharp.dll</HintPath> <HintPath>..\packages\SkiaSharp.1.68.2-preview.29\lib\net45\SkiaSharp.dll</HintPath>

View File

@ -1,24 +1,30 @@
using Ninject.Activation; using Ninject.Activation;
using Serilog; using Serilog;
using Serilog.Core;
using Serilog.Events;
namespace Artemis.Core.Ninject namespace Artemis.Core.Ninject
{ {
internal class LoggerProvider : Provider<ILogger> internal class LoggerProvider : Provider<ILogger>
{ {
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.FromLogContext()
.Enrich.WithDemystifiedStackTraces() .Enrich.WithDemystifiedStackTraces()
.WriteTo.File("Logs/Artemis log-.txt", .WriteTo.File("Logs/Artemis log-.txt",
rollingInterval: RollingInterval.Day, 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(); .CreateLogger();
protected override ILogger CreateInstance(IContext context) protected override ILogger CreateInstance(IContext context)
{ {
var requestingType = context.Request.ParentContext?.Plan?.Type; var requestingType = context.Request.ParentContext?.Plan?.Type;
if (requestingType != null) if (requestingType != null)
return _logger.ForContext(requestingType); return Logger.ForContext(requestingType);
return _logger; return Logger;
} }
} }
} }

View File

@ -4,12 +4,15 @@ using System.Threading.Tasks;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.Core.Exceptions; using Artemis.Core.Exceptions;
using Artemis.Core.JsonConverters; using Artemis.Core.JsonConverters;
using Artemis.Core.Ninject;
using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage.Interfaces; using Artemis.Core.Services.Storage.Interfaces;
using Newtonsoft.Json; using Newtonsoft.Json;
using RGB.NET.Core; using RGB.NET.Core;
using Serilog; using Serilog;
using Serilog.Events;
using SkiaSharp; using SkiaSharp;
namespace Artemis.Core.Services namespace Artemis.Core.Services
@ -25,16 +28,21 @@ namespace Artemis.Core.Services
private readonly IRgbService _rgbService; private readonly IRgbService _rgbService;
private readonly ISurfaceService _surfaceService; private readonly ISurfaceService _surfaceService;
private List<Module> _modules; private List<Module> _modules;
private PluginSetting<LogEventLevel> _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; _logger = logger;
_pluginService = pluginService; _pluginService = pluginService;
_rgbService = rgbService; _rgbService = rgbService;
_surfaceService = surfaceService; _surfaceService = surfaceService;
_profileService = profileService; _profileService = profileService;
_loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
_rgbService.Surface.Updating += SurfaceOnUpdating; _rgbService.Surface.Updating += SurfaceOnUpdating;
_rgbService.Surface.Updated += SurfaceOnUpdated; _rgbService.Surface.Updated += SurfaceOnUpdated;
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
_modules = _pluginService.GetPluginsOfType<Module>(); _modules = _pluginService.GetPluginsOfType<Module>();
_pluginService.PluginEnabled += (sender, args) => _modules = _pluginService.GetPluginsOfType<Module>(); _pluginService.PluginEnabled += (sender, args) => _modules = _pluginService.GetPluginsOfType<Module>();
@ -79,6 +87,7 @@ namespace Artemis.Core.Services
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized."); throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
_logger.Information("Initializing Artemis Core version {version}", typeof(CoreService).Assembly.GetName().Version); _logger.Information("Initializing Artemis Core version {version}", typeof(CoreService).Assembly.GetName().Version);
ApplyLoggingLevel();
// Initialize the services // Initialize the services
await Task.Run(() => _pluginService.CopyBuiltInPlugins()); await Task.Run(() => _pluginService.CopyBuiltInPlugins());
@ -95,6 +104,12 @@ namespace Artemis.Core.Services
OnInitialized(); OnInitialized();
} }
private void ApplyLoggingLevel()
{
_logger.Information("Setting logging level to {loggingLevel}", _loggingLevel.Value);
LoggerProvider.LoggingLevelSwitch.MinimumLevel = _loggingLevel.Value;
}
private void SurfaceOnUpdating(UpdatingEventArgs args) private void SurfaceOnUpdating(UpdatingEventArgs args)
{ {
try try

View File

@ -10,6 +10,7 @@ using Artemis.Core.Services.Storage.Interfaces;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog;
namespace Artemis.Core.Services.Storage namespace Artemis.Core.Services.Storage
{ {
@ -19,12 +20,14 @@ namespace Artemis.Core.Services.Storage
public class ProfileService : IProfileService public class ProfileService : IProfileService
{ {
private readonly ILayerService _layerService; private readonly ILayerService _layerService;
private readonly ILogger _logger;
private readonly IPluginService _pluginService; private readonly IPluginService _pluginService;
private readonly IProfileRepository _profileRepository; private readonly IProfileRepository _profileRepository;
private readonly ISurfaceService _surfaceService; 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; _pluginService = pluginService;
_surfaceService = surfaceService; _surfaceService = surfaceService;
_layerService = layerService; _layerService = layerService;
@ -121,7 +124,10 @@ namespace Artemis.Core.Services.Storage
public void UndoUpdateProfile(Profile profile, ProfileModule module) public void UndoUpdateProfile(Profile profile, ProfileModule module)
{ {
if (!profile.UndoStack.Any()) if (!profile.UndoStack.Any())
{
_logger.Debug("Undo profile update - Failed, undo stack empty");
return; return;
}
ActivateProfile(module, null); ActivateProfile(module, null);
var top = profile.UndoStack.Pop(); var top = profile.UndoStack.Pop();
@ -130,12 +136,17 @@ namespace Artemis.Core.Services.Storage
profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top); profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top);
profile.ApplyToProfile(); profile.ApplyToProfile();
ActivateProfile(module, profile); ActivateProfile(module, profile);
_logger.Debug("Undo profile update - Success");
} }
public void RedoUpdateProfile(Profile profile, ProfileModule module) public void RedoUpdateProfile(Profile profile, ProfileModule module)
{ {
if (!profile.RedoStack.Any()) if (!profile.RedoStack.Any())
{
_logger.Debug("Redo profile update - Failed, redo empty");
return; return;
}
ActivateProfile(module, null); ActivateProfile(module, null);
var top = profile.RedoStack.Pop(); var top = profile.RedoStack.Pop();
@ -144,6 +155,8 @@ namespace Artemis.Core.Services.Storage
profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top); profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top);
profile.ApplyToProfile(); profile.ApplyToProfile();
ActivateProfile(module, profile); ActivateProfile(module, profile);
_logger.Debug("Redo profile update - Success");
} }
private void InstantiateProfileLayerBrushes(Profile profile) private void InstantiateProfileLayerBrushes(Profile profile)

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="AppDomainToolkit" version="1.0.4.3" targetFramework="net461" /> <package id="AppDomainToolkit" version="1.0.4.3" targetFramework="net461" />
<package id="Ben.Demystifier" version="0.1.4" targetFramework="net472" /> <package id="Ben.Demystifier" version="0.1.4" targetFramework="net472" />
@ -14,11 +13,13 @@
<package id="PropertyChanged.Fody" version="3.1.3" targetFramework="net472" /> <package id="PropertyChanged.Fody" version="3.1.3" targetFramework="net472" />
<package id="Serilog" version="2.8.0" targetFramework="net472" /> <package id="Serilog" version="2.8.0" targetFramework="net472" />
<package id="Serilog.Enrichers.Demystify" version="1.0.0-dev-00019" targetFramework="net472" /> <package id="Serilog.Enrichers.Demystify" version="1.0.0-dev-00019" targetFramework="net472" />
<package id="Serilog.Sinks.File" version="4.0.0" targetFramework="net472" /> <package id="Serilog.Sinks.Debug" version="1.0.1" targetFramework="net472" />
<package id="Serilog.Sinks.File" version="4.1.0" targetFramework="net472" />
<package id="SkiaSharp" version="1.68.2-preview.29" targetFramework="net472" /> <package id="SkiaSharp" version="1.68.2-preview.29" targetFramework="net472" />
<package id="Stylet" version="1.3.1" targetFramework="net472" /> <package id="Stylet" version="1.3.1" targetFramework="net472" />
<package id="System.Buffers" version="4.5.0" targetFramework="net472" /> <package id="System.Buffers" version="4.5.0" targetFramework="net472" />
<package id="System.Collections.Immutable" version="1.6.0-preview8.19405.3" targetFramework="net472" /> <package id="System.Collections.Immutable" version="1.6.0-preview8.19405.3" targetFramework="net472" />
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net472" />
<package id="System.Diagnostics.DiagnosticSource" version="4.5.1" targetFramework="net472" /> <package id="System.Diagnostics.DiagnosticSource" version="4.5.1" targetFramework="net472" />
<package id="System.Memory" version="4.5.3" targetFramework="net472" /> <package id="System.Memory" version="4.5.3" targetFramework="net472" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" /> <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />

View File

@ -91,20 +91,12 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="UserControl1.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="ColorPicker.xaml.cs"> <Compile Include="ColorPicker.xaml.cs">
<DependentUpon>ColorPicker.xaml</DependentUpon> <DependentUpon>ColorPicker.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Converters\ColorToSKColorConverter.cs" /> <Compile Include="Converters\ColorToSKColorConverter.cs" />
<Compile Include="Converters\ColorToSolidColorConverter.cs" /> <Compile Include="Converters\ColorToSolidColorConverter.cs" />
<Compile Include="Converters\ColorToStringConverter.cs" /> <Compile Include="Converters\ColorToStringConverter.cs" />
<Compile Include="UserControl1.xaml.cs">
<DependentUpon>UserControl1.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">

View File

@ -22,12 +22,13 @@
Height="17" Height="17"
Padding="1 0" Padding="1 0"
Margin="0 4 0 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" Cursor="/Resources/aero_drag_ew.cur"
Foreground="{DynamicResource SecondaryAccentBrush}" Foreground="{DynamicResource SecondaryAccentBrush}"
MouseDown="InputMouseDown" MouseDown="InputMouseDown"
MouseUp="InputMouseUp" MouseUp="InputMouseUp"
MouseMove="InputMouseMove" /> MouseMove="InputMouseMove"
RequestBringIntoView="Input_OnRequestBringIntoView"/>
</Border> </Border>
<!-- Input --> <!-- Input -->
@ -36,7 +37,7 @@
Height="20" Height="20"
materialDesign:ValidationAssist.UsePopup="True" materialDesign:ValidationAssist.UsePopup="True"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Text="{Binding Value, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Text="{Binding Value, StringFormat=N3, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
LostFocus="InputLostFocus" LostFocus="InputLostFocus"
KeyDown="InputKeyDown" KeyDown="InputKeyDown"
RequestBringIntoView="Input_OnRequestBringIntoView" /> RequestBringIntoView="Input_OnRequestBringIntoView" />

View File

@ -24,9 +24,13 @@ namespace Artemis.UI.Shared
typeof(RoutedPropertyChangedEventHandler<float>), typeof(RoutedPropertyChangedEventHandler<float>),
typeof(DraggableFloat)); typeof(DraggableFloat));
public event EventHandler DragStarted;
public event EventHandler DragEnded;
private bool _inCallback; private bool _inCallback;
private Point _mouseDragStartPoint; private Point _mouseDragStartPoint;
private float _startValue; private float _startValue;
private bool _calledDragStarted;
public DraggableFloat() public DraggableFloat()
{ {
@ -75,22 +79,33 @@ namespace Artemis.UI.Shared
var position = e.GetPosition((IInputElement) sender); var position = e.GetPosition((IInputElement) sender);
if (position == _mouseDragStartPoint) if (position == _mouseDragStartPoint)
DisplayInput(); DisplayInput();
else
{
OnDragEnded();
_calledDragStarted = false;
}
((IInputElement) sender).ReleaseMouseCapture(); ((IInputElement) sender).ReleaseMouseCapture();
} }
private void InputMouseMove(object sender, MouseEventArgs e) 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 OnDragStarted();
var startValue = new decimal(_startValue); _calledDragStarted = true;
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);
} }
// 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) private void InputLostFocus(object sender, RoutedEventArgs e)
@ -127,5 +142,20 @@ namespace Artemis.UI.Shared
{ {
e.Handled = true; 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);
}
} }
} }

View File

@ -1,10 +0,0 @@
<UserControl x:Class="Artemis.UI.Shared.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid />
</UserControl>

View File

@ -1,15 +0,0 @@
using System.Windows.Controls;
namespace Artemis.UI.Shared
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
}
}

View File

@ -227,6 +227,7 @@
<Compile Include="Services\Interfaces\IProfileEditorService.cs" /> <Compile Include="Services\Interfaces\IProfileEditorService.cs" />
<Compile Include="Services\LayerEditorService.cs" /> <Compile Include="Services\LayerEditorService.cs" />
<Compile Include="Services\ProfileEditorService.cs" /> <Compile Include="Services\ProfileEditorService.cs" />
<Compile Include="Utilities\HitTestUtilities.cs" />
<Compile Include="Utilities\ThemeWatcher.cs" /> <Compile Include="Utilities\ThemeWatcher.cs" />
<Compile Include="Behaviors\TreeViewSelectionBehavior.cs" /> <Compile Include="Behaviors\TreeViewSelectionBehavior.cs" />
<Compile Include="Utilities\TriggerTracing.cs" /> <Compile Include="Utilities\TriggerTracing.cs" />

View File

@ -3,9 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@ -19,8 +17,7 @@
ItemsSource="{Binding Path=EnumValues}" ItemsSource="{Binding Path=EnumValues}"
SelectedValuePath="Value" SelectedValuePath="Value"
DisplayMemberPath="Description" DisplayMemberPath="Description"
SelectedValue="{Binding Path=BrushInputValue}" SelectedValue="{Binding Path=BrushInputValue}" />
materialDesign:ComboBoxAssist.ClassicMode="True" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" /> <TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -5,7 +5,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:EnumPropertyInputViewModel}"> d:DataContext="{d:DesignInstance local:EnumPropertyInputViewModel}">
@ -20,8 +19,7 @@
ItemsSource="{Binding Path=EnumValues}" ItemsSource="{Binding Path=EnumValues}"
SelectedValuePath="Value" SelectedValuePath="Value"
DisplayMemberPath="Description" DisplayMemberPath="Description"
SelectedValue="{Binding Path=EnumInputValue}" SelectedValue="{Binding Path=EnumInputValue}" />
materialDesign:ComboBoxAssist.ClassicMode="True" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" /> <TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -11,7 +11,10 @@
d:DataContext="{d:DesignInstance local:FloatPropertyInputViewModel}"> d:DataContext="{d:DesignInstance local:FloatPropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" /> <TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<shared:DraggableFloat Value="{Binding FloatInputValue}" StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" /> <shared:DraggableFloat Value="{Binding FloatInputValue}"
StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" /> <TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -4,7 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" 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:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"
@ -12,7 +11,10 @@
d:DataContext="{d:DesignInstance local:IntPropertyInputViewModel}"> d:DataContext="{d:DesignInstance local:IntPropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" /> <TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<shared:DraggableFloat Value="{Binding IntInputValue}" StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" /> <shared:DraggableFloat Value="{Binding IntInputValue}"
StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" /> <TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -13,10 +13,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P
ProfileEditorService = profileEditorService; ProfileEditorService = profileEditorService;
} }
protected IProfileEditorService ProfileEditorService { get; set; } protected IProfileEditorService ProfileEditorService { get; }
public abstract List<Type> CompatibleTypes { get; } public abstract List<Type> CompatibleTypes { get; }
public bool Initialized { get; private set; } public bool Initialized { get; private set; }
public bool InputDragging { get; private set; }
public LayerPropertyViewModel LayerPropertyViewModel { get; private set; } public LayerPropertyViewModel LayerPropertyViewModel { get; private set; }
protected object InputValue 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 // Force the keyframe engine to update, the edited keyframe might affect the current keyframe progress
LayerPropertyViewModel.LayerProperty.KeyframeEngine?.Update(0); 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(); ProfileEditorService.UpdateSelectedProfileElement();
} }
#endregion
} }
} }

View File

@ -4,8 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" 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:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared" xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"

View File

@ -4,7 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" 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:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"
@ -14,11 +13,15 @@
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" /> <TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<shared:DraggableFloat ToolTip="X-coordinate (horizontal)" <shared:DraggableFloat ToolTip="X-coordinate (horizontal)"
Value="{Binding X}" Value="{Binding X}"
StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" /> StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" />
<TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock> <TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock>
<shared:DraggableFloat ToolTip="Y-coordinate (vertical)" <shared:DraggableFloat ToolTip="Y-coordinate (vertical)"
Value="{Binding Y}" Value="{Binding Y}"
StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" /> StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" /> <TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -4,7 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput" 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:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" mc:Ignorable="d"
@ -12,9 +11,17 @@
d:DataContext="{d:DesignInstance local:SKSizePropertyInputViewModel}"> d:DataContext="{d:DesignInstance local:SKSizePropertyInputViewModel}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" /> <TextBlock Margin="0 0 5 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputPrefix}" />
<shared:DraggableFloat ToolTip="Height" Value="{Binding Height}" StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" /> <shared:DraggableFloat ToolTip="Height"
Value="{Binding Height}"
StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" />
<TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock> <TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock>
<shared:DraggableFloat ToolTip="Width" Value="{Binding Width}" StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}" /> <shared:DraggableFloat ToolTip="Width"
Value="{Binding Width}"
StepSize="{Binding LayerPropertyViewModel.LayerProperty.InputStepSize}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}" />
<TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" /> <TextBlock Margin="5 0 0 4" Width="10" VerticalAlignment="Bottom" Text="{Binding LayerPropertyViewModel.LayerProperty.InputAffix}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -9,11 +9,11 @@
d:DesignHeight="25" d:DesignHeight="25"
d:DesignWidth="800" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:PropertyTimelineViewModel}"> d:DataContext="{d:DesignInstance local:PropertyTimelineViewModel}">
<Canvas Background="{DynamicResource MaterialDesignToolBarBackground}" <Grid Background="{DynamicResource MaterialDesignToolBarBackground}"
MouseDown="{s:Action TimelineCanvasMouseDown}" MouseDown="{s:Action TimelineCanvasMouseDown}"
MouseUp="{s:Action TimelineCanvasMouseUp}" MouseUp="{s:Action TimelineCanvasMouseUp}"
MouseMove="{s:Action TimelineCanvasMouseMove}"> MouseMove="{s:Action TimelineCanvasMouseMove}">
<Canvas.Triggers> <Grid.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseLeftButtonDown"> <EventTrigger RoutedEvent="UIElement.MouseLeftButtonDown">
<BeginStoryboard> <BeginStoryboard>
<Storyboard Storyboard.TargetName="MultiSelectionPath" Storyboard.TargetProperty="Opacity"> <Storyboard Storyboard.TargetName="MultiSelectionPath" Storyboard.TargetProperty="Opacity">
@ -28,7 +28,7 @@
</Storyboard> </Storyboard>
</BeginStoryboard> </BeginStoryboard>
</EventTrigger> </EventTrigger>
</Canvas.Triggers> </Grid.Triggers>
<ItemsControl ItemsSource="{Binding PropertyTrackViewModels}" <ItemsControl ItemsSource="{Binding PropertyTrackViewModels}"
Width="{Binding Width}" Width="{Binding Width}"
@ -51,5 +51,5 @@
<SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" /> <SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" />
</Path.Fill> </Path.Fill>
</Path> </Path>
</Canvas> </Grid>
</UserControl> </UserControl>

View File

@ -7,6 +7,7 @@ using System.Windows.Media;
using System.Windows.Shapes; using System.Windows.Shapes;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Utilities;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
@ -95,26 +96,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
UpdateEndTime(); 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) private void CreateViewModels(LayerPropertyViewModel property)
{ {
PropertyTrackViewModels.Add(_propertyTrackVmFactory.Create(this, property)); PropertyTrackViewModels.Add(_propertyTrackVmFactory.Create(this, property));
@ -122,6 +103,30 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
CreateViewModels(child); 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 #region Keyframe selection
private Point _mouseDragStartPoint; private Point _mouseDragStartPoint;
@ -130,6 +135,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
// ReSharper disable once UnusedMember.Global - Called from view // ReSharper disable once UnusedMember.Global - Called from view
public void TimelineCanvasMouseDown(object sender, MouseButtonEventArgs e) public void TimelineCanvasMouseDown(object sender, MouseButtonEventArgs e)
{ {
if (e.LeftButton == MouseButtonState.Released)
return;
((IInputElement) sender).CaptureMouse(); ((IInputElement) sender).CaptureMouse();
SelectionRectangle.Rect = new Rect(); SelectionRectangle.Rect = new Rect();
@ -148,18 +156,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
var selectedRect = new Rect(_mouseDragStartPoint, position); var selectedRect = new Rect(_mouseDragStartPoint, position);
SelectionRectangle.Rect = selectedRect; SelectionRectangle.Rect = selectedRect;
// Find all keyframes in the rectangle var selectedKeyframes = HitTestUtilities.GetHitViewModels<PropertyTrackKeyframeViewModel>((Visual) sender, SelectionRectangle);
var selectedKeyframes = new List<PropertyTrackKeyframeViewModel>();
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 keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList(); var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList();
foreach (var keyframeViewModel in keyframeViewModels) foreach (var keyframeViewModel in keyframeViewModels)
keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel); keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel);

View File

@ -49,7 +49,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
if (e.LeftButton == MouseButtonState.Released) if (e.LeftButton == MouseButtonState.Released)
return; return;
((IInputElement)sender).CaptureMouse(); ((IInputElement) sender).CaptureMouse();
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && !IsSelected) if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && !IsSelected)
PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, true, false); PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, true, false);
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) 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) public void KeyframeMouseUp(object sender, MouseButtonEventArgs e)
{ {
_profileEditorService.UpdateSelectedProfileElement(); _profileEditorService.UpdateSelectedProfileElement();
PropertyTrackViewModel.PropertyTimelineViewModel.ReleaseSelectedKeyframes();
((IInputElement)sender).ReleaseMouseCapture(); ((IInputElement) sender).ReleaseMouseCapture();
e.Handled = true;
} }
public void KeyframeMouseMove(object sender, MouseEventArgs e) public void KeyframeMouseMove(object sender, MouseEventArgs e)
{ {
if (e.LeftButton == MouseButtonState.Pressed) if (e.LeftButton == MouseButtonState.Pressed)
{ PropertyTrackViewModel.PropertyTimelineViewModel.MoveSelectedKeyframes(GetCursorTime(e.GetPosition(ParentView)));
// 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);
}
e.Handled = true; 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 #endregion
#region Context menu actions #region Context menu actions
@ -137,5 +140,34 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
} }
#endregion #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
} }
} }

View File

@ -318,8 +318,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
// Scale down the resulting position and make it relative // Scale down the resulting position and make it relative
var scaled = _layerEditorService.GetScaledPoint(layer, position, true); var scaled = _layerEditorService.GetScaledPoint(layer, position, true);
// Update the position property // Round and update the position property
layer.PositionProperty.SetCurrentValue(scaled, ProfileEditorService.CurrentTime); layer.PositionProperty.SetCurrentValue(RoundPoint(scaled, 3), ProfileEditorService.CurrentTime);
ProfileEditorService.UpdateProfilePreview(); ProfileEditorService.UpdateProfilePreview();
} }
@ -338,13 +338,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
var scaled = _layerEditorService.GetScaledPoint(layer, countered[1], false); var scaled = _layerEditorService.GetScaledPoint(layer, countered[1], false);
// Update the anchor point, this causes the shape to move // 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 // 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); var path = _layerEditorService.GetLayerPath(layer, true, true, true);
// Calculate the (scaled) difference between the old and now position // Calculate the (scaled) difference between the old and now position
var difference = _layerEditorService.GetScaledPoint(layer, _topLeft - path.Points[0], false); var difference = _layerEditorService.GetScaledPoint(layer, _topLeft - path.Points[0], false);
// Apply the difference so that the shape effectively stays in place // 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(); ProfileEditorService.UpdateProfilePreview();
} }

View File

@ -36,7 +36,7 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"> <StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">Start up with Windows</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">Start up with Windows</TextBlock>
</StackPanel> </StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"> <StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" ToolTip="Default ToggleButton Style" /> <ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" ToolTip="Default ToggleButton Style" />
@ -54,7 +54,7 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"> <StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">Start up with Windows minimized</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">Start up with Windows minimized</TextBlock>
</StackPanel> </StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"> <StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" ToolTip="Default ToggleButton Style" /> <ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" ToolTip="Default ToggleButton Style" />
@ -72,8 +72,8 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center"> <StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">Debugger</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">Debugger</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"> <TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
Use the debugger to see the raw image Artemis is rendering on the surface. Use the debugger to see the raw image Artemis is rendering on the surface.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
@ -95,18 +95,17 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center"> <StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">Logs</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">Application files</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"> <TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
Opens the directory where logs are stored. Opens the directory where application files like plugins and settings are stored.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"> <StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowLogsFolder}" Width="150"> <Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowDataFolder}" Width="150">
SHOW LOGS SHOW APP FILES
</Button> </Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" /> <Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid> <Grid>
@ -119,17 +118,39 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center"> <StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">Application files</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">Logs</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"> <TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
Opens the directory where application files like plugins and settings are stored. Opens the directory where logs are stored.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"> <StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowDataFolder}" Width="150"> <Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowLogsFolder}" Width="150">
SHOW APP FILES SHOW LOGS
</Button> </Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Log level</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
Sets the logging level, a verbose logging level will result in more log files.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ComboBox Width="80" SelectedValue="{Binding SelectedLogLevel}" ItemsSource="{Binding LogLevels}" SelectedValuePath="Value" DisplayMemberPath="Description" />
</StackPanel>
</Grid>
</StackPanel> </StackPanel>
</materialDesign:Card> </materialDesign:Card>
@ -147,8 +168,8 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"> <StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">Render scale</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">Render scale</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap"> <TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
Sets the resolution Artemis renders at, higher scale means more CPU-usage, especially on large surfaces. Sets the resolution Artemis renders at, higher scale means more CPU-usage, especially on large surfaces.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
@ -168,8 +189,8 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"> <StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">Target framerate</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">Target framerate</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap"> <TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
Sets the FPS Artemis tries to render at, higher FPS means more CPU-usage but smoother animations. Sets the FPS Artemis tries to render at, higher FPS means more CPU-usage but smoother animations.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
@ -189,8 +210,8 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"> <StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">LED sample size</TextBlock> <TextBlock Style="{StaticResource MaterialDesignTextBlock}">LED sample size</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap"> <TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
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. 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.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>

View File

@ -12,8 +12,10 @@ using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Settings.Debug; using Artemis.UI.Screens.Settings.Debug;
using Artemis.UI.Screens.Settings.Tabs.Devices; using Artemis.UI.Screens.Settings.Tabs.Devices;
using Artemis.UI.Screens.Settings.Tabs.Plugins; using Artemis.UI.Screens.Settings.Tabs.Plugins;
using Artemis.UI.Shared.Utilities;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using Ninject; using Ninject;
using Serilog.Events;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.Settings namespace Artemis.UI.Screens.Settings
@ -26,6 +28,7 @@ namespace Artemis.UI.Screens.Settings
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly ISurfaceService _surfaceService; private readonly ISurfaceService _surfaceService;
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
private object _test;
public SettingsViewModel(IKernel kernel, public SettingsViewModel(IKernel kernel,
ISurfaceService surfaceService, ISurfaceService surfaceService,
@ -47,6 +50,8 @@ namespace Artemis.UI.Screens.Settings
DeviceSettingsViewModels = new BindableCollection<DeviceSettingsViewModel>(); DeviceSettingsViewModels = new BindableCollection<DeviceSettingsViewModel>();
Plugins = new BindableCollection<PluginSettingsViewModel>(); Plugins = new BindableCollection<PluginSettingsViewModel>();
LogLevels = EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel));
RenderScales = new List<Tuple<string, double>> {new Tuple<string, double>("10%", 0.1)}; RenderScales = new List<Tuple<string, double>> {new Tuple<string, double>("10%", 0.1)};
for (var i = 25; i <= 100; i += 25) for (var i = 25; i <= 100; i += 25)
RenderScales.Add(new Tuple<string, double>(i + "%", i / 100.0)); RenderScales.Add(new Tuple<string, double>(i + "%", i / 100.0));
@ -61,6 +66,8 @@ namespace Artemis.UI.Screens.Settings
public List<Tuple<string, int>> TargetFrameRates { get; set; } public List<Tuple<string, int>> TargetFrameRates { get; set; }
public List<Tuple<string, double>> RenderScales { get; set; } public List<Tuple<string, double>> RenderScales { get; set; }
public IEnumerable<ValueDescription> LogLevels { get; private set; }
public List<int> SampleSizes { get; set; } public List<int> SampleSizes { get; set; }
public BindableCollection<DeviceSettingsViewModel> DeviceSettingsViewModels { get; set; } public BindableCollection<DeviceSettingsViewModel> DeviceSettingsViewModels { get; set; }
public BindableCollection<PluginSettingsViewModel> Plugins { get; set; } public BindableCollection<PluginSettingsViewModel> Plugins { get; set; }
@ -77,6 +84,16 @@ namespace Artemis.UI.Screens.Settings
set => TargetFrameRate = value.Item2; 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 public double RenderScale
{ {
get => _settingsService.GetSetting("Core.RenderScale", 1.0).Value; get => _settingsService.GetSetting("Core.RenderScale", 1.0).Value;
@ -87,7 +104,6 @@ namespace Artemis.UI.Screens.Settings
} }
} }
public int TargetFrameRate public int TargetFrameRate
{ {
get => _settingsService.GetSetting("Core.TargetFrameRate", 25).Value; get => _settingsService.GetSetting("Core.TargetFrameRate", 25).Value;

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace Artemis.UI.Utilities
{
public static class HitTestUtilities
{
/// <summary>
/// Runs a hit test on children of the container within the rectangle matching all elements that have a data context of <see cref="T"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="container"></param>
/// <param name="rectangleGeometry"></param>
/// <returns></returns>
public static List<T> GetHitViewModels<T>(Visual container, RectangleGeometry rectangleGeometry)
{
var result = new List<T>();
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;
}
}
}