Device visualizer - Added support for rotation and scale

Profile editor - Fixed keyframe context menu actions
This commit is contained in:
SpoinkyNL committed 2020-06-22 22:14:33 +02:00
1 parent 8cb5d31175
commit 920aea6695
15 files changed
+185 -78

No files matched your search

@@ -30,16 +30,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.0.1" />
<PackageReference Include="Fody" Version="6.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
<PackageReference Include="MaterialDesignExtensions" Version="3.1.0" />
<PackageReference Include="MaterialDesignThemes" Version="3.1.3" />
<PackageReference Include="Ninject" Version="3.3.4" />
<PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.2.8" />
<PackageReference Include="SkiaSharp" Version="1.68.3" />
<PackageReference Include="SkiaSharp.Views.WPF" Version="1.68.3" />
<PackageReference Include="Stylet" Version="1.3.2" />
@@ -1,16 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core.Models.Surface;
using RGB.NET.Core;
using Point = System.Windows.Point;
using Size = System.Windows.Size;
namespace Artemis.UI.Shared.Controls
{
@@ -29,6 +28,7 @@ namespace Artemis.UI.Shared.Controls
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
private BitmapImage _deviceImage;
private bool _subscribed;
private ArtemisDevice _oldDevice;
public DeviceVisualizer()
{
@@ -38,7 +38,7 @@ namespace Artemis.UI.Shared.Controls
Loaded += (sender, args) => SubscribeToUpdate(true);
Unloaded += (sender, args) => SubscribeToUpdate(false);
}
public ArtemisDevice Device
{
get => (ArtemisDevice) GetValue(DeviceProperty);
@@ -68,9 +68,10 @@ namespace Artemis.UI.Shared.Controls
return;
// Determine the scale required to fit the desired size of the control
var scale = Math.Min(DesiredSize.Width / Device.RgbDevice.Size.Width, DesiredSize.Height / Device.RgbDevice.Size.Height);
var scaledRect = new Rect(0, 0, Device.RgbDevice.Size.Width * scale, Device.RgbDevice.Size.Height * scale);
var measureSize = MeasureOverride(Size.Empty);
var scale = Math.Min(DesiredSize.Width / measureSize.Width, DesiredSize.Height / measureSize.Height);
var scaledRect = new Rect(0, 0, measureSize.Width * scale, measureSize.Height * scale);
// Center and scale the visualization in the desired bounding box
if (DesiredSize.Width > 0 && DesiredSize.Height > 0)
{
@@ -78,16 +79,45 @@ namespace Artemis.UI.Shared.Controls
drawingContext.PushTransform(new ScaleTransform(scale, scale));
}
// Determine the offset required to rotate within bounds
var rotationRect = new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
rotationRect.Transform(new RotateTransform(Device.Rotation).Value);
// Apply device rotation
drawingContext.PushTransform(new TranslateTransform(0 - rotationRect.Left, 0 - rotationRect.Top));
drawingContext.PushTransform(new RotateTransform(Device.Rotation));
// Apply device scale
drawingContext.PushTransform(new ScaleTransform(Device.Scale, Device.Scale));
// Render device and LED images
if (_deviceImage != null)
drawingContext.DrawImage(_deviceImage, new Rect(0, 0, Device.RgbDevice.Size.Width, Device.RgbDevice.Size.Height));
foreach (var deviceVisualizerLed in _deviceVisualizerLeds)
deviceVisualizerLed.RenderImage(drawingContext);
drawingContext.DrawDrawing(_backingStore);
}
protected override Size MeasureOverride(Size availableSize)
{
if (Device == null)
return Size.Empty;
var rotationRect = new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
rotationRect.Transform(new RotateTransform(Device.Rotation).Value);
return rotationRect.Size;
}
private void UpdateTransform()
{
InvalidateVisual();
InvalidateMeasure();
}
private void SubscribeToUpdate(bool subscribe)
{
if (_subscribed == subscribe)
@@ -121,6 +151,13 @@ namespace Artemis.UI.Shared.Controls
if (Device == null)
return;
if (_oldDevice != null)
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
_oldDevice = Device;
Device.RgbDevice.PropertyChanged += DevicePropertyChanged;
UpdateTransform();
// Load the device main image
if (Device.RgbDevice?.DeviceInfo?.Image?.AbsolutePath != null && File.Exists(Device.RgbDevice.DeviceInfo.Image.AbsolutePath))
_deviceImage = new BitmapImage(Device.RgbDevice.DeviceInfo.Image);
@@ -130,7 +167,10 @@ namespace Artemis.UI.Shared.Controls
_deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed));
if (!ShowColors)
{
InvalidateMeasure();
return;
}
// Create the opacity drawing group
var opacityDrawingGroup = new DrawingGroup();
@@ -157,6 +197,14 @@ namespace Artemis.UI.Shared.Controls
var bitmapBrush = new ImageBrush(bitmap);
bitmapBrush.Freeze();
_backingStore.OpacityMask = bitmapBrush;
InvalidateMeasure();
}
private void DevicePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Device.RgbDevice.Scale) || e.PropertyName == nameof(Device.RgbDevice.Rotation))
UpdateTransform();
}
private void RgbSurfaceOnUpdated(UpdatedEventArgs e)
@@ -13,12 +13,14 @@ namespace Artemis.UI.Shared.Controls
{
public DeviceVisualizerLed(ArtemisLed led)
{
if (led.Device.Scale != 1)
Console.WriteLine();
Led = led;
LedRect = new Rect(
Led.RgbLed.LedRectangle.Location.X,
Led.RgbLed.LedRectangle.Location.Y,
Led.RgbLed.LedRectangle.Size.Width,
Led.RgbLed.LedRectangle.Size.Height
Led.RgbLed.Location.X,
Led.RgbLed.Location.Y,
Led.RgbLed.Size.Width,
Led.RgbLed.Size.Height
);
if (Led.RgbLed.Image != null && File.Exists(Led.RgbLed.Image.AbsolutePath))
@@ -97,7 +99,7 @@ namespace Artemis.UI.Shared.Controls
}
// Stroke geometry is the display geometry excluding the inner geometry
DisplayGeometry.Transform = new TranslateTransform(Led.RgbLed.LedRectangle.Location.X, Led.RgbLed.LedRectangle.Location.Y);
DisplayGeometry.Transform = new TranslateTransform(Led.RgbLed.Location.X, Led.RgbLed.Location.Y);
// Try to gain some performance
DisplayGeometry.Freeze();
}
@@ -12,7 +12,7 @@
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource MaterialDesignValidationErrorTemplate}" />
</Style>
</UserControl.Style>
<StackPanel>
<Grid>
<!-- Drag handle -->
<Border x:Name="DragHandle" BorderThickness="0,0,0,1" Height="19">
<Border.BorderBrush>
@@ -28,8 +28,8 @@
<TextBlock Style="{x:Null}"
Width="60"
Height="17"
Padding="1 0"
Margin="0 4 0 0"
Padding="2 0"
Margin="0 3 0 0"
Text="{Binding Value, StringFormat=N3, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Cursor="/Resources/Cursors/aero_drag_ew.cur"
Foreground="{DynamicResource SecondaryAccentBrush}"
@@ -42,11 +42,13 @@
<!-- Input -->
<TextBox x:Name="Input"
Width="60"
Height="20"
Height="21"
Padding="0 0 -2 0"
HorizontalAlignment="Left"
Text="{Binding Value, StringFormat=N3, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
LostFocus="InputLostFocus"
KeyDown="InputKeyDown"
Visibility="Collapsed"
RequestBringIntoView="Input_OnRequestBringIntoView" />
</StackPanel>
</Grid>
</UserControl>
@@ -8,6 +8,7 @@ namespace Artemis.UI.Shared.PropertyInput
public abstract class PropertyInputViewModel<T> : ValidatingModelBase, IDisposable
{
private T _inputValue;
private bool _inputDragging;
protected PropertyInputViewModel(LayerProperty<T> layerProperty, IProfileEditorService profileEditorService)
{
@@ -30,14 +31,18 @@ namespace Artemis.UI.Shared.PropertyInput
public LayerProperty<T> LayerProperty { get; }
public IProfileEditorService ProfileEditorService { get; }
public bool InputDragging { get; private set; }
public bool InputDragging
{
get => _inputDragging;
private set => SetAndNotify(ref _inputDragging, value);
}
public T InputValue
{
get => _inputValue;
set
{
_inputValue = value;
SetAndNotify(ref _inputValue, value);
ApplyInputValue();
}
}
@@ -14,7 +14,7 @@
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Text="{Binding Header}"
TextWrapping="Wrap" />
<Separator Margin="0 15" />
<ScrollViewer MaxHeight="500">
<ScrollViewer MaxHeight="500" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsControl ItemsSource="{Binding Exceptions}">
<ItemsControl.ItemTemplate>
@@ -7,6 +7,8 @@ namespace Artemis.UI.Shared.Screens.Dialogs
{
public class ExceptionDialogViewModel : DialogViewModelBase
{
private List<DialogException> _exceptions;
public ExceptionDialogViewModel(string message, Exception exception)
{
Header = message;
@@ -21,8 +23,12 @@ namespace Artemis.UI.Shared.Screens.Dialogs
}
public string Header { get; }
public List<DialogException> Exceptions { get; set; }
public List<DialogException> Exceptions
{
get => _exceptions;
set => SetAndNotify(ref _exceptions, value);
}
public void Close()
{
@@ -96,18 +96,25 @@ namespace Artemis.UI.Shared.Services.Dialog
new ConstructorArgument("exception", exception)
};
await Execute.OnUIThreadAsync(async () =>
try
{
try
await Execute.OnUIThreadAsync(async () =>
{
DialogHost.CloseDialogCommand.Execute(new object(), null);
await ShowDialog<ExceptionDialogViewModel>(arguments);
}
catch (Exception)
{
// ignored
}
});
try
{
DialogHost.CloseDialogCommand.Execute(new object(), null);
await ShowDialog<ExceptionDialogViewModel>(arguments);
}
catch (Exception)
{
// ignored
}
});
}
catch (Exception)
{
// ignored
}
}
private async Task<object> ShowDialog(string identifier, DialogViewModelBase viewModel)
@@ -5,6 +5,9 @@ namespace Artemis.UI.Shared.Services.Dialog
{
public abstract class DialogViewModelBase : ValidatingModelBase
{
private DialogViewModelHost _dialogViewModelHost;
private DialogSession _session;
protected DialogViewModelBase(IModelValidator validator) : base(validator)
{
}
@@ -13,8 +16,17 @@ namespace Artemis.UI.Shared.Services.Dialog
{
}
public DialogViewModelHost DialogViewModelHost { get; set; }
public DialogSession Session { get; private set; }
public DialogViewModelHost DialogViewModelHost
{
get => _dialogViewModelHost;
set => SetAndNotify(ref _dialogViewModelHost, value);
}
public DialogSession Session
{
get => _session;
private set => SetAndNotify(ref _session, value);
}
public void OnDialogOpened(object sender, DialogOpenedEventArgs e)
{