mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Device visualizer - Added support for rotation and scale
Profile editor - Fixed keyframe context menu actions
This commit is contained in:
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)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user