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

Profile editor - Visualization fixes

Profile editor - Timeline fixes
This commit is contained in:
Robert 2022-02-06 00:00:48 +01:00
parent 0b42e7f510
commit c36110e79d
22 changed files with 331 additions and 230 deletions

View File

@ -74,47 +74,10 @@
"System.Xml.XmlDocument": "4.3.0" "System.Xml.XmlDocument": "4.3.0"
} }
}, },
"EmbedIO": {
"type": "Transitive",
"resolved": "3.4.3",
"contentHash": "YM6hpZNAfvbbixfG9T4lWDGfF0D/TqutbTROL4ogVcHKwPF1hp+xS3ABwd3cxxTxvDFkj/zZl57QgWuFA8Igxw==",
"dependencies": {
"Unosquare.Swan.Lite": "3.0.0"
}
},
"HidSharp": {
"type": "Transitive",
"resolved": "2.1.0",
"contentHash": "UTdxWvbgp2xzT1Ajaa2va+Qi3oNHJPasYmVhbKI2VVdu1VYP6yUG+RikhsHvpD7iM0S8e8UYb5Qm/LTWxx9QAA=="
},
"LiteDB": {
"type": "Transitive",
"resolved": "5.0.11",
"contentHash": "6cL4bOmVCUB0gIK+6qIr68HeqjjHZicPDGQjvJ87mIOvkFsEsJWkIps3yoKNeLpHhJQur++yoQ9Q8gxsdos0xQ=="
},
"McMaster.NETCore.Plugins": {
"type": "Transitive",
"resolved": "1.4.0",
"contentHash": "UKw5Z2/QHhkR7kiAJmqdCwVDMQV0lwsfj10+FG676r8DsJWIpxtachtEjE0qBs9WoK5GUQIqxgyFeYUSwuPszg==",
"dependencies": {
"Microsoft.DotNet.PlatformAbstractions": "3.1.6",
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
},
"Microsoft.DotNet.PlatformAbstractions": {
"type": "Transitive",
"resolved": "3.1.6",
"contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "umBECCoMC+sOUgm083yFr8SxTobUOcPFH4AXigdO2xJiszCHAnmeDl4qPphJt+oaJ/XIfV1wOjIts2nRnki61Q=="
},
"Microsoft.NETCore.Platforms": { "Microsoft.NETCore.Platforms": {
"type": "Transitive", "type": "Transitive",
"resolved": "5.0.0", "resolved": "1.1.0",
"contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
}, },
"Microsoft.NETCore.Targets": { "Microsoft.NETCore.Targets": {
"type": "Transitive", "type": "Transitive",
@ -182,19 +145,6 @@
"System.Xml.XDocument": "4.3.0" "System.Xml.XDocument": "4.3.0"
} }
}, },
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.1",
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
},
"Ninject.Extensions.ChildKernel": {
"type": "Transitive",
"resolved": "3.3.0",
"contentHash": "vl/p3f8sIaCCHiKsjhq9R8n3bH705Hu1WJXNpMEz1UC79EV51Mk5TWYXQbRnsK20hxF48CiAgUBb9pMKfX6sLw==",
"dependencies": {
"Ninject": "3.3.4"
}
},
"Ninject.Extensions.Factory": { "Ninject.Extensions.Factory": {
"type": "Transitive", "type": "Transitive",
"resolved": "3.3.2", "resolved": "3.3.2",
@ -204,27 +154,6 @@
"Ninject": "3.3.3" "Ninject": "3.3.3"
} }
}, },
"RGB.NET.Core": {
"type": "Transitive",
"resolved": "1.0.0-prerelease7",
"contentHash": "IIja5sC4QZ5pbSNckRCG7TlY4U6j/dRbrl4e2FZqsTGgsevaVB3IqonUQLFY1GGst4xNSl2oh0A23coXQxXGbQ=="
},
"RGB.NET.Layout": {
"type": "Transitive",
"resolved": "1.0.0-prerelease7",
"contentHash": "S0kfWVa8EfMOAl2WPHsq98dwaO+SNz9TWr1AtMkdo8aZuYIVhaJ1c+mSAMMnH1V+mSbxDWPHWkNzi9ITszJucA==",
"dependencies": {
"RGB.NET.Core": "1.0.0-prerelease7"
}
},
"RGB.NET.Presets": {
"type": "Transitive",
"resolved": "1.0.0-prerelease7",
"contentHash": "NgShvOPQM0miOsdqMKjkNunngJUZMwr8KR8ME2/Ksir7wgIQfgJj1YwZy8aIj+ar7fDo6VZJZenAshs/Ul+04A==",
"dependencies": {
"RGB.NET.Core": "1.0.0-prerelease7"
}
},
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -332,38 +261,6 @@
"resolved": "4.3.0", "resolved": "4.3.0",
"contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg=="
}, },
"Serilog.Sinks.Console": {
"type": "Transitive",
"resolved": "4.0.0",
"contentHash": "yJQit9sTJ4xGLKgCujqDJsaGqBNJwGB/H898z+xYlMG06twy4//6LLnSrsmpduZxcHIG4im7cv+JmXLzXz2EkQ==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.Debug": {
"type": "Transitive",
"resolved": "2.0.0",
"contentHash": "Y6g3OBJ4JzTyyw16fDqtFcQ41qQAydnEvEqmXjhwhgjsnG/FaJ8GUqF5ldsC/bVkK8KYmqrPhDO+tm4dF6xx4A==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.File": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"SkiaSharp": {
"type": "Transitive",
"resolved": "2.80.3",
"contentHash": "qX6tGNP3+MXNYe2pKm0PCRiJ/cx+LTeLaggwZifB7sUMXhECfKKKHJq45VqZKt37xQegnCCdf1jHXwmHeJQs5Q==",
"dependencies": {
"System.Memory": "4.5.3"
}
},
"System.AppContext": { "System.AppContext": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -656,15 +553,6 @@
"System.Threading.Tasks": "4.3.0" "System.Threading.Tasks": "4.3.0"
} }
}, },
"System.IO.FileSystem.AccessControl": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "SxHB3nuNrpptVk+vZ/F+7OHEpoHUIKKMl02bUmYHQr1r+glbZQxs7pRtsf4ENO29TVm2TH3AEeep2fJcy92oYw==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.IO.FileSystem.Primitives": { "System.IO.FileSystem.Primitives": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -709,11 +597,6 @@
"System.Threading": "4.3.0" "System.Threading": "4.3.0"
} }
}, },
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Net.Http": { "System.Net.Http": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -839,11 +722,6 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.Reflection.Metadata": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ=="
},
"System.Reflection.Primitives": { "System.Reflection.Primitives": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -942,15 +820,6 @@
"System.Runtime.Extensions": "4.3.0" "System.Runtime.Extensions": "4.3.0"
} }
}, },
"System.Security.AccessControl": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
"dependencies": {
"Microsoft.NETCore.Platforms": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Security.Cryptography.Algorithms": { "System.Security.Cryptography.Algorithms": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1095,11 +964,6 @@
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
} }
}, },
"System.Security.Principal.Windows": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
},
"System.Text.Encoding": { "System.Text.Encoding": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",
@ -1226,45 +1090,8 @@
"System.Xml.ReaderWriter": "4.3.0" "System.Xml.ReaderWriter": "4.3.0"
} }
}, },
"Unosquare.Swan.Lite": {
"type": "Transitive",
"resolved": "3.0.0",
"contentHash": "noPwJJl1Q9uparXy1ogtkmyAPGNfSGb0BLT1292nFH1jdMKje6o2kvvrQUvF9Xklj+IoiAI0UzF6Aqxlvo10lw=="
},
"artemis.core": { "artemis.core": {
"type": "Project", "type": "Project"
"dependencies": {
"Artemis.Storage": "1.0.0",
"EmbedIO": "3.4.3",
"HidSharp": "2.1.0",
"Humanizer.Core": "2.11.10",
"LiteDB": "5.0.11",
"McMaster.NETCore.Plugins": "1.4.0",
"Newtonsoft.Json": "13.0.1",
"Ninject": "3.3.4",
"Ninject.Extensions.ChildKernel": "3.3.0",
"Ninject.Extensions.Conventions": "3.3.0",
"RGB.NET.Core": "1.0.0-prerelease7",
"RGB.NET.Layout": "1.0.0-prerelease7",
"RGB.NET.Presets": "1.0.0-prerelease7",
"Serilog": "2.10.0",
"Serilog.Sinks.Console": "4.0.0",
"Serilog.Sinks.Debug": "2.0.0",
"Serilog.Sinks.File": "5.0.0",
"SkiaSharp": "2.80.3",
"System.Buffers": "4.5.1",
"System.IO.FileSystem.AccessControl": "5.0.0",
"System.Numerics.Vectors": "4.5.0",
"System.Reflection.Metadata": "5.0.0",
"System.ValueTuple": "4.5.0"
}
},
"artemis.storage": {
"type": "Project",
"dependencies": {
"LiteDB": "5.0.11",
"Serilog": "2.10.0"
}
} }
} }
} }

View File

@ -94,6 +94,12 @@ namespace Artemis.Core
if (Disposed) if (Disposed)
throw new ObjectDisposedException("Folder"); throw new ObjectDisposedException("Folder");
if (Timeline.IsOverridden)
{
Timeline.ClearOverride();
return;
}
UpdateDisplayCondition(); UpdateDisplayCondition();
UpdateTimeline(deltaTime); UpdateTimeline(deltaTime);

View File

@ -338,6 +338,12 @@ namespace Artemis.Core
if (Disposed) if (Disposed)
throw new ObjectDisposedException("Layer"); throw new ObjectDisposedException("Layer");
if (Timeline.IsOverridden)
{
Timeline.ClearOverride();
return;
}
UpdateDisplayCondition(); UpdateDisplayCondition();
UpdateTimeline(deltaTime); UpdateTimeline(deltaTime);

View File

@ -46,6 +46,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
private TimeSpan _startSegmentLength; private TimeSpan _startSegmentLength;
private TimeSpan _mainSegmentLength; private TimeSpan _mainSegmentLength;
private TimeSpan _endSegmentLength; private TimeSpan _endSegmentLength;
private TimeSpan _lastOverride;
/// <summary> /// <summary>
/// Gets the current position of the timeline /// Gets the current position of the timeline
@ -251,8 +252,6 @@ public class Timeline : CorePropertyChanged, IStorageModel
#region Updating #region Updating
private TimeSpan _lastOverridePosition;
/// <summary> /// <summary>
/// Updates the timeline, applying the provided <paramref name="delta" /> to the <see cref="Position" /> /// Updates the timeline, applying the provided <paramref name="delta" /> to the <see cref="Position" />
/// </summary> /// </summary>
@ -262,12 +261,12 @@ public class Timeline : CorePropertyChanged, IStorageModel
{ {
lock (_lock) lock (_lock)
{ {
if (IsOverridden)
throw new ArtemisCoreException("Can't update an overridden timeline, call ClearOverride first.");
Delta += delta; Delta += delta;
Position += delta; Position += delta;
IsOverridden = false;
_lastOverridePosition = Position;
if (!stickToMainSegment || Position <= MainSegmentEndPosition) if (!stickToMainSegment || Position <= MainSegmentEndPosition)
return; return;
@ -334,16 +333,19 @@ public class Timeline : CorePropertyChanged, IStorageModel
{ {
lock (_lock) lock (_lock)
{ {
Delta += position - _lastOverridePosition; if (_lastOverride == TimeSpan.Zero)
Position = position; Delta = Position - position;
else
Delta = position - _lastOverride;
Position = position;
IsOverridden = true; IsOverridden = true;
_lastOverridePosition = position; _lastOverride = position;
if (!stickToMainSegment || Position < MainSegmentStartPosition) if (!stickToMainSegment || Position < MainSegmentStartPosition)
return; return;
bool atSegmentStart = Position == MainSegmentStartPosition; bool atSegmentStart = Position >= MainSegmentStartPosition;
if (MainSegmentLength > TimeSpan.Zero) if (MainSegmentLength > TimeSpan.Zero)
{ {
Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(Position.TotalMilliseconds % MainSegmentLength.TotalMilliseconds); Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(Position.TotalMilliseconds % MainSegmentLength.TotalMilliseconds);
@ -359,6 +361,12 @@ public class Timeline : CorePropertyChanged, IStorageModel
} }
} }
internal void ClearOverride()
{
IsOverridden = false;
_lastOverride = TimeSpan.Zero;
}
/// <summary> /// <summary>
/// Sets the <see cref="Delta" /> to <see cref="TimeSpan.Zero" /> /// Sets the <see cref="Delta" /> to <see cref="TimeSpan.Zero" />
/// </summary> /// </summary>

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using SkiaSharp; using SkiaSharp;

View File

@ -218,6 +218,7 @@ namespace Artemis.Core.Services
); );
_logger.Information("Startup arguments: {args}", StartupArguments); _logger.Information("Startup arguments: {args}", StartupArguments);
_logger.Information("Elevated permissions: {perms}", IsElevated); _logger.Information("Elevated permissions: {perms}", IsElevated);
_logger.Information("Stopwatch high resolution: {perms}", Stopwatch.IsHighResolution);
ApplyLoggingLevel(); ApplyLoggingLevel();

View File

@ -43,6 +43,13 @@ public class SelectionRectangle : Control
public static readonly StyledProperty<IControl?> InputElementProperty = public static readonly StyledProperty<IControl?> InputElementProperty =
AvaloniaProperty.Register<SelectionRectangle, IControl?>(nameof(InputElement), notifying: OnInputElementChanged); AvaloniaProperty.Register<SelectionRectangle, IControl?>(nameof(InputElement), notifying: OnInputElementChanged);
/// <summary>
/// Defines the <see cref="ZoomRatio" /> property.
/// </summary>
public static readonly StyledProperty<double> ZoomRatioProperty =
AvaloniaProperty.Register<SelectionRectangle, double>(nameof(ZoomRatio), 1);
/// <summary> /// <summary>
/// Defines the read-only <see cref="IsSelecting" /> property. /// Defines the read-only <see cref="IsSelecting" /> property.
/// </summary> /// </summary>
@ -108,6 +115,15 @@ public class SelectionRectangle : Control
set => SetValue(InputElementProperty, value); set => SetValue(InputElementProperty, value);
} }
/// <summary>
/// Gets or sets the zoom ratio to counteract when drawing
/// </summary>
public double ZoomRatio
{
get => GetValue(ZoomRatioProperty);
set => SetValue(ZoomRatioProperty, value);
}
/// <summary> /// <summary>
/// Gets a boolean indicating whether the selection rectangle is currently performing a selection. /// Gets a boolean indicating whether the selection rectangle is currently performing a selection.
/// </summary> /// </summary>
@ -150,6 +166,7 @@ public class SelectionRectangle : Control
((SelectionRectangle) sender).SubscribeToInputElement(); ((SelectionRectangle) sender).SubscribeToInputElement();
} }
private void ParentOnPointerPressed(object? sender, PointerPressedEventArgs e) private void ParentOnPointerPressed(object? sender, PointerPressedEventArgs e)
{ {
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
@ -227,7 +244,7 @@ public class SelectionRectangle : Control
public override void Render(DrawingContext drawingContext) public override void Render(DrawingContext drawingContext)
{ {
if (_displayRect != null) if (_displayRect != null)
drawingContext.DrawRectangle(Background, new Pen(BorderBrush, BorderThickness), _displayRect.Value, BorderRadius, BorderRadius); drawingContext.DrawRectangle(Background, new Pen(BorderBrush, BorderThickness / ZoomRatio), _displayRect.Value, BorderRadius / ZoomRatio, BorderRadius / ZoomRatio);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using Artemis.Core; using Artemis.Core;
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands; namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
@ -32,16 +33,37 @@ public class ToggleLayerPropertyKeyframes<T> : IProfileEditorCommand
/// <inheritdoc /> /// <inheritdoc />
public void Execute() public void Execute()
{ {
if (_enable)
{
_layerProperty.KeyframesEnabled = true;
// If there weren't any keyframes yet, add one with the current value at the current time
if (!_layerProperty.Keyframes.Any())
{
// If executed before, reuse the previous keyframe
_keyframe ??= new LayerPropertyKeyframe<T>(_layerProperty.CurrentValue, _time, Easings.Functions.Linear, _layerProperty); _keyframe ??= new LayerPropertyKeyframe<T>(_layerProperty.CurrentValue, _time, Easings.Functions.Linear, _layerProperty);
_layerProperty.KeyframesEnabled = _enable;
_layerProperty.AddKeyframe(_keyframe); _layerProperty.AddKeyframe(_keyframe);
} }
}
else
{
_layerProperty.KeyframesEnabled = false;
}
}
/// <inheritdoc /> /// <inheritdoc />
public void Undo() public void Undo()
{ {
_layerProperty.RemoveKeyframe(_keyframe!); if (_enable)
_layerProperty.KeyframesEnabled = !_enable; {
if (_keyframe != null)
_layerProperty.RemoveKeyframe(_keyframe);
_layerProperty.KeyframesEnabled = false;
}
else
{
_layerProperty.KeyframesEnabled = true;
}
} }
#endregion #endregion

View File

@ -33,6 +33,12 @@
<controls:HyperlinkButton Classes="icon-button icon-button-small"> <controls:HyperlinkButton Classes="icon-button icon-button-small">
<avalonia:MaterialIcon Kind="Web" /> <avalonia:MaterialIcon Kind="Web" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
<TextBlock Margin="0 5 0 0">Button.window-button</TextBlock>
<Button Classes="title-bar-button">
<avalonia:MaterialIcon Kind="WindowMinimize" />
</Button>
</StackPanel> </StackPanel>
</Border> </Border>
</Design.PreviewWith> </Design.PreviewWith>
@ -69,4 +75,15 @@
<Style Selector="controls|HyperlinkButton.icon-button"> <Style Selector="controls|HyperlinkButton.icon-button">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" /> <Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style> </Style>
<Style Selector="Button.title-bar-button">
<Setter Property="Width" Value="46"></Setter>
<Setter Property="Height" Value="32"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="CornerRadius" Value="0"></Setter>
<Setter Property="Margin" Value="0"></Setter>
</Style>
<Style Selector="Button.title-bar-button:pointerover">
<Setter Property="Background" Value="Red"></Setter>
</Style>
</Styles> </Styles>

View File

@ -5,7 +5,7 @@
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugView"> x:Class="Artemis.UI.Screens.Debugger.Performance.PerformanceDebugView">
<Grid RowDefinitions="Auto,*"> <Grid RowDefinitions="Auto,Auto,*">
<StackPanel Grid.Row="0"> <StackPanel Grid.Row="0">
<TextBlock Classes="h3">Performance</TextBlock> <TextBlock Classes="h3">Performance</TextBlock>
<TextBlock TextWrapping="Wrap"> <TextBlock TextWrapping="Wrap">
@ -21,7 +21,19 @@
</Grid> </Grid>
</StackPanel> </StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto"> <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="10 0">
<TextBlock Text="FPS: " />
<TextBlock FontWeight="Bold" Text="{Binding CurrentFps}" />
<TextBlock Text=" at " />
<TextBlock Text="{Binding RenderWidth}" />
<TextBlock Text="x" />
<TextBlock Text="{Binding RenderHeight}" />
<TextBlock Text=" - Renderer: "></TextBlock>
<TextBlock Text="{Binding Renderer}"></TextBlock>
</StackPanel>
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
<ItemsControl Items="{Binding Items}" Margin="0 0 10 0" /> <ItemsControl Items="{Binding Items}" Margin="0 0 10 0" />
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>

View File

@ -8,16 +8,23 @@ using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using ReactiveUI; using ReactiveUI;
using SkiaSharp;
namespace Artemis.UI.Screens.Debugger.Performance namespace Artemis.UI.Screens.Debugger.Performance
{ {
public class PerformanceDebugViewModel : ActivatableViewModelBase, IRoutableViewModel public class PerformanceDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
{ {
private readonly ICoreService _coreService;
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private double _currentFps;
private string? _renderer;
private int _renderHeight;
private int _renderWidth;
public PerformanceDebugViewModel(IScreen hostScreen, IPluginManagementService pluginManagementService) public PerformanceDebugViewModel(IScreen hostScreen, ICoreService coreService, IPluginManagementService pluginManagementService)
{ {
HostScreen = hostScreen; HostScreen = hostScreen;
_coreService = coreService;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
Timer updateTimer = new(500); Timer updateTimer = new(500);
@ -38,6 +45,7 @@ namespace Artemis.UI.Screens.Debugger.Performance
.Subscribe(_ => Repopulate()) .Subscribe(_ => Repopulate())
.DisposeWith(disposables); .DisposeWith(disposables);
HandleActivation();
PopulateItems(); PopulateItems();
updateTimer.Start(); updateTimer.Start();
@ -45,14 +53,50 @@ namespace Artemis.UI.Screens.Debugger.Performance
{ {
updateTimer.Stop(); updateTimer.Stop();
Items.Clear(); Items.Clear();
HandleDeactivation();
}).DisposeWith(disposables); }).DisposeWith(disposables);
}); });
} }
public ObservableCollection<PerformanceDebugPluginViewModel> Items { get; } = new();
public string UrlPathSegment => "performance"; public string UrlPathSegment => "performance";
public IScreen HostScreen { get; } public IScreen HostScreen { get; }
public ObservableCollection<PerformanceDebugPluginViewModel> Items { get; } = new();
public double CurrentFps
{
get => _currentFps;
set => this.RaiseAndSetIfChanged(ref _currentFps, value);
}
public int RenderWidth
{
get => _renderWidth;
set => this.RaiseAndSetIfChanged(ref _renderWidth, value);
}
public int RenderHeight
{
get => _renderHeight;
set => this.RaiseAndSetIfChanged(ref _renderHeight, value);
}
public string? Renderer
{
get => _renderer;
set => this.RaiseAndSetIfChanged(ref _renderer, value);
}
private void HandleActivation()
{
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
_coreService.FrameRendered += CoreServiceOnFrameRendered;
}
private void HandleDeactivation()
{
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
}
private void PopulateItems() private void PopulateItems()
{ {
@ -69,10 +113,19 @@ namespace Artemis.UI.Screens.Debugger.Performance
PopulateItems(); PopulateItems();
} }
private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e) private void UpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
{ {
foreach (PerformanceDebugPluginViewModel viewModel in Items) foreach (PerformanceDebugPluginViewModel viewModel in Items)
viewModel.Update(); viewModel.Update();
} }
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
{
CurrentFps = _coreService.FrameRate;
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
RenderHeight = bitmapInfo.Height;
RenderWidth = bitmapInfo.Width;
}
} }
} }

View File

@ -24,6 +24,21 @@
<TextBlock Text="{Binding Renderer}"></TextBlock> <TextBlock Text="{Binding Renderer}"></TextBlock>
</StackPanel> </StackPanel>
<Image Source="{Binding CurrentFrame}" /> <Border Classes="card">
<ZoomBorder Name="ZoomBorder"
Stretch="None"
ClipToBounds="True"
Focusable="True"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Image Name="Visualization" Source="{Binding CurrentFrame}">
<Image.Transitions>
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut"/>
</Transitions>
</Image.Transitions>
</Image>
</ZoomBorder>
</Border>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -102,7 +102,6 @@ namespace Artemis.UI.Screens.Debugger.Render
} }
} }
public string UrlPathSegment => "render"; public string UrlPathSegment => "render";
public IScreen HostScreen { get; } public IScreen HostScreen { get; }
} }

View File

@ -9,7 +9,9 @@
<controls:SelectionRectangle InputElement="{Binding $parent[ZoomBorder]}" <controls:SelectionRectangle InputElement="{Binding $parent[ZoomBorder]}"
BorderBrush="{DynamicResource SystemAccentColor}" BorderBrush="{DynamicResource SystemAccentColor}"
BorderRadius="8" BorderRadius="8"
SelectionFinished="SelectionRectangle_OnSelectionFinished"> BorderThickness="2"
SelectionFinished="SelectionRectangle_OnSelectionFinished"
ZoomRatio="{Binding $parent[ZoomBorder].ZoomX}">
<controls:SelectionRectangle.Background> <controls:SelectionRectangle.Background>
<SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2"></SolidColorBrush> <SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2"></SolidColorBrush>
</controls:SelectionRectangle.Background> </controls:SelectionRectangle.Background>

View File

@ -1,4 +1,5 @@
using Artemis.UI.Shared.Events; using Artemis.UI.Shared.Events;
using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Skia; using Avalonia.Skia;
@ -19,6 +20,6 @@ public class SelectionAddToolView : ReactiveUserControl<SelectionAddToolViewMode
private void SelectionRectangle_OnSelectionFinished(object? sender, SelectionRectangleEventArgs e) private void SelectionRectangle_OnSelectionFinished(object? sender, SelectionRectangleEventArgs e)
{ {
ViewModel?.AddLedsInRectangle(e.Rectangle.ToSKRect()); ViewModel?.AddLedsInRectangle(e.Rectangle.ToSKRect(), e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Control));
} }
} }

View File

@ -57,12 +57,26 @@ public class SelectionAddToolViewModel : ToolViewModel
base.Dispose(disposing); base.Dispose(disposing);
} }
public void AddLedsInRectangle(SKRect rect) public void AddLedsInRectangle(SKRect rect, bool expand, bool inverse)
{ {
if (_layer == null) if (_layer == null)
return; return;
List<ArtemisLed> leds = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList(); if (inverse)
{
List<ArtemisLed> toRemove = _layer.Leds.Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
List<ArtemisLed> toAdd = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).Except(toRemove).ToList();
List<ArtemisLed> leds = _layer.Leds.Except(toRemove).ToList();
leds.AddRange(toAdd);
_profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds)); _profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds));
} }
else
{
List<ArtemisLed> leds = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
if (expand)
leds.AddRange(_layer.Leds);
_profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds.Distinct().ToList()));
}
}
} }

View File

@ -11,7 +11,9 @@
<controls:SelectionRectangle InputElement="{Binding $parent[paz:ZoomBorder]}" <controls:SelectionRectangle InputElement="{Binding $parent[paz:ZoomBorder]}"
BorderBrush="{DynamicResource SystemAccentColor}" BorderBrush="{DynamicResource SystemAccentColor}"
BorderRadius="8" BorderRadius="8"
SelectionFinished="SelectionRectangle_OnSelectionFinished"> BorderThickness="2"
SelectionFinished="SelectionRectangle_OnSelectionFinished"
ZoomRatio="{Binding $parent[ZoomBorder].ZoomX}">
<controls:SelectionRectangle.Background> <controls:SelectionRectangle.Background>
<SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2"></SolidColorBrush> <SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2"></SolidColorBrush>
</controls:SelectionRectangle.Background> </controls:SelectionRectangle.Background>

View File

@ -10,6 +10,13 @@
<UserControl.Styles> <UserControl.Styles>
<Style Selector="Path.layer-visualizer"> <Style Selector="Path.layer-visualizer">
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" /> <Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
<Setter Property="Transitions">
<Setter.Value>
<Transitions>
<DoubleTransition Property="StrokeThickness" Duration="0:0:0.2" Easing="CubicEaseOut"></DoubleTransition>
</Transitions>
</Setter.Value>
</Setter>
</Style> </Style>
<Style Selector="Path.layer-visualizer-selected"> <Style Selector="Path.layer-visualizer-selected">
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorLight1}" /> <Setter Property="Stroke" Value="{StaticResource SystemAccentColorLight1}" />
@ -17,27 +24,35 @@
<Style Selector="Path.layer-visualizer-unbound"> <Style Selector="Path.layer-visualizer-unbound">
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" /> <Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
<Setter Property="Opacity" Value="0.50" /> <Setter Property="Opacity" Value="0.50" />
<Setter Property="Transitions">
<Setter.Value>
<Transitions>
<DoubleTransition Property="StrokeThickness" Duration="0:0:0.2" Easing="CubicEaseOut"></DoubleTransition>
</Transitions>
</Setter.Value>
</Setter>
</Style> </Style>
<Style Selector="Path.layer-visualizer-unbound-selected"> <Style Selector="Path.layer-visualizer-unbound-selected">
<Setter Property="Opacity" Value="0.75" /> <Setter Property="Opacity" Value="0.75" />
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<Canvas> <Canvas>
<Path Classes="layer-visualizer-unbound" <Path Name="LayerVisualizerUnbound"
Classes="layer-visualizer-unbound"
Classes.layer-visualizer-unbound-selected="{CompiledBinding Selected}" Classes.layer-visualizer-unbound-selected="{CompiledBinding Selected}"
Data="{CompiledBinding ShapeGeometry, Mode=OneWay}" Data="{CompiledBinding ShapeGeometry, Mode=OneWay}"
StrokeThickness="2" StrokeThickness="4"
Margin="0 0 2 2"
StrokeJoin="Round"> StrokeJoin="Round">
</Path> </Path>
<Border ClipToBounds="True" <Border Name="LayerVisualizerBounds"
Width="{CompiledBinding LayerBounds.Width}" ClipToBounds="True"
Height="{CompiledBinding LayerBounds.Height}"> Width="{CompiledBinding LayerBounds.Width, Mode=OneWay}"
<Path Classes="layer-visualizer" Height="{CompiledBinding LayerBounds.Height, Mode=OneWay}">
<Path Name="LayerVisualizer"
Classes="layer-visualizer"
Classes.layer-visualizer-selected="{CompiledBinding Selected}" Classes.layer-visualizer-selected="{CompiledBinding Selected}"
Data="{CompiledBinding ShapeGeometry, Mode=OneWay}" Data="{CompiledBinding ShapeGeometry, Mode=OneWay}"
StrokeThickness="2" StrokeThickness="4"
Margin="0 0 2 2"
StrokeJoin="Round"> StrokeJoin="Round">
</Path> </Path>
</Border> </Border>

View File

@ -1,3 +1,10 @@
using System;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.PanAndZoom;
using Avalonia.Controls.Shapes;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
@ -5,14 +12,50 @@ namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers
{ {
public partial class LayerShapeVisualizerView : ReactiveUserControl<LayerShapeVisualizerViewModel> public partial class LayerShapeVisualizerView : ReactiveUserControl<LayerShapeVisualizerViewModel>
{ {
private ZoomBorder? _zoomBorder;
private readonly Path _layerVisualizerUnbound;
private readonly Path _layerVisualizer;
public LayerShapeVisualizerView() public LayerShapeVisualizerView()
{ {
InitializeComponent(); InitializeComponent();
_layerVisualizer = this.Get<Path>("LayerVisualizer");
_layerVisualizerUnbound = this.Get<Path>("LayerVisualizerUnbound");
} }
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
#region Overrides of TemplatedControl
/// <inheritdoc />
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_zoomBorder = (ZoomBorder?) this.GetLogicalAncestors().FirstOrDefault(l => l is ZoomBorder);
if (_zoomBorder != null)
_zoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged;
base.OnAttachedToLogicalTree(e);
}
/// <inheritdoc />
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
if (_zoomBorder != null)
_zoomBorder.PropertyChanged -= ZoomBorderOnPropertyChanged;
base.OnDetachedFromLogicalTree(e);
}
private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property != ZoomBorder.ZoomXProperty || _zoomBorder == null)
return;
_layerVisualizer.StrokeThickness = Math.Max(1, 4 / _zoomBorder.ZoomX);
_layerVisualizerUnbound.StrokeThickness = _layerVisualizer.StrokeThickness;
}
#endregion
} }
} }

View File

@ -15,10 +15,10 @@
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorDark2}" /> <Setter Property="Stroke" Value="{StaticResource SystemAccentColorDark2}" />
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<Path Classes="layer-visualizer" <Path Name="LayerVisualizer"
Classes="layer-visualizer"
Classes.layer-visualizer-selected="{CompiledBinding Selected}" Classes.layer-visualizer-selected="{CompiledBinding Selected}"
StrokeThickness="2" StrokeThickness="4"
Margin="0 0 2 2"
StrokeDashArray="6,2" StrokeDashArray="6,2"
StrokeJoin="Round"> StrokeJoin="Round">
<Path.Data> <Path.Data>

View File

@ -1,15 +1,55 @@
using System;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.PanAndZoom;
using Avalonia.Controls.Shapes;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.VisualTree;
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers
{ {
public partial class LayerVisualizerView : ReactiveUserControl<LayerVisualizerViewModel> public partial class LayerVisualizerView : ReactiveUserControl<LayerVisualizerViewModel>
{ {
private ZoomBorder? _zoomBorder;
private readonly Path _layerVisualizer;
public LayerVisualizerView() public LayerVisualizerView()
{ {
InitializeComponent(); InitializeComponent();
_layerVisualizer = this.Get<Path>("LayerVisualizer");
} }
#region Overrides of TemplatedControl
/// <inheritdoc />
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_zoomBorder = (ZoomBorder?) this.GetLogicalAncestors().FirstOrDefault(l => l is ZoomBorder);
if (_zoomBorder != null)
_zoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged;
base.OnAttachedToLogicalTree(e);
}
/// <inheritdoc />
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
if (_zoomBorder != null)
_zoomBorder.PropertyChanged -= ZoomBorderOnPropertyChanged;
base.OnDetachedFromLogicalTree(e);
}
private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property != ZoomBorder.ZoomXProperty || _zoomBorder == null)
return;
_layerVisualizer.StrokeThickness = Math.Max(1, 4 / _zoomBorder.ZoomX);
}
#endregion
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);

View File

@ -5,7 +5,7 @@
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Root.DefaultTitleBarView"> x:Class="Artemis.UI.Screens.Root.DefaultTitleBarView">
<Button Classes="icon-button icon-button-small" Command="{Binding ShowDebugger}" HorizontalAlignment="Right"> <Button Classes="title-bar-button" Command="{Binding ShowDebugger}" HorizontalAlignment="Right">
<avalonia:MaterialIcon Kind="Bug"></avalonia:MaterialIcon> <avalonia:MaterialIcon Kind="Bug"></avalonia:MaterialIcon>
</Button> </Button>
</UserControl> </UserControl>