mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
UI - Added reusable device visualizer
Settings - Show device LED images in settings Editor - Improved performance on big surfaces
This commit is contained in:
1 parent
8a45039863
commit
efa0f28231
31 files changed
+407
-402
No files matched your search
@@ -26,10 +26,12 @@
|
||||
<PackageReference Include="Ninject" Version="3.3.4" />
|
||||
<PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="1.68.2-preview.29" />
|
||||
<PackageReference Include="SkiaSharp.Views.WPF" Version="1.68.2-preview.29" />
|
||||
<PackageReference Include="Stylet" Version="1.3.1" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.0" />
|
||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />
|
||||
<PackageReference Include="WriteableBitmapEx" Version="1.6.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="obj\x64\Debug\ColorPicker.g.cs" />
|
||||
@@ -50,6 +52,11 @@
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="RGB.NET.Core">
|
||||
<HintPath>..\..\..\RGB.NET\bin\netstandard2.0\RGB.NET.Core.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Fonts\RobotoMono-Regular.ttf" />
|
||||
</ItemGroup>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters" x:Class="Artemis.UI.Shared.ColorPicker"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters" x:Class="Artemis.UI.Shared.Controls.ColorPicker"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="101.848" d:DesignWidth="242.956">
|
||||
<UserControl.Resources>
|
||||
+1
-1
@@ -5,7 +5,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ColorPicker.xaml
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Artemis.Core.Models.Surface;
|
||||
using RGB.NET.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
public class DeviceVisualizer : FrameworkElement
|
||||
{
|
||||
public static readonly DependencyProperty DeviceProperty = DependencyProperty.Register(nameof(Device), typeof(ArtemisDevice), typeof(DeviceVisualizer),
|
||||
new FrameworkPropertyMetadata(default(ArtemisDevice), FrameworkPropertyMetadataOptions.AffectsRender, DevicePropertyChangedCallback));
|
||||
|
||||
public static readonly DependencyProperty ShowColorsProperty = DependencyProperty.Register(nameof(ShowColors), typeof(bool), typeof(DeviceVisualizer),
|
||||
new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
|
||||
private BitmapImage _deviceImage;
|
||||
private List<DeviceVisualizerLed> _deviceVisualizerLeds;
|
||||
private DrawingGroup _backingStore;
|
||||
|
||||
private static void DevicePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var deviceVisualizer = (DeviceVisualizer) d;
|
||||
deviceVisualizer.Dispatcher.Invoke(() =>
|
||||
{
|
||||
deviceVisualizer.SubscribeToSurfaceUpdate((ArtemisDevice) e.OldValue, (ArtemisDevice) e.NewValue);
|
||||
deviceVisualizer.Initialize();
|
||||
});
|
||||
}
|
||||
|
||||
public ArtemisDevice Device
|
||||
{
|
||||
get => (ArtemisDevice) GetValue(DeviceProperty);
|
||||
set => SetValue(DeviceProperty, value);
|
||||
}
|
||||
|
||||
public bool ShowColors
|
||||
{
|
||||
get => (bool) GetValue(ShowColorsProperty);
|
||||
set => SetValue(ShowColorsProperty, value);
|
||||
}
|
||||
|
||||
public DeviceVisualizer()
|
||||
{
|
||||
_backingStore = new DrawingGroup();
|
||||
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
|
||||
}
|
||||
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_deviceImage = null;
|
||||
_deviceVisualizerLeds.Clear();
|
||||
|
||||
if (Device == null)
|
||||
return;
|
||||
|
||||
// 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);
|
||||
|
||||
// Create all the LEDs
|
||||
foreach (var artemisLed in Device.Leds)
|
||||
{
|
||||
_deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed));
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeToSurfaceUpdate(ArtemisDevice oldValue, ArtemisDevice newValue)
|
||||
{
|
||||
if (oldValue != null)
|
||||
oldValue.Surface.RgbSurface.Updated -= RgbSurfaceOnUpdated;
|
||||
if (newValue != null)
|
||||
newValue.Surface.RgbSurface.Updated += RgbSurfaceOnUpdated;
|
||||
}
|
||||
|
||||
private void RgbSurfaceOnUpdated(UpdatedEventArgs args)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (ShowColors)
|
||||
{
|
||||
Render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Render()
|
||||
{
|
||||
var drawingContext = _backingStore.Open();
|
||||
|
||||
foreach (var deviceVisualizerLed in _deviceVisualizerLeds)
|
||||
deviceVisualizerLed.Render(drawingContext, true);
|
||||
|
||||
drawingContext.Close();
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
if (Device == null)
|
||||
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);
|
||||
|
||||
// Center and scale the visualization in the desired bounding box
|
||||
if (DesiredSize.Width > 0 && DesiredSize.Height > 0)
|
||||
{
|
||||
drawingContext.PushTransform(new TranslateTransform(DesiredSize.Width / 2 - scaledRect.Width / 2, DesiredSize.Height / 2 - scaledRect.Height / 2));
|
||||
drawingContext.PushTransform(new ScaleTransform(scale, 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.Render(drawingContext, false);
|
||||
|
||||
drawingContext.DrawDrawing(_backingStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Artemis.Core.Models.Surface;
|
||||
using RGB.NET.Core;
|
||||
using Color = System.Windows.Media.Color;
|
||||
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
internal class DeviceVisualizerLed
|
||||
{
|
||||
public DeviceVisualizerLed(ArtemisLed led)
|
||||
{
|
||||
Led = led;
|
||||
if (Led.RgbLed.Image != null && File.Exists(Led.RgbLed.Image.AbsolutePath))
|
||||
LedImage = new BitmapImage(Led.RgbLed.Image);
|
||||
CreateLedGeometry();
|
||||
}
|
||||
|
||||
public ArtemisLed Led { get; }
|
||||
public BitmapImage LedImage { get; set; }
|
||||
|
||||
public Geometry DisplayGeometry { get; private set; }
|
||||
|
||||
internal void Render(DrawingContext drawingContext, bool renderGeometry)
|
||||
{
|
||||
if (!renderGeometry)
|
||||
RenderImage(drawingContext);
|
||||
else
|
||||
RenderGeometry(drawingContext);
|
||||
}
|
||||
|
||||
private void CreateLedGeometry()
|
||||
{
|
||||
switch (Led.RgbLed.Shape)
|
||||
{
|
||||
case Shape.Custom:
|
||||
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
|
||||
CreateCustomGeometry(2.0);
|
||||
else
|
||||
CreateCustomGeometry(1.0);
|
||||
break;
|
||||
case Shape.Rectangle:
|
||||
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
|
||||
CreateKeyCapGeometry();
|
||||
else
|
||||
CreateRectangleGeometry();
|
||||
break;
|
||||
case Shape.Circle:
|
||||
CreateCircleGeometry();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Stroke geometry is the display geometry excluding the inner geometry
|
||||
DisplayGeometry.Transform = new TranslateTransform(Led.RgbLed.LedRectangle.Location.X, Led.RgbLed.LedRectangle.Location.Y);
|
||||
// Try to gain some performance
|
||||
DisplayGeometry = DisplayGeometry.GetFlattenedPathGeometry();
|
||||
DisplayGeometry.Freeze();
|
||||
}
|
||||
|
||||
private void CreateRectangleGeometry()
|
||||
{
|
||||
DisplayGeometry = new RectangleGeometry(new Rect(0.5, 0.5, Led.RgbLed.Size.Width - 1, Led.RgbLed.Size.Height - 1));
|
||||
}
|
||||
|
||||
private void CreateCircleGeometry()
|
||||
{
|
||||
DisplayGeometry = new EllipseGeometry(new Rect(0.5, 0.5, Led.RgbLed.Size.Width - 1, Led.RgbLed.Size.Height - 1));
|
||||
}
|
||||
|
||||
private void CreateKeyCapGeometry()
|
||||
{
|
||||
DisplayGeometry = new RectangleGeometry(new Rect(1, 1, Led.RgbLed.Size.Width - 2, Led.RgbLed.Size.Height - 2), 1.6, 1.6);
|
||||
}
|
||||
|
||||
private void CreateCustomGeometry(double deflateAmount)
|
||||
{
|
||||
try
|
||||
{
|
||||
DisplayGeometry = Geometry.Combine(
|
||||
Geometry.Empty,
|
||||
Geometry.Parse(Led.RgbLed.ShapeData),
|
||||
GeometryCombineMode.Union,
|
||||
new TransformGroup
|
||||
{
|
||||
Children = new TransformCollection
|
||||
{
|
||||
new ScaleTransform(Led.RgbLed.Size.Width - deflateAmount, Led.RgbLed.Size.Height - deflateAmount),
|
||||
new TranslateTransform(deflateAmount / 2, deflateAmount / 2)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CreateRectangleGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderGeometry(DrawingContext drawingContext)
|
||||
{
|
||||
if (DisplayGeometry == null)
|
||||
return;
|
||||
|
||||
var r = Led.RgbLed.Color.GetR();
|
||||
var g = Led.RgbLed.Color.GetG();
|
||||
var b = Led.RgbLed.Color.GetB();
|
||||
|
||||
var fillBrush = new SolidColorBrush(Color.FromArgb(100, r,g,b));
|
||||
fillBrush.Freeze();
|
||||
var penBrush = new SolidColorBrush(Color.FromArgb(255, r, g, b));
|
||||
penBrush.Freeze();
|
||||
|
||||
drawingContext.DrawGeometry(fillBrush, new Pen(penBrush, 1), DisplayGeometry);
|
||||
}
|
||||
|
||||
private void RenderImage(DrawingContext drawingContext)
|
||||
{
|
||||
if (LedImage == null)
|
||||
return;
|
||||
|
||||
var ledRect = new Rect(
|
||||
Led.RgbLed.LedRectangle.Location.X,
|
||||
Led.RgbLed.LedRectangle.Location.Y,
|
||||
Led.RgbLed.LedRectangle.Size.Width,
|
||||
Led.RgbLed.LedRectangle.Size.Height
|
||||
);
|
||||
drawingContext.DrawImage(LedImage, ledRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<UserControl x:Class="Artemis.UI.Shared.DraggableFloat"
|
||||
<UserControl x:Class="Artemis.UI.Shared.Controls.DraggableFloat"
|
||||
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"
|
||||
+1
-1
@@ -5,7 +5,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DraggableFloat.xaml
|
||||
+1
-2
@@ -1,9 +1,8 @@
|
||||
<UserControl x:Class="Artemis.UI.Shared.GradientPicker"
|
||||
<UserControl x:Class="Artemis.UI.Shared.Controls.GradientPicker"
|
||||
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"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
+1
-1
@@ -8,7 +8,7 @@ using Artemis.Core.Models.Profile;
|
||||
using Artemis.UI.Shared.Annotations;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
namespace Artemis.UI.Shared.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for GradientPicker.xaml
|
||||
@@ -9,6 +9,7 @@
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:controls1="clr-namespace:Artemis.UI.Shared.Controls"
|
||||
mc:Ignorable="d"
|
||||
Title="Gradient Editor"
|
||||
Background="{DynamicResource MaterialDesignPaper}"
|
||||
@@ -80,7 +81,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" HorizontalAlignment="Right">Color:</Label>
|
||||
<shared:ColorPicker
|
||||
<controls1:ColorPicker
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
x:Name="CurrentColor"
|
||||
|
||||
@@ -100,6 +100,7 @@ namespace Artemis.UI.Shared.Services.Dialog
|
||||
{
|
||||
try
|
||||
{
|
||||
DialogHost.CloseDialogCommand.Execute(new object(), null);
|
||||
await ShowDialog<ExceptionDialogViewModel>(arguments);
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -122,7 +123,7 @@ namespace Artemis.UI.Shared.Services.Dialog
|
||||
else
|
||||
result = DialogHost.Show(view, identifier, viewModel.OnDialogOpened, viewModel.OnDialogClosed);
|
||||
});
|
||||
|
||||
|
||||
return await result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user