Merge branch 'master' into RGB.NET_update

This commit is contained in:
Robert committed 2021-03-05 15:40:15 +01:00
commit da90c0d3a1
94 files changed
+1525 -1007

No files matched your search

@@ -0,0 +1,133 @@
<mde:MaterialWindow x:Class="Artemis.UI.Screens.Settings.Device.DeviceDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:mde="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Settings.Device"
mc:Ignorable="d"
Title="{Binding DisplayName}"
TitleBarIcon="{StaticResource BowIcon}"
Foreground="{DynamicResource MaterialDesignBody}"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
UseLayoutRounding="True"
FadeContentIfInactive="False"
Width="1400"
Height="800"
d:DesignHeight="800" d:DesignWidth="800"
d:DataContext="{d:DesignInstance device:DeviceDialogViewModel}"
Icon="/Resources/Images/Logo/logo-512.png">
<mde:MaterialWindow.InputBindings>
<KeyBinding Command="{s:Action ClearSelection}" Key="Escape" />
</mde:MaterialWindow.InputBindings>
<DockPanel>
<mde:AppBar Type="Dense"
Title="{Binding Device.RgbDevice.DeviceInfo.Model}"
ShowShadow="True"
DockPanel.Dock="Top"
Margin="-18 0 0 0">
<mde:AppBar.AppIcon>
<materialDesign:PackIcon Kind="HammerWrench" Width="20" Height="28" />
</mde:AppBar.AppIcon>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Identify" Command="{s:Action IdentifyDevice}">
<materialDesign:PackIcon Kind="AlarmLight" />
</Button>
<materialDesign:PopupBox PlacementMode="BottomAndAlignRightEdges" StaysOpen="False">
<StackPanel>
<Button Command="{s:Action OpenPluginDirectory}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Plugin" Margin="0 0 10 0 " VerticalAlignment="Center" />
<TextBlock VerticalAlignment="Center">Open plugin directory</TextBlock>
</StackPanel>
</Button>
<Button Command="{s:Action OpenImageDirectory}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Image" Margin="0 0 10 0 " VerticalAlignment="Center" />
<TextBlock VerticalAlignment="Center">Open layout image directory</TextBlock>
</StackPanel>
</Button>
<Separator />
<Button Command="{s:Action ReloadLayout}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Reload" Margin="0 0 10 0 " VerticalAlignment="Center" />
<TextBlock VerticalAlignment="Center">Reload layout</TextBlock>
</StackPanel>
</Button>
<Button Command="{s:Action ExportLayout}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Xml" Margin="0 0 10 0 " VerticalAlignment="Center" />
<TextBlock VerticalAlignment="Center">Export layout</TextBlock>
</StackPanel>
</Button>
</StackPanel>
</materialDesign:PopupBox>
</StackPanel>
</mde:AppBar>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Name="DeviceDisplayGrid" Grid.Column="0">
<Grid.Background>
<VisualBrush TileMode="Tile" Stretch="Uniform" Viewport="0 0 25 25" ViewportUnits="Absolute">
<VisualBrush.Visual>
<Grid Width="20" Height="20">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" />
<Rectangle Grid.Row="0" Grid.Column="1" />
<Rectangle Grid.Row="1" Grid.Column="0" />
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" />
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Background>
<shared:DeviceVisualizer Device="{Binding Device}"
HighlightedLeds="{Binding SelectedLeds}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ShowColors="True"
Margin="0 0 100 0" />
</Grid>
<GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" Panel.ZIndex="3" />
<materialDesign:Card Grid.Column="2"
materialDesign:ShadowAssist.ShadowDepth="Depth3"
Background="{DynamicResource MaterialDesignPaper}">
<TabControl
Style="{StaticResource MaterialDesignTabControl}"
ItemsSource="{Binding Items}"
SelectedItem="{Binding ActiveItem}"
DisplayMemberPath="DisplayName">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding IsAsync=True}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsTabStop="False"
TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</materialDesign:Card>
</Grid>
</DockPanel>
</mde:MaterialWindow>
@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.Shared;
using Artemis.UI.Shared.Services;
using Ookii.Dialogs.Wpf;
using RGB.NET.Layout;
using Stylet;
namespace Artemis.UI.Screens.Settings.Device
{
public class DeviceDialogViewModel : Conductor<Screen>.Collection.OneActive
{
private readonly IDeviceService _deviceService;
private readonly IDialogService _dialogService;
private readonly IRgbService _rgbService;
private ArtemisLed _selectedLed;
public DeviceDialogViewModel(ArtemisDevice device, IDeviceService deviceService, IRgbService rgbService, IDialogService dialogService, IDeviceDebugVmFactory factory)
{
_deviceService = deviceService;
_rgbService = rgbService;
_dialogService = dialogService;
Device = device;
PanZoomViewModel = new PanZoomViewModel();
Items.Add(factory.DevicePropertiesTabViewModel(device));
Items.Add(factory.DeviceInfoTabViewModel(device));
Items.Add(factory.DeviceLedsTabViewModel(device));
ActiveItem = Items.First();
DisplayName = $"{device.RgbDevice.DeviceInfo.Model} | Artemis";
}
public ArtemisDevice Device { get; }
public PanZoomViewModel PanZoomViewModel { get; }
public ArtemisLed SelectedLed
{
get => _selectedLed;
set
{
if (!SetAndNotify(ref _selectedLed, value)) return;
NotifyOfPropertyChange(nameof(SelectedLeds));
}
}
public List<ArtemisLed> SelectedLeds => SelectedLed != null ? new List<ArtemisLed> { SelectedLed } : null;
public bool CanOpenImageDirectory => Device.Layout?.Image != null;
// ReSharper disable UnusedMember.Global
#region Command handlers
public void ClearSelection()
{
SelectedLed = null;
}
public void IdentifyDevice()
{
_deviceService.IdentifyDevice(Device);
}
public void OpenPluginDirectory()
{
try
{
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Device.DeviceProvider.Plugin.Directory.FullName);
}
catch (Exception e)
{
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
}
}
public void OpenImageDirectory()
{
if (!CanOpenImageDirectory)
return;
try
{
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.GetDirectoryName(Device.Layout.Image.AbsolutePath));
}
catch (Exception e)
{
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's image folder for you", e);
}
}
public void ReloadLayout()
{
_rgbService.ApplyBestDeviceLayout(Device);
}
public void ExportLayout()
{
if (Device.Layout == null)
return;
VistaFolderBrowserDialog dialog = new()
{
Description = "Select layout export target folder",
UseDescriptionForTitle = true,
ShowNewFolderButton = true,
SelectedPath = Path.Combine(Constants.DataFolder, "user layouts")
};
bool? result = dialog.ShowDialog();
if (result != true)
return;
string directory = Path.Combine(
dialog.SelectedPath,
Device.RgbDevice.DeviceInfo.Manufacturer,
Device.RgbDevice.DeviceInfo.DeviceType.ToString()
);
string filePath = Path.Combine(directory, Device.GetLayoutFileName());
Core.Utilities.CreateAccessibleDirectory(directory);
// XML
XmlSerializer serializer = new(typeof(DeviceLayout));
using StreamWriter writer = new(filePath);
serializer.Serialize(writer, Device.Layout!.RgbLayout);
// Device images
if (!Uri.IsWellFormedUriString(Device.Layout.FilePath, UriKind.Absolute))
return;
Uri targetDirectory = new(directory + "/", UriKind.Absolute);
Uri sourceDirectory = new(Path.GetDirectoryName(Device.Layout.FilePath)! + "/", UriKind.Absolute);
Uri deviceImageTarget = new(targetDirectory, Device.Layout.LayoutCustomDeviceData.DeviceImage);
// Create folder (if needed) and copy image
Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(deviceImageTarget.LocalPath)!);
if (Device.Layout.Image != null && File.Exists(Device.Layout.Image.LocalPath) && !File.Exists(deviceImageTarget.LocalPath))
File.Copy(Device.Layout.Image.LocalPath, deviceImageTarget.LocalPath);
foreach (ArtemisLedLayout ledLayout in Device.Layout.Leds)
{
if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
continue;
// Only the image of the current logical layout is available as an URI, iterate each layout and find the images manually
foreach (LayoutCustomLedDataLogicalLayout logicalLayout in ledLayout.LayoutCustomLedData.LogicalLayouts)
{
Uri image = new(sourceDirectory, logicalLayout.Image);
Uri imageTarget = new(targetDirectory, logicalLayout.Image);
// Create folder (if needed) and copy image
Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(imageTarget.LocalPath)!);
if (File.Exists(image.LocalPath) && !File.Exists(imageTarget.LocalPath))
File.Copy(image.LocalPath, imageTarget.LocalPath);
}
}
}
#endregion
// ReSharper restore UnusedMember.Global
}
}
@@ -0,0 +1,121 @@
<UserControl x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DeviceInfoTabView"
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:tabs="clr-namespace:Artemis.UI.Screens.Settings.Device.Tabs"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance tabs:DeviceInfoTabViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- First row -->
<StackPanel HorizontalAlignment="Stretch" Grid.Column="0" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Top" Margin="15 15 7.5 0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Device name</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Manufacturer</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Device type</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.RgbDevice.DeviceInfo.DeviceType}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
<StackPanel Visibility="{Binding IsKeyboard, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Physical layout</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.PhysicalLayout}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
</StackPanel>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Top" Margin="7.5 15 15 0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Size (1px = 1mm)</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.RgbDevice.Size}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Location (1px = 1mm)</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.RgbDevice.Location}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Rotation (degrees)</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.RgbDevice.Rotation.Degrees}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
<StackPanel Visibility="{Binding IsKeyboard, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Logical layout</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.LogicalLayout}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
</StackPanel>
</StackPanel>
<!-- Second row -->
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="15 0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Layout file path</TextBlock>
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Copy path to clipboard" Width="24" Height="24">
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
</Button>
</Grid>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.Layout.FilePath}" />
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Image file path</TextBlock>
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Copy path to clipboard" Width="24" Height="24">
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
</Button>
</Grid>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
TextWrapping="Wrap"
Text="{Binding Device.Layout.Image}" />
</StackPanel>
</Grid>
</UserControl>
@@ -0,0 +1,18 @@
using Artemis.Core;
using RGB.NET.Core;
using Stylet;
namespace Artemis.UI.Screens.Settings.Device.Tabs
{
public class DeviceInfoTabViewModel : Screen
{
public DeviceInfoTabViewModel(ArtemisDevice device)
{
Device = device;
DisplayName = "INFO";
}
public bool IsKeyboard => Device.RgbDevice is IKeyboard;
public ArtemisDevice Device { get; }
}
}
@@ -0,0 +1,37 @@
<UserControl
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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Settings.Device.Tabs"
xmlns:Converters="clr-namespace:Artemis.UI.Converters" x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DeviceLedsTabView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type tabs:DeviceLedsTabViewModel}}">
<UserControl.Resources>
<Converters:UriToFileNameConverter x:Key="UriToFileNameConverter"/>
</UserControl.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Device.Leds}"
d:DataContext="{d:DesignInstance Type={x:Type core:ArtemisLed}}"
CanUserSortColumns="True"
IsReadOnly="True"
CanUserAddRows="False"
AutoGenerateColumns="False"
materialDesign:DataGridAssist.CellPadding="13 8 8 8"
materialDesign:DataGridAssist.ColumnHeaderPadding="8"
SelectedItem="{Binding Parent.SelectedLed}"
CanUserResizeRows="False"
Margin="10">
<DataGrid.Columns>
<materialDesign:DataGridTextColumn Binding="{Binding RgbLed.Id}" Header="LED ID" Width="Auto" />
<materialDesign:DataGridTextColumn Binding="{Binding RgbLed.Color}" Header="Color (ARGB)" Width="Auto" />
<materialDesign:DataGridTextColumn Binding="{Binding Layout.Image, Converter={StaticResource UriToFileNameConverter}, Mode=OneWay}" Header="Image file" />
<materialDesign:DataGridTextColumn Binding="{Binding RgbLed.Shape}" Header="Shape" />
<materialDesign:DataGridTextColumn Binding="{Binding RgbLed.Size}" Header="Size" Width="Auto" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
@@ -0,0 +1,17 @@
using Artemis.Core;
using Stylet;
namespace Artemis.UI.Screens.Settings.Device.Tabs
{
public class DeviceLedsTabViewModel : Screen
{
public DeviceLedsTabViewModel(ArtemisDevice device)
{
Device = device;
DisplayName = "LEDS";
}
public ArtemisDevice Device { get; }
}
}
@@ -0,0 +1,189 @@
<UserControl x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DevicePropertiesTabView"
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.Screens.Settings.Device.Tabs"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:DevicePropertiesTabViewModel}">
<UserControl.Resources>
<shared:SKColorToColorConverter x:Key="SKColorToColorConverter" />
</UserControl.Resources>
<!-- Body -->
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="40"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
Surface properties
</TextBlock>
<TextBox materialDesign:HintAssist.Hint="X-coordinate"
materialDesign:TextFieldAssist.SuffixText="mm"
Text="{Binding X, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5" />
<TextBox materialDesign:HintAssist.Hint="Y-coordinate"
materialDesign:TextFieldAssist.SuffixText="mm"
Text="{Binding Y, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5" />
<TextBox materialDesign:HintAssist.Hint="Scale"
materialDesign:TextFieldAssist.SuffixText="times"
Text="{Binding Scale, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5" />
<TextBox materialDesign:HintAssist.Hint="Rotation"
materialDesign:TextFieldAssist.SuffixText="deg"
Text="{Binding Rotation, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5 0 12" />
</StackPanel>
<Rectangle Grid.Column="1" VerticalAlignment="Stretch" Fill="{StaticResource MaterialDesignTextBoxBorder}" Width="1" Margin="0 0 0 5" />
<StackPanel Grid.Column="2">
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
Color calibration
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}"
TextWrapping="Wrap"
TextAlignment="Justify">
Use the sliders below to adjust the colors of your device so that it matches your other devices.
</TextBlock>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="R" VerticalAlignment="Center" />
<Slider Grid.Column="1"
Minimum="0"
Maximum="200"
ValueChanged="{s:Action ApplyScaling}"
Value="{Binding RedScale, UpdateSourceTrigger=PropertyChanged}"
Margin="10"
VerticalAlignment="Center" />
<TextBox Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding RedScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
materialDesign:TextFieldAssist.SuffixText="%"
Width="50" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="G" VerticalAlignment="Center" />
<Slider Grid.Column="1"
Minimum="0"
Maximum="200"
ValueChanged="{s:Action ApplyScaling}"
Value="{Binding GreenScale, UpdateSourceTrigger=PropertyChanged}"
Margin="10"
VerticalAlignment="Center" />
<TextBox Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding GreenScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
materialDesign:TextFieldAssist.SuffixText="%"
Width="50" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="B" VerticalAlignment="Center" />
<Slider Grid.Column="1"
Minimum="0"
Maximum="200"
ValueChanged="{s:Action ApplyScaling}"
Value="{Binding BlueScale, UpdateSourceTrigger=PropertyChanged}"
Margin="10"
Ticks="100"
VerticalAlignment="Center" />
<TextBox Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding BlueScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
materialDesign:TextFieldAssist.SuffixText="%"
Width="50" />
</Grid>
<Grid Margin="0 28 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0"
IsChecked="{Binding DisplayOnDevices}"
Content="Show preview"
VerticalAlignment="Center" />
<shared:ColorPicker Grid.Column="1"
Margin="0,0,5,0"
HorizontalAlignment="Right"
Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
VerticalAlignment="Center" />
</Grid>
</StackPanel>
</Grid>
<!-- Layout -->
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="0 25 0 0">
Custom layout
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}"
TextWrapping="Wrap"
TextAlignment="Justify">
Select a custom layout below if you want to change the appearance and/or LEDs of this device.
</TextBlock>
<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Text="{Binding Device.CustomLayoutPath}"
VerticalAlignment="Center"
materialDesign:TextFieldAssist.HasClearButton="True"
IsReadOnly="True"
PreviewMouseLeftButtonUp="{s:Action BrowseCustomLayout}">
<materialDesign:HintAssist.Hint>
<StackPanel Orientation="Horizontal" Margin="-2 0 0 0">
<materialDesign:PackIcon Kind="Xml" Width="20" />
<TextBlock>Layout path</TextBlock>
</StackPanel>
</materialDesign:HintAssist.Hint>
</TextBox>
</StackPanel>
<!-- Buttons -->
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignOutlinedButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Reset}">
RESET
</Button>
<Button Style="{StaticResource MaterialDesignRaisedButton}" IsDefault="True" Margin="0 8 8 0" Command="{s:Action Apply}">
APPLY
</Button>
</StackPanel>
</Grid>
</UserControl>
@@ -0,0 +1,197 @@
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services;
using Ookii.Dialogs.Wpf;
using SkiaSharp;
using Stylet;
namespace Artemis.UI.Screens.Settings.Device.Tabs
{
public class DevicePropertiesTabViewModel : Screen
{
private readonly ICoreService _coreService;
private readonly IMessageService _messageService;
private readonly IRgbService _rgbService;
private double _blueScale;
private SKColor _currentColor;
private bool _displayOnDevices;
private double _greenScale;
private double _initialBlueScale;
private double _initialGreenScale;
private double _initialRedScale;
private double _redScale;
private int _rotation;
private double _scale;
private int _x;
private int _y;
public DevicePropertiesTabViewModel(ArtemisDevice device,
ICoreService coreService,
IRgbService rgbService,
IMessageService messageService,
IModelValidator<DevicePropertiesTabViewModel> validator) : base(validator)
{
_coreService = coreService;
_rgbService = rgbService;
_messageService = messageService;
Device = device;
DisplayName = "PROPERTIES";
}
public ArtemisDevice Device { get; }
public int X
{
get => _x;
set => SetAndNotify(ref _x, value);
}
public int Y
{
get => _y;
set => SetAndNotify(ref _y, value);
}
public double Scale
{
get => _scale;
set => SetAndNotify(ref _scale, value);
}
public int Rotation
{
get => _rotation;
set => SetAndNotify(ref _rotation, value);
}
public double RedScale
{
get => _redScale;
set => SetAndNotify(ref _redScale, value);
}
public double GreenScale
{
get => _greenScale;
set => SetAndNotify(ref _greenScale, value);
}
public double BlueScale
{
get => _blueScale;
set => SetAndNotify(ref _blueScale, value);
}
public SKColor CurrentColor
{
get => _currentColor;
set => SetAndNotify(ref _currentColor, value);
}
public bool DisplayOnDevices
{
get => _displayOnDevices;
set => SetAndNotify(ref _displayOnDevices, value);
}
public void ApplyScaling()
{
Device.RedScale = RedScale / 100d;
Device.GreenScale = GreenScale / 100d;
Device.BlueScale = BlueScale / 100d;
}
public void BrowseCustomLayout(object sender, MouseEventArgs e)
{
if (e.OriginalSource is Button)
{
Device.CustomLayoutPath = null;
_messageService.ShowMessage("Cleared imported layout");
return;
}
VistaOpenFileDialog dialog = new();
dialog.Filter = "Layout files (*.xml)|*.xml";
dialog.Title = "Select device layout file";
bool? result = dialog.ShowDialog();
if (result == true)
{
Device.CustomLayoutPath = dialog.FileName;
_messageService.ShowMessage($"Imported layout from {dialog.FileName}");
}
}
public async Task Apply()
{
await ValidateAsync();
if (HasErrors)
return;
_coreService.ModuleRenderingDisabled = true;
await Task.Delay(100);
Device.X = X;
Device.Y = Y;
Device.Scale = Scale;
Device.Rotation = Rotation;
Device.RedScale = RedScale / 100d;
Device.GreenScale = GreenScale / 100d;
Device.BlueScale = BlueScale / 100d;
_coreService.ModuleRenderingDisabled = false;
}
public void Reset()
{
Device.RedScale = _initialRedScale;
Device.GreenScale = _initialGreenScale;
Device.BlueScale = _initialBlueScale;
}
protected override void OnActivate()
{
X = (int) Device.X;
Y = (int) Device.Y;
Scale = Device.Scale;
Rotation = (int) Device.Rotation;
RedScale = Device.RedScale * 100d;
GreenScale = Device.GreenScale * 100d;
BlueScale = Device.BlueScale * 100d;
//we need to store the initial values to be able to restore them when the user clicks "Cancel"
_initialRedScale = Device.RedScale;
_initialGreenScale = Device.GreenScale;
_initialBlueScale = Device.BlueScale;
CurrentColor = SKColors.White;
_coreService.FrameRendering += OnFrameRendering;
Device.PropertyChanged += DeviceOnPropertyChanged;
base.OnActivate();
}
#region Event handlers
private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Device.CustomLayoutPath)) _rgbService.ApplyBestDeviceLayout(Device);
}
private void OnFrameRendering(object sender, FrameRenderingEventArgs e)
{
if (!_displayOnDevices)
return;
using SKPaint overlayPaint = new()
{
Color = CurrentColor
};
e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint);
}
#endregion
}
}
@@ -0,0 +1,19 @@
using FluentValidation;
namespace Artemis.UI.Screens.Settings.Device.Tabs
{
public class DevicePropertiesTabViewModelValidator : AbstractValidator<DevicePropertiesTabViewModel>
{
public DevicePropertiesTabViewModelValidator()
{
RuleFor(m => m.X).GreaterThanOrEqualTo(0).WithMessage("X-coordinate must be 0 or greater");
RuleFor(m => m.Y).GreaterThanOrEqualTo(0).WithMessage("Y-coordinate must be 0 or greater");
RuleFor(m => m.Scale).GreaterThanOrEqualTo(0.2f).WithMessage("Scale must be 0.2 or greater");
RuleFor(m => m.Rotation).GreaterThanOrEqualTo(0).WithMessage("Rotation must be 0 or greater");
RuleFor(m => m.Rotation).LessThanOrEqualTo(359).WithMessage("Rotation must be 359 or less");
}
}
}