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

Merge branch 'development'

This commit is contained in:
Robert 2023-06-22 23:16:56 +02:00
commit 79309dcee7
30 changed files with 698 additions and 585 deletions

View File

@ -1,9 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Events; using Artemis.UI.Shared.Events;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
@ -13,6 +14,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Threading; using Avalonia.Threading;
using RGB.NET.Core; using RGB.NET.Core;
using DryIoc;
using Color = RGB.NET.Core.Color; using Color = RGB.NET.Core.Color;
using Point = Avalonia.Point; using Point = Avalonia.Point;
using Size = Avalonia.Size; using Size = Avalonia.Size;
@ -24,20 +26,19 @@ namespace Artemis.UI.Shared;
/// </summary> /// </summary>
public class DeviceVisualizer : Control public class DeviceVisualizer : Control
{ {
private const double UPDATE_FRAME_RATE = 25.0; private readonly ICoreService _coreService;
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds; private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
private readonly DispatcherTimer _timer;
private Rect _deviceBounds; private Rect _deviceBounds;
private RenderTargetBitmap? _deviceImage; private RenderTargetBitmap? _deviceImage;
private ArtemisDevice? _oldDevice; private ArtemisDevice? _oldDevice;
private bool _loading; private bool _loading;
private Color[] _previousState = Array.Empty<Color>(); private Color[] _previousState = Array.Empty<Color>();
/// <inheritdoc /> /// <inheritdoc />
public DeviceVisualizer() public DeviceVisualizer()
{ {
_timer = new DispatcherTimer(DispatcherPriority.Background) {Interval = TimeSpan.FromMilliseconds(1000.0 / UPDATE_FRAME_RATE)}; _coreService = UI.Locator.Resolve<ICoreService>();
_deviceVisualizerLeds = new List<DeviceVisualizerLed>(); _deviceVisualizerLeds = new List<DeviceVisualizerLed>();
PointerReleased += OnPointerReleased; PointerReleased += OnPointerReleased;
@ -120,23 +121,26 @@ public class DeviceVisualizer : Control
if (Device == null) if (Device == null)
return false; return false;
Color[] state = new Color[Device.RgbDevice.Count()]; bool difference = false;
bool difference = _previousState.Length != state.Length;
int newLedCount = Device.RgbDevice.Count();
if (_previousState.Length != newLedCount)
{
_previousState = new Color[newLedCount];
difference = true;
}
// Check all LEDs for differences and copy the colors to a new state // Check all LEDs for differences and copy the colors to a new state
int index = 0; int index = 0;
foreach (Led led in Device.RgbDevice) foreach (Led led in Device.RgbDevice)
{ {
if (!difference && !led.Color.Equals(_previousState[index])) if (_previousState[index] != led.Color)
difference = true; difference = true;
state[index] = led.Color; _previousState[index] = led.Color;
index++; index++;
} }
// Store the new state for next time
_previousState = state;
return difference; return difference;
} }
@ -156,11 +160,14 @@ public class DeviceVisualizer : Control
return geometry.Bounds; return geometry.Bounds;
} }
private void TimerOnTick(object? sender, EventArgs e) private void OnFrameRendered(object? sender, FrameRenderedEventArgs e)
{ {
if (IsDirty() && ShowColors && IsVisible && Opacity > 0) Dispatcher.UIThread.Post(() =>
Update(); {
if (ShowColors && IsVisible && Opacity > 0 && IsDirty())
Update();
}, DispatcherPriority.Background);
} }
private void OnPointerReleased(object? sender, PointerReleasedEventArgs e) private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
@ -250,16 +257,16 @@ public class DeviceVisualizer : Control
/// <inheritdoc /> /// <inheritdoc />
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
_timer.Start(); _coreService.FrameRendered += OnFrameRendered;
_timer.Tick += TimerOnTick;
base.OnAttachedToLogicalTree(e); base.OnAttachedToLogicalTree(e);
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
_timer.Stop(); _coreService.FrameRendered -= OnFrameRendered;
_timer.Tick -= TimerOnTick;
base.OnDetachedFromLogicalTree(e); base.OnDetachedFromLogicalTree(e);
} }

View File

@ -10,6 +10,15 @@
<TextBlock>I'm in a panel yo!</TextBlock> <TextBlock>I'm in a panel yo!</TextBlock>
<Border Classes="card-separator" /> <Border Classes="card-separator" />
<TextBlock>I'm in a panel yo!</TextBlock> <TextBlock>I'm in a panel yo!</TextBlock>
<Border Classes="card-separator-slim" />
<TextBlock>I'm in a panel yo!</TextBlock>
</StackPanel>
</Border>
<Border Classes="card" Margin="20">
<StackPanel Orientation="Horizontal">
<TextBlock>I'm in a panel yo!</TextBlock>
<Border Classes="card-separator-vertical" />
<TextBlock>I'm in a panel yo!</TextBlock>
</StackPanel> </StackPanel>
</Border> </Border>
</StackPanel> </StackPanel>
@ -50,4 +59,23 @@
<Setter Property="Height" Value="1" /> <Setter Property="Height" Value="1" />
</Style> </Style>
<Style Selector="Border.card-separator-slim">
<Setter Property="Background" Value="{DynamicResource ButtonBorderBrush}" />
<Setter Property="Margin" Value="-12 10" />
<Setter Property="Height" Value="1" />
</Style>
<Style Selector="Border.card-separator-vertical">
<Setter Property="Background" Value="{DynamicResource ButtonBorderBrush}" />
<Setter Property="Margin" Value="15 -12" />
<Setter Property="Width" Value="1" />
</Style>
<Style Selector="Border.card-separator-slim-vertical">
<Setter Property="Background" Value="{DynamicResource ButtonBorderBrush}" />
<Setter Property="Margin" Value="10 -12" />
<Setter Property="Width" Value="1" />
</Style>
</Styles> </Styles>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows10.0.17763.0</TargetFramework> <TargetFramework>net7.0-windows10.0.17763.0</TargetFramework>
@ -29,7 +29,7 @@
<PackageReference Include="Avalonia.Win32" Version="11.0.0-rc1.1" /> <PackageReference Include="Avalonia.Win32" Version="11.0.0-rc1.1" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" /> <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.Win32" Version="2.0.1" /> <PackageReference Include="Microsoft.Win32" Version="2.0.1" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" /> <PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.3" />
<PackageReference Include="RawInput.Sharp" Version="0.1.1" /> <PackageReference Include="RawInput.Sharp" Version="0.1.1" />
<PackageReference Include="ReactiveUI" Version="18.4.26" /> <PackageReference Include="ReactiveUI" Version="18.4.26" />
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.3" /> <PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.3" />

View File

@ -41,27 +41,4 @@
<ItemGroup> <ItemGroup>
<AvaloniaResource Include="Assets\**" /> <AvaloniaResource Include="Assets\**" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="Screens\Settings\Tabs\ReleasesTabView.axaml.cs">
<DependentUpon>UpdatingTabView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Screens\Settings\Updating\ReleaseView.axaml.cs">
<DependentUpon>UpdatingTabView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Screens\Plugins\Features\PluginFeatureView.axaml.cs">
<DependentUpon>PluginFeatureView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Screens\Plugins\Prerequisites\PluginPrerequisiteActionView.axaml.cs">
<DependentUpon>PluginPrerequisiteActionView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Screens\Plugins\Prerequisites\PluginPrerequisiteView.axaml.cs">
<DependentUpon>PluginPrerequisiteView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project> </Project>

View File

@ -25,6 +25,7 @@ using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.VisualScripting; using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Screens.VisualScripting.Pins; using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Shared;
using DryIoc; using DryIoc;
using ReactiveUI; using ReactiveUI;
@ -39,10 +40,10 @@ public interface IDeviceVmFactory : IVmFactory
DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device); DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device);
DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel); DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel);
DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device); DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device);
DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device); DeviceLayoutTabViewModel DeviceLayoutTabViewModel(ArtemisDevice device);
DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device);
DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds); DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds); InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
DeviceGeneralTabViewModel DeviceGeneralTabViewModel(ArtemisDevice device);
} }
public class DeviceFactory : IDeviceVmFactory public class DeviceFactory : IDeviceVmFactory
{ {
@ -68,16 +69,11 @@ public class DeviceFactory : IDeviceVmFactory
return _container.Resolve<DeviceDetectInputViewModel>(new object[] { device }); return _container.Resolve<DeviceDetectInputViewModel>(new object[] { device });
} }
public DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device) public DeviceLayoutTabViewModel DeviceLayoutTabViewModel(ArtemisDevice device)
{ {
return _container.Resolve<DevicePropertiesTabViewModel>(new object[] { device }); return _container.Resolve<DeviceLayoutTabViewModel>(new object[] { device });
} }
public DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device)
{
return _container.Resolve<DeviceInfoTabViewModel>(new object[] { device });
}
public DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds) public DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds)
{ {
return _container.Resolve<DeviceLedsTabViewModel>(new object[] { device, selectedLeds }); return _container.Resolve<DeviceLedsTabViewModel>(new object[] { device, selectedLeds });
@ -87,6 +83,11 @@ public class DeviceFactory : IDeviceVmFactory
{ {
return _container.Resolve<InputMappingsTabViewModel>(new object[] { device, selectedLeds }); return _container.Resolve<InputMappingsTabViewModel>(new object[] { device, selectedLeds });
} }
public DeviceGeneralTabViewModel DeviceGeneralTabViewModel(ArtemisDevice device)
{
return _container.Resolve<DeviceGeneralTabViewModel>(new object[] { device });
}
} }
public interface ISettingsVmFactory : IVmFactory public interface ISettingsVmFactory : IVmFactory

View File

@ -17,7 +17,7 @@
<TextBlock TextWrapping="Wrap" Classes="subtitle" Margin="0 10"> <TextBlock TextWrapping="Wrap" Classes="subtitle" Margin="0 10">
These performance stats are rather basic, for advanced performance profiling check out the wiki. These performance stats are rather basic, for advanced performance profiling check out the wiki.
</TextBlock> </TextBlock>
<controls:HyperlinkButton Grid.Column="1" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins/profiling"> <controls:HyperlinkButton Grid.Column="1" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins/profiling?mtm_campaign=artemis&amp;mtm_kwd=debugger">
JetBrains Profiling Guide JetBrains Profiling Guide
</controls:HyperlinkButton> </controls:HyperlinkButton>
</Grid> </Grid>

View File

@ -1,58 +1,59 @@
<windowing:AppWindow xmlns="https://github.com/avaloniaui" <windowing:AppWindow xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device" xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia" xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800" mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Device.DevicePropertiesView" x:Class="Artemis.UI.Screens.Device.DevicePropertiesView"
x:DataType="device:DevicePropertiesViewModel" x:DataType="device:DevicePropertiesViewModel"
Icon="/Assets/Images/Logo/application.ico" Icon="/Assets/Images/Logo/application.ico"
Title="Artemis | Device Properties" Title="Artemis | Device Properties"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
Width="1250" Width="1400"
Height="900"> Height="800">
<windowing:AppWindow.KeyBindings> <windowing:AppWindow.KeyBindings>
<KeyBinding Gesture="Escape" Command="{CompiledBinding ClearSelectedLeds}" /> <KeyBinding Gesture="Escape" Command="{CompiledBinding ClearSelectedLeds}" />
</windowing:AppWindow.KeyBindings> </windowing:AppWindow.KeyBindings>
<Grid ColumnDefinitions="*,0,1.5*"> <Grid ColumnDefinitions="*,0,1.5*">
<Grid.Background> <Border Grid.Column="0" Classes="card" Margin="5">
<VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,25,25"> <Border.Background>
<VisualBrush.Visual> <VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,25,25">
<Grid Width="25" Height="25" RowDefinitions="*,*" ColumnDefinitions="*,*"> <VisualBrush.Visual>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" /> <Grid Width="25" Height="25" RowDefinitions="*,*" ColumnDefinitions="*,*">
<Rectangle Grid.Row="0" Grid.Column="1" /> <Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" />
<Rectangle Grid.Row="1" Grid.Column="0" /> <Rectangle Grid.Row="0" Grid.Column="1" />
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" /> <Rectangle Grid.Row="1" Grid.Column="0" />
</Grid> <Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" />
</VisualBrush.Visual> </Grid>
</VisualBrush> </VisualBrush.Visual>
</Grid.Background> </VisualBrush>
<Grid Grid.Column="0" Name="DeviceDisplayGrid" PointerReleased="DeviceDisplayGrid_OnPointerReleased"> </Border.Background>
<shared:DeviceVisualizer Device="{CompiledBinding Device}" <Grid Name="DeviceDisplayGrid" PointerReleased="DeviceDisplayGrid_OnPointerReleased">
HorizontalAlignment="Center" <shared:DeviceVisualizer Device="{CompiledBinding Device}"
VerticalAlignment="Center" HorizontalAlignment="Center"
ShowColors="True" VerticalAlignment="Center"
Margin="20" ShowColors="True"
RenderOptions.BitmapInterpolationMode="MediumQuality" Margin="5"
LedClicked="DeviceVisualizer_OnLedClicked" RenderOptions.BitmapInterpolationMode="MediumQuality"
Clicked="DeviceVisualizer_OnClicked" /> LedClicked="DeviceVisualizer_OnLedClicked"
Clicked="DeviceVisualizer_OnClicked" />
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="15" IsVisible="{CompiledBinding Device.Layout.RgbLayout.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
IsVisible="{CompiledBinding Device.Layout.RgbLayout.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"> <TextBlock Classes="h5" Text="Device layout by " />
<TextBlock Classes="h5" Text="Device layout by " /> <TextBlock Classes="h5" FontWeight="Bold" Text="{CompiledBinding Device.Layout.RgbLayout.Author}" />
<TextBlock Classes="h5" FontWeight="Bold" Text="{CompiledBinding Device.Layout.RgbLayout.Author}" /> </StackPanel>
</StackPanel> </Grid>
</Grid> </Border>
<GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" /> <GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" />
<Border Grid.Column="2" Classes="card-condensed" CornerRadius="10 0 0 0" Margin="0 10 0 0" Background="#ff323232"> <Border Grid.Column="2" Classes="card-condensed" Margin="5">
<Panel> <Panel>
<TabControl ItemsSource="{CompiledBinding Tabs}" IsVisible="{CompiledBinding Tabs.Count}" Padding="12"> <TabControl ItemsSource="{CompiledBinding Tabs}" IsVisible="{CompiledBinding Tabs.Count}" Padding="12">
<TabControl.ItemTemplate> <TabControl.ItemTemplate>
@ -69,6 +70,6 @@
</Border> </Border>
<StackPanel Grid.Column="0" Grid.ColumnSpan="3" Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" /> <StackPanel Grid.Column="0" Grid.ColumnSpan="3" Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Top" HorizontalAlignment="Right" />
</Grid> </Grid>
</windowing:AppWindow> </windowing:AppWindow>

View File

@ -64,8 +64,8 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
private void AddTabs() private void AddTabs()
{ {
Tabs.Add(_deviceVmFactory.DevicePropertiesTabViewModel(Device)); Tabs.Add(_deviceVmFactory.DeviceGeneralTabViewModel(Device));
Tabs.Add(_deviceVmFactory.DeviceInfoTabViewModel(Device)); Tabs.Add(_deviceVmFactory.DeviceLayoutTabViewModel(Device));
if (Device.DeviceType == RGBDeviceType.Keyboard) if (Device.DeviceType == RGBDeviceType.Keyboard)
Tabs.Add(_deviceVmFactory.InputMappingsTabViewModel(Device, SelectedLeds)); Tabs.Add(_deviceVmFactory.InputMappingsTabViewModel(Device, SelectedLeds));
Tabs.Add(_deviceVmFactory.DeviceLedsTabViewModel(Device, SelectedLeds)); Tabs.Add(_deviceVmFactory.DeviceLedsTabViewModel(Device, SelectedLeds));

View File

@ -0,0 +1,210 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="650"
x:Class="Artemis.UI.Screens.Device.DeviceGeneralTabView"
x:DataType="device:DeviceGeneralTabViewModel">
<UserControl.Resources>
<converters:SKColorToColorConverter x:Key="SKColorToColorConverter" />
</UserControl.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<Grid RowDefinitions="*,Auto">
<StackPanel Grid.Row="0" Orientation="Vertical">
<!-- Device information and categories -->
<Grid ColumnDefinitions="*,*" RowDefinitions="*,*">
<TextBlock Grid.Column="0" Grid.Row="0" Classes="card-title" Text="Information" Margin="10,0,0,0" />
<Border Grid.Column="0" Grid.Row="1" Classes="card" Margin="5" x:Name="InformationBorder">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,*,*,*,*,*">
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Text="Model" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.Model}" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBlock Text="Manufacturer" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.Manufacturer}" />
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0">
<TextBlock Text="Device Type" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.DeviceType}" />
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="0">
<TextBlock Text="Size (1px = 1mm)" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.Size}" />
</StackPanel>
<StackPanel Grid.Row="4" Grid.Column="0" IsVisible="{CompiledBinding IsKeyboard}">
<TextBlock Text="Physical Layout" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding Device.PhysicalLayout}" />
</StackPanel>
<StackPanel Grid.Row="5" Grid.Column="0" IsVisible="{CompiledBinding IsKeyboard}" >
<TextBlock Text="Logical Layout" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding Device.LogicalLayout}" />
</StackPanel>
</Grid>
</Border>
<TextBlock Grid.Column="1" Grid.Row="0" Classes="card-title" Text="Categories" Margin="10,0,0,0" />
<Border Grid.Row="1" Grid.Column="1" Classes="card" Margin="5" x:Name="CategoryBorder">
<StackPanel>
<TextBlock TextWrapping="Wrap" Text="Artemis uses categories to determine where the layers of imported profiles are applied to." />
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,*,*,*,*" Margin="0,10,0,0">
<StackPanel Grid.Row="0" Grid.Column="0" Margin="0,0,0,10">
<TextBlock Text="Peripheral" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="A peripheral such as a mouse or keyboard" />
</StackPanel>
<CheckBox Grid.Row="0" Grid.Column="1" IsChecked="{CompiledBinding HasPeripheralsCategory}" />
<StackPanel Grid.Row="1" Grid.Column="0" Margin="0,0,0,10">
<TextBlock Text="Desk" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="A device acting as desk ornamentation such as a LED strip" />
</StackPanel>
<CheckBox Grid.Row="1" Grid.Column="1" IsChecked="{CompiledBinding HasDeskCategory}" />
<StackPanel Grid.Row="2" Grid.Column="0" Margin="0,0,0,10">
<TextBlock Text="Monitor" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="A device attached to the monitor such as ambilight LEDs" />
</StackPanel>
<CheckBox Grid.Row="2" Grid.Column="1" IsChecked="{CompiledBinding HasMonitorCategory}" />
<StackPanel Grid.Row="3" Grid.Column="0" Margin="0,0,0,10">
<TextBlock Text="Case" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="A device inside your computer case" />
</StackPanel>
<CheckBox Grid.Row="3" Grid.Column="1" IsChecked="{CompiledBinding HasCaseCategory}" />
<StackPanel Grid.Row="4" Grid.Column="0" Margin="0,0,0,10">
<TextBlock Text="Room" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="A device elsewhere in the room" />
</StackPanel>
<CheckBox Grid.Row="4" Grid.Column="1" IsChecked="{CompiledBinding HasRoomCategory}" />
</Grid>
</StackPanel>
</Border>
</Grid>
<!-- Surface and Calibration -->
<Grid ColumnDefinitions="*,*" RowDefinitions="*,*">
<TextBlock Grid.Column="0" Grid.Row="0" Classes="card-title" Text="Surface" Margin="10,0,0,0" />
<Border Grid.Column="0" Grid.Row="1" Classes="card" Margin="5" x:Name="SurfaceBorder">
<StackPanel Orientation="Vertical">
<TextBlock TextWrapping="Wrap" Text="The device can be rotated and scaled on the surface with the values below." />
<Grid ColumnDefinitions="*,Auto,Auto" RowDefinitions="*,*,*,*">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">X-coordinate</TextBlock>
<NumericUpDown Grid.Row="0"
Grid.Column="1"
Margin="10 5"
VerticalAlignment="Center"
Value="{CompiledBinding X}" />
<TextBlock Grid.Row="0" Grid.Column="2" VerticalAlignment="Center">mm</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center">Y-coordinate</TextBlock>
<NumericUpDown Grid.Row="1"
Grid.Column="1"
Margin="10 5"
VerticalAlignment="Center"
Value="{CompiledBinding Y}" />
<TextBlock Grid.Row="1" Grid.Column="2" VerticalAlignment="Center">mm</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center">Scale</TextBlock>
<NumericUpDown Grid.Row="2"
Grid.Column="1"
Margin="10 5"
VerticalAlignment="Center"
Increment="0.1"
FormatString="F1"
Value="{CompiledBinding Scale}" />
<TextBlock Grid.Row="2" Grid.Column="2" VerticalAlignment="Center">times</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center">Rotation</TextBlock>
<NumericUpDown Grid.Row="3"
Grid.Column="1"
Margin="10 5"
VerticalAlignment="Center"
Value="{CompiledBinding Rotation}" />
<TextBlock Grid.Row="3" Grid.Column="2" VerticalAlignment="Center">deg</TextBlock>
</Grid>
</StackPanel>
</Border>
<TextBlock Grid.Column="1" Grid.Row="0" Classes="card-title" Text="Calibration" Margin="10,0,0,0" />
<Border Grid.Row="1" Grid.Column="1" Classes="card" Margin="5" x:Name="CalibrationBorder">
<StackPanel Orientation="Vertical">
<TextBlock TextWrapping="Wrap" Text="Use the sliders below to adjust the colors of your device so that it matches your other devices." />
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="*,*,*">
<Label Grid.Row="0" Grid.Column="0" Content="R" VerticalAlignment="Center" />
<Slider Grid.Row="0" Grid.Column="1" Minimum="0" Maximum="200" Value="{CompiledBinding RedScale}" Margin="10 0" VerticalAlignment="Center" />
<NumericUpDown Grid.Row="0"
Grid.Column="2"
VerticalAlignment="Center"
Width="65"
Value="{CompiledBinding RedScale}"
ShowButtonSpinner="False"
FormatString="{}{0:0.0}"
Minimum="0"
Maximum="200"
ClipValueToMinMax="True" />
<Label Grid.Row="1" Grid.Column="0" Content="G" VerticalAlignment="Center" />
<Slider Grid.Row="1" Grid.Column="1" Minimum="0" Maximum="200" Value="{CompiledBinding GreenScale}" Margin="10 0" VerticalAlignment="Center" />
<NumericUpDown Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Center"
Width="65"
Value="{CompiledBinding GreenScale}"
ShowButtonSpinner="False"
FormatString="{}{0:0.0}"
Minimum="0"
Maximum="200"
ClipValueToMinMax="True" />
<Label Grid.Row="2" Grid.Column="0" Content="B" VerticalAlignment="Center" />
<Slider Grid.Row="2" Grid.Column="1" Minimum="0" Maximum="200" Value="{CompiledBinding BlueScale}" Margin="10 0" Ticks="100" VerticalAlignment="Center" />
<NumericUpDown Grid.Row="2"
Grid.Column="2"
VerticalAlignment="Center"
Width="65"
Value="{CompiledBinding BlueScale}"
ShowButtonSpinner="False"
FormatString="{}{0:0.0}"
Minimum="0"
Maximum="200"
ClipValueToMinMax="True" />
</Grid>
<Grid ColumnDefinitions="Auto,*,Auto">
<Button
Grid.Column="0"
Content="Reset"
ToolTip.Tip="Reset the color scaling to what it had previously"
Command="{CompiledBinding ResetScaling}"
HorizontalAlignment="Center"/>
<CheckBox Grid.Column="1" IsChecked="{CompiledBinding DisplayOnDevices}" Content="Preview specific color" VerticalAlignment="Center" HorizontalAlignment="Right" />
<controls:ColorPickerButton Grid.Column="2"
VerticalAlignment="Center"
Color="{CompiledBinding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
ShowAcceptDismissButtons="False" />
</Grid>
</StackPanel>
</Border>
</Grid>
</StackPanel>
<Button Grid.Row="1" Margin="5"
IsVisible="{CompiledBinding RequiresManualSetup}"
Command="{CompiledBinding RestartSetup}"
ToolTip.Tip="Restart device setup, allowing you to select a new physical and logical layout">
Restart setup
</Button>
</Grid>
</ScrollViewer>
</UserControl>

View File

@ -0,0 +1,11 @@
using Avalonia.Controls;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device;
public partial class DeviceGeneralTabView : ReactiveUserControl<DeviceGeneralTabViewModel>
{
public DeviceGeneralTabView()
{
InitializeComponent();
}
}

View File

@ -1,53 +1,57 @@
using System; using Artemis.UI.Shared;
using Artemis.UI.Shared;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using ReactiveUI; using ReactiveUI;
using RGB.NET.Core;
using SkiaSharp; using SkiaSharp;
namespace Artemis.UI.Screens.Device; namespace Artemis.UI.Screens.Device;
public class DevicePropertiesTabViewModel : ActivatableViewModelBase public class DeviceGeneralTabViewModel : ActivatableViewModelBase
{ {
private readonly List<DeviceCategory> _categories;
private readonly ICoreService _coreService; private readonly ICoreService _coreService;
private readonly IRgbService _rgbService;
private readonly IWindowService _windowService;
private readonly List<DeviceCategory> _categories;
private readonly float _initialBlueScale; private readonly float _initialBlueScale;
private readonly float _initialGreenScale; private readonly float _initialGreenScale;
private readonly float _initialRedScale; private readonly float _initialRedScale;
private readonly INotificationService _notificationService;
private readonly IRgbService _rgbService;
private readonly IWindowService _windowService;
private float _blueScale;
private SKColor _currentColor;
private bool _displayOnDevices;
private float _greenScale;
private float _redScale;
private int _rotation; private int _rotation;
private float _scale; private float _scale;
private int _x; private int _x;
private int _y; private int _y;
public DevicePropertiesTabViewModel(ArtemisDevice device, ICoreService coreService, IRgbService rgbService, IWindowService windowService, INotificationService notificationService) private float _redScale;
private float _greenScale;
private float _blueScale;
private SKColor _currentColor;
private bool _displayOnDevices;
public DeviceGeneralTabViewModel(ArtemisDevice device, ICoreService coreService, IRgbService rgbService, IWindowService windowService)
{ {
_coreService = coreService; _coreService = coreService;
_rgbService = rgbService; _rgbService = rgbService;
_windowService = windowService; _windowService = windowService;
_notificationService = notificationService;
_categories = new List<DeviceCategory>(device.Categories); _categories = new List<DeviceCategory>(device.Categories);
Device = device; Device = device;
DisplayName = "Properties"; DisplayName = "General";
X = (int)Device.X;
X = (int) Device.X; Y = (int)Device.Y;
Y = (int) Device.Y;
Scale = Device.Scale; Scale = Device.Scale;
Rotation = (int) Device.Rotation; Rotation = (int)Device.Rotation;
RedScale = Device.RedScale * 100f; RedScale = Device.RedScale * 100f;
GreenScale = Device.GreenScale * 100f; GreenScale = Device.GreenScale * 100f;
BlueScale = Device.BlueScale * 100f; BlueScale = Device.BlueScale * 100f;
@ -69,10 +73,13 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
{ {
_coreService.FrameRendering -= OnFrameRendering; _coreService.FrameRendering -= OnFrameRendering;
Device.PropertyChanged -= DeviceOnPropertyChanged; Device.PropertyChanged -= DeviceOnPropertyChanged;
Apply();
}).DisposeWith(d); }).DisposeWith(d);
}); });
} }
public bool RequiresManualSetup => !Device.DeviceProvider.CanDetectPhysicalLayout || !Device.DeviceProvider.CanDetectLogicalLayout;
public ArtemisDevice Device { get; } public ArtemisDevice Device { get; }
public int X public int X
@ -99,6 +106,38 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _rotation, value); set => RaiseAndSetIfChanged(ref _rotation, value);
} }
public bool IsKeyboard => Device.DeviceType == RGBDeviceType.Keyboard;
public bool HasDeskCategory
{
get => GetCategory(DeviceCategory.Desk);
set => SetCategory(DeviceCategory.Desk, value);
}
public bool HasMonitorCategory
{
get => GetCategory(DeviceCategory.Monitor);
set => SetCategory(DeviceCategory.Monitor, value);
}
public bool HasCaseCategory
{
get => GetCategory(DeviceCategory.Case);
set => SetCategory(DeviceCategory.Case, value);
}
public bool HasRoomCategory
{
get => GetCategory(DeviceCategory.Room);
set => SetCategory(DeviceCategory.Room, value);
}
public bool HasPeripheralsCategory
{
get => GetCategory(DeviceCategory.Peripherals);
set => SetCategory(DeviceCategory.Peripherals, value);
}
public float RedScale public float RedScale
{ {
get => _redScale; get => _redScale;
@ -129,72 +168,19 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _displayOnDevices, value); set => RaiseAndSetIfChanged(ref _displayOnDevices, value);
} }
// This solution won't scale well but I don't expect there to be many more categories. private bool GetCategory(DeviceCategory category)
// If for some reason there will be, dynamically creating a view model per category may be more appropriate
public bool HasDeskCategory
{ {
get => GetCategory(DeviceCategory.Desk); return _categories.Contains(category);
set => SetCategory(DeviceCategory.Desk, value);
} }
public bool HasMonitorCategory private void SetCategory(DeviceCategory category, bool value)
{ {
get => GetCategory(DeviceCategory.Monitor); if (value && !_categories.Contains(category))
set => SetCategory(DeviceCategory.Monitor, value); _categories.Add(category);
} else if (!value)
_categories.Remove(category);
public bool HasCaseCategory this.RaisePropertyChanged($"Has{category}Category");
{
get => GetCategory(DeviceCategory.Case);
set => SetCategory(DeviceCategory.Case, value);
}
public bool HasRoomCategory
{
get => GetCategory(DeviceCategory.Room);
set => SetCategory(DeviceCategory.Room, value);
}
public bool HasPeripheralsCategory
{
get => GetCategory(DeviceCategory.Peripherals);
set => SetCategory(DeviceCategory.Peripherals, value);
}
public bool RequiresManualSetup => !Device.DeviceProvider.CanDetectPhysicalLayout || !Device.DeviceProvider.CanDetectLogicalLayout;
public void ApplyScaling()
{
Device.RedScale = RedScale / 100f;
Device.GreenScale = GreenScale / 100f;
Device.BlueScale = BlueScale / 100f;
_rgbService.FlushLeds = true;
}
public void ClearCustomLayout()
{
Device.CustomLayoutPath = null;
_notificationService.CreateNotification()
.WithMessage("Cleared imported layout.")
.WithSeverity(NotificationSeverity.Informational);
}
public async Task BrowseCustomLayout()
{
string[]? files = await _windowService.CreateOpenFileDialog()
.WithTitle("Select device layout file")
.HavingFilter(f => f.WithName("Layout files").WithExtension("xml"))
.ShowAsync();
if (files?.Length > 0)
{
Device.CustomLayoutPath = files[0];
_notificationService.CreateNotification()
.WithTitle("Imported layout")
.WithMessage($"File loaded from {files[0]}")
.WithSeverity(NotificationSeverity.Informational);
}
} }
public async Task RestartSetup() public async Task RestartSetup()
@ -211,12 +197,12 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
_rgbService.ApplyBestDeviceLayout(Device); _rgbService.ApplyBestDeviceLayout(Device);
} }
public async Task Apply() private void Apply()
{ {
// TODO: Validation // TODO: Validation
_coreService.ProfileRenderingDisabled = true; _coreService.ProfileRenderingDisabled = true;
await Task.Delay(100); Thread.Sleep(100);
Device.X = X; Device.X = X;
Device.Y = Y; Device.Y = Y;
@ -234,40 +220,28 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
_coreService.ProfileRenderingDisabled = false; _coreService.ProfileRenderingDisabled = false;
} }
public void Reset() public void ApplyScaling()
{ {
HasDeskCategory = Device.Categories.Contains(DeviceCategory.Desk); Device.RedScale = RedScale / 100f;
HasMonitorCategory = Device.Categories.Contains(DeviceCategory.Monitor); Device.GreenScale = GreenScale / 100f;
HasCaseCategory = Device.Categories.Contains(DeviceCategory.Case); Device.BlueScale = BlueScale / 100f;
HasRoomCategory = Device.Categories.Contains(DeviceCategory.Room);
HasPeripheralsCategory = Device.Categories.Contains(DeviceCategory.Peripherals);
_rgbService.FlushLeds = true;
}
public void ResetScaling()
{
RedScale = _initialRedScale * 100; RedScale = _initialRedScale * 100;
GreenScale = _initialGreenScale * 100; GreenScale = _initialGreenScale * 100;
BlueScale = _initialBlueScale * 100; BlueScale = _initialBlueScale * 100;
} }
private bool GetCategory(DeviceCategory category)
{
return _categories.Contains(category);
}
private void SetCategory(DeviceCategory category, bool value)
{
if (value && !_categories.Contains(category))
_categories.Add(category);
else if (!value)
_categories.Remove(category);
this.RaisePropertyChanged($"Has{category}Category");
}
private void OnFrameRendering(object? sender, FrameRenderingEventArgs e) private void OnFrameRendering(object? sender, FrameRenderingEventArgs e)
{ {
if (!_displayOnDevices) if (!DisplayOnDevices)
return; return;
using SKPaint overlayPaint = new() {Color = CurrentColor}; using SKPaint overlayPaint = new() { Color = CurrentColor };
e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint); e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint);
} }

View File

@ -1,88 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Device.DeviceInfoTabView"
x:DataType="device:DeviceInfoTabViewModel">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,*" Margin="-5">
<!-- First row -->
<Border Classes="card" Grid.Column="0" Grid.Row="0" Margin="5">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Top">
<TextBlock FontWeight="Bold">Device name</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.DeviceName}" />
<Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Manufacturer</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.DeviceInfo.Manufacturer}" />
<Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Device type</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.DeviceType}" />
<Border Classes="card-separator" IsVisible="{CompiledBinding IsKeyboard}" />
<StackPanel IsVisible="{CompiledBinding IsKeyboard}">
<TextBlock FontWeight="Bold">Physical layout</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.PhysicalLayout}" />
</StackPanel>
</StackPanel>
</Border>
<Border Classes="card" Grid.Row="0" Grid.Column="1" Margin="5">
<StackPanel VerticalAlignment="Top">
<TextBlock FontWeight="Bold">Size (1px = 1mm)</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.Size}" />
<Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Location (1px = 1mm)</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.Location}" />
<Border Classes="card-separator" />
<TextBlock FontWeight="Bold">Rotation (degrees)</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.RgbDevice.Rotation.Degrees}" />
<Border Classes="card-separator" IsVisible="{CompiledBinding IsKeyboard}" />
<StackPanel IsVisible="{CompiledBinding IsKeyboard}">
<TextBlock FontWeight="Bold">Logical layout</TextBlock>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.LogicalLayout}" />
</StackPanel>
</StackPanel>
</Border>
<!-- Second row -->
<Border Classes="card" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="5">
<StackPanel>
<Grid ColumnDefinitions="*,Auto">
<TextBlock FontWeight="Bold">Default layout file path</TextBlock>
<Button Grid.Column="1"
Classes="icon-button"
ToolTip.Tip="Copy path to clipboard"
Click="LayoutPathButton_OnClick">
<avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" />
</Button>
</Grid>
<TextBlock
TextWrapping="Wrap"
Text="{CompiledBinding DefaultLayoutPath}" />
<Border Classes="card-separator" />
<Grid ColumnDefinitions="*,Auto">
<TextBlock FontWeight="Bold">Image file path</TextBlock>
<Button Grid.Column="1"
Classes="icon-button"
ToolTip.Tip="Copy path to clipboard"
Click="ImagePathButton_OnClick">
<avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" />
</Button>
</Grid>
<TextBlock TextWrapping="Wrap" Text="{CompiledBinding Device.Layout.Image.LocalPath}" />
</StackPanel>
</Border>
</Grid>
</ScrollViewer>
</UserControl>

View File

@ -1,29 +0,0 @@
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Avalonia;
using Avalonia.Controls;
using RGB.NET.Core;
namespace Artemis.UI.Screens.Device;
public class DeviceInfoTabViewModel : ActivatableViewModelBase
{
private readonly INotificationService _notificationService;
public DeviceInfoTabViewModel(ArtemisDevice device, INotificationService notificationService)
{
_notificationService = notificationService;
Device = device;
DisplayName = "Info";
DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath;
}
public bool IsKeyboard => Device.DeviceType == RGBDeviceType.Keyboard;
public ArtemisDevice Device { get; }
public string DefaultLayoutPath { get; }
}

View File

@ -0,0 +1,89 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Device.DeviceLayoutTabView"
x:DataType="device:DeviceLayoutTabViewModel">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<Border Classes="card" Margin="5">
<Grid RowDefinitions="*,Auto">
<StackPanel Grid.Row="0">
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Text="Default layout file path" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding DefaultLayoutPath}" />
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<Button
Classes="icon-button"
HorizontalAlignment="Right"
ToolTip.Tip="Copy layout file path to clipboard"
Click="LayoutPathButton_OnClick">
<avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" />
</Button>
</StackPanel>
</Grid>
<Border Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBlock Text="Image file path" />
<TextBlock Classes="subtitle" FontSize="12" TextWrapping="Wrap" Text="{CompiledBinding Device.Layout.Image.LocalPath}" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center">
<Button
Classes="icon-button"
HorizontalAlignment="Right"
ToolTip.Tip="Copy image file path to clipboard"
Click="ImagePathButton_OnClick">
<avalonia:MaterialIcon Kind="ContentCopy" Width="18" Height="18" />
</Button>
</StackPanel>
</Grid>
<Border Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBlock Text="Disable default layout" />
<TextBlock Classes="subtitle" FontSize="12" Text="With this checked, Artemis will not load a layout for this device unless you specifically provide one." />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center">
<CheckBox HorizontalAlignment="Right" Margin="0,0,-10,0" />
</StackPanel>
</Grid>
<Border Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBlock Text="Custom layout path" />
<TextBlock Classes="subtitle" FontSize="12" Text="{CompiledBinding CustomLayoutPath}" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
<Button Content="Clear" Command="{CompiledBinding ClearCustomLayout}" IsEnabled="{CompiledBinding HasCustomLayout}" />
<!-- 5 pixels of margin between the buttons -->
<Button Margin="5,0,0,0" Content="Browse" Command="{CompiledBinding BrowseCustomLayout}" />
</StackPanel>
</Grid>
<Border Classes="card-separator" />
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBlock Text="Export current layout" />
<TextBlock Classes="subtitle" FontSize="12" Text="If there is a layout used, export that. Otherwise, export the LEDs present." />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
<Button HorizontalAlignment="Right" Content="Export" Command="{CompiledBinding ExportLayout}" />
</StackPanel>
</Grid>
</StackPanel>
<controls:HyperlinkButton
Grid.Row="1"
Content="Learn more about layouts on the wiki"
NavigateUri="https://wiki.artemis-rgb.com/en/guides/developer/layouts?mtm_campaign=artemis&amp;mtm_kwd=device-properties"
Margin="0 20"
HorizontalAlignment="Right"
VerticalAlignment="Bottom" />
</Grid>
</Border>
</ScrollViewer>
</UserControl>

View File

@ -1,13 +1,15 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device; namespace Artemis.UI.Screens.Device;
public partial class DeviceInfoTabView : ReactiveUserControl<DeviceInfoTabViewModel> public partial class DeviceLayoutTabView : ReactiveUserControl<DeviceLayoutTabViewModel>
{ {
public DeviceInfoTabView() public DeviceLayoutTabView()
{ {
InitializeComponent(); InitializeComponent();
} }
@ -15,10 +17,12 @@ public partial class DeviceInfoTabView : ReactiveUserControl<DeviceInfoTabViewMo
private void LayoutPathButton_OnClick(object? sender, RoutedEventArgs e) private void LayoutPathButton_OnClick(object? sender, RoutedEventArgs e)
{ {
TopLevel.GetTopLevel(this).Clipboard.SetTextAsync(ViewModel.DefaultLayoutPath); TopLevel.GetTopLevel(this).Clipboard.SetTextAsync(ViewModel.DefaultLayoutPath);
ViewModel.ShowCopiedNotification();
} }
private void ImagePathButton_OnClick(object? sender, RoutedEventArgs e) private void ImagePathButton_OnClick(object? sender, RoutedEventArgs e)
{ {
TopLevel.GetTopLevel(this).Clipboard.SetTextAsync(ViewModel.Device.Layout.Image.LocalPath); TopLevel.GetTopLevel(this).Clipboard.SetTextAsync(ViewModel.Device.Layout.Image.LocalPath);
ViewModel.ShowCopiedNotification();
} }
} }

View File

@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Avalonia.Controls;
using ReactiveUI;
using RGB.NET.Layout;
using SkiaSharp;
namespace Artemis.UI.Screens.Device;
public class DeviceLayoutTabViewModel : ActivatableViewModelBase
{
private readonly IWindowService _windowService;
private readonly INotificationService _notificationService;
public DeviceLayoutTabViewModel(
IWindowService windowService,
INotificationService notificationService,
ArtemisDevice device)
{
_windowService = windowService;
_notificationService = notificationService;
Device = device;
DisplayName = "Layout";
DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath;
}
public ArtemisDevice Device { get; }
public string DefaultLayoutPath { get; }
public string CustomLayoutPath => Device.CustomLayoutPath ?? "None";
public bool HasCustomLayout => Device.CustomLayoutPath != null;
private void RaiseCustomLayoutChanged()
{
this.RaisePropertyChanged(nameof(CustomLayoutPath));
this.RaisePropertyChanged(nameof(HasCustomLayout));
}
public void ClearCustomLayout()
{
Device.CustomLayoutPath = null;
_notificationService.CreateNotification()
.WithMessage("Cleared imported layout.")
.WithSeverity(NotificationSeverity.Informational);
RaiseCustomLayoutChanged();
}
public async Task BrowseCustomLayout()
{
string[]? files = await _windowService.CreateOpenFileDialog()
.WithTitle("Select device layout file")
.HavingFilter(f => f.WithName("Layout files").WithExtension("xml"))
.ShowAsync();
if (files?.Length > 0)
{
Device.CustomLayoutPath = files[0];
_notificationService.CreateNotification()
.WithTitle("Imported layout")
.WithMessage($"File loaded from {files[0]}")
.WithSeverity(NotificationSeverity.Informational);
}
RaiseCustomLayoutChanged();
}
public async Task ExportLayout()
{
string fileName = Device.DeviceProvider.GetDeviceLayoutName(Device);
string layoutDir = Constants.LayoutsFolder;
string filePath = Path.Combine(
layoutDir,
Device.RgbDevice.DeviceInfo.Manufacturer,
Device.DeviceType.ToString(),
fileName
);
if (!Directory.Exists(filePath))
Directory.CreateDirectory(filePath);
string? result = await _windowService.CreateSaveFileDialog()
.HavingFilter(f => f.WithExtension("xml").WithName("Artemis layout"))
.WithDirectory(filePath)
.WithInitialFileName(fileName)
.ShowAsync();
if (result == null)
return;
ArtemisLayout? layout = Device.Layout;
if (layout?.IsValid == true)
{
string path = layout.FilePath;
File.Copy(path, result, true);
}
else
{
List<LedLayout> ledLayouts = Device.Leds.Select(x => new LedLayout()
{
Id = x.RgbLed.Id.ToString(),
DescriptiveX = x.Rectangle.Left.ToString(),
DescriptiveY = x.Rectangle.Top.ToString(),
DescriptiveWidth = $"{x.Rectangle.Width}mm",
DescriptiveHeight = $"{x.Rectangle.Height}mm",
}).ToList();
DeviceLayout emptyLayout = new()
{
Author = "Artemis",
Type = Device.DeviceType,
Vendor = Device.RgbDevice.DeviceInfo.Manufacturer,
Model = Device.RgbDevice.DeviceInfo.Model,
Width = Device.Rectangle.Width,
Height = Device.Rectangle.Height,
InternalLeds = ledLayouts,
};
XmlSerializer serializer = new(typeof(DeviceLayout));
await using StreamWriter writer = new(result);
serializer.Serialize(writer, emptyLayout);
}
_notificationService.CreateNotification()
.WithMessage("Layout exported")
.WithTimeout(TimeSpan.FromSeconds(5))
.WithSeverity(NotificationSeverity.Success)
.Show();
}
public void ShowCopiedNotification()
{
_notificationService.CreateNotification()
.WithTitle("Copied!")
.WithSeverity(NotificationSeverity.Informational)
.Show();
}
}

View File

@ -1,204 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="1200"
x:Class="Artemis.UI.Screens.Device.DevicePropertiesTabView"
x:DataType="device:DevicePropertiesTabViewModel">
<UserControl.Resources>
<converters:SKColorToColorConverter x:Key="SKColorToColorConverter" />
</UserControl.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<!-- Body -->
<Grid RowDefinitions="*,Auto">
<StackPanel Grid.Row="0">
<!-- Layout -->
<TextBlock Classes="h4">
Categories
</TextBlock>
<TextBlock TextWrapping="Wrap">
Artemis uses categories to determine where the layers of imported profiles are applied to.
</TextBlock>
<TextBlock TextWrapping="Wrap">
You can hover over a category for a more detailed description.
</TextBlock>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{CompiledBinding HasDeskCategory}"
ToolTip.Tip="A device acting as desk ornamentation such as a LED strip"
Content="Desk" />
<CheckBox IsChecked="{CompiledBinding HasMonitorCategory}"
ToolTip.Tip="A device attached to the monitor such as ambilight LEDs"
Content="Monitor" />
<CheckBox ToolTip.Tip="A device inside your computer case"
IsChecked="{CompiledBinding HasCaseCategory}"
Content="Case" />
<CheckBox IsChecked="{CompiledBinding HasRoomCategory}"
ToolTip.Tip="A device elsewhere in the room"
Content="Room" />
<CheckBox IsChecked="{CompiledBinding HasPeripheralsCategory}"
ToolTip.Tip="A peripheral such as a mouse or keyboard"
Content="Peripheral" />
</StackPanel>
<Grid Margin="0 25" ColumnDefinitions="*,40,*">
<Grid Grid.Column="0" ColumnDefinitions="*,Auto,Auto" RowDefinitions="*,*,*,*,*">
<TextBlock Grid.Row="0" Grid.ColumnSpan="3" Classes="h4">
Surface properties
</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center">X-coordinate</TextBlock>
<NumericUpDown Grid.Row="1"
Grid.Column="1"
Margin="10 0"
VerticalAlignment="Center"
Value="{CompiledBinding X}" />
<TextBlock Grid.Row="1" Grid.Column="2" VerticalAlignment="Center">mm</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center">Y-coordinate</TextBlock>
<NumericUpDown Grid.Row="2"
Grid.Column="1"
Margin="10 0"
VerticalAlignment="Center"
Value="{CompiledBinding Y}" />
<TextBlock Grid.Row="2" Grid.Column="2" VerticalAlignment="Center">mm</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center">Scale</TextBlock>
<NumericUpDown Grid.Row="3"
Grid.Column="1"
Margin="10 0"
VerticalAlignment="Center"
Increment="0.1"
FormatString="F1"
Value="{CompiledBinding Scale}" />
<TextBlock Grid.Row="3" Grid.Column="2" VerticalAlignment="Center">times</TextBlock>
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center">Rotation</TextBlock>
<NumericUpDown Grid.Row="4"
Grid.Column="1"
Margin="10 0"
VerticalAlignment="Center"
Value="{CompiledBinding Rotation}" />
<TextBlock Grid.Row="4" Grid.Column="2" VerticalAlignment="Center">deg</TextBlock>
</Grid>
<Rectangle Grid.Column="1" VerticalAlignment="Stretch" Width="1" Fill="{DynamicResource ButtonBorderBrush}" Margin="0 0 0 5" />
<StackPanel Grid.Column="2">
<TextBlock Classes="h4">
Color calibration
</TextBlock>
<TextBlock TextWrapping="Wrap">
Use the sliders below to adjust the colors of your device so that it matches your other devices.
</TextBlock>
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="*,*,*,*">
<Label Grid.Row="0" Grid.Column="0" Content="R" VerticalAlignment="Center" />
<Slider Grid.Row="0" Grid.Column="1" Minimum="0" Maximum="200" Value="{CompiledBinding RedScale}" Margin="10 0" VerticalAlignment="Center" />
<NumericUpDown Grid.Row="0"
Grid.Column="2"
VerticalAlignment="Center"
Width="65"
Value="{CompiledBinding RedScale}"
ShowButtonSpinner="False"
FormatString="{}{0:0.0}"
Minimum="0"
Maximum="200"
ClipValueToMinMax="True" />
<Label Grid.Row="1" Grid.Column="0" Content="G" VerticalAlignment="Center" />
<Slider Grid.Row="1" Grid.Column="1" Minimum="0" Maximum="200" Value="{CompiledBinding GreenScale}" Margin="10 0" VerticalAlignment="Center" />
<NumericUpDown Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Center"
Width="65"
Value="{CompiledBinding GreenScale}"
ShowButtonSpinner="False"
FormatString="{}{0:0.0}"
Minimum="0"
Maximum="200"
ClipValueToMinMax="True" />
<Label Grid.Row="2" Grid.Column="0" Content="B" VerticalAlignment="Center" />
<Slider Grid.Row="2" Grid.Column="1" Minimum="0" Maximum="200" Value="{CompiledBinding BlueScale}" Margin="10 0" Ticks="100" VerticalAlignment="Center" />
<NumericUpDown Grid.Row="2"
Grid.Column="2"
VerticalAlignment="Center"
Width="65"
Value="{CompiledBinding BlueScale}"
ShowButtonSpinner="False"
FormatString="{}{0:0.0}"
Minimum="0"
Maximum="200"
ClipValueToMinMax="True" />
<CheckBox Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" IsChecked="{CompiledBinding DisplayOnDevices}" Content="Show preview" VerticalAlignment="Center" />
<controls:ColorPickerButton Grid.Row="3"
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Color="{CompiledBinding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
ShowAcceptDismissButtons="False" />
</Grid>
</StackPanel>
</Grid>
<!-- Layout -->
<TextBlock Classes="h4">
Layout
</TextBlock>
<TextBlock TextWrapping="Wrap">
The device layout is used to determine the position of LEDs and to create the visual representation of the device you see on the left side of this window.
</TextBlock>
<CheckBox Margin="0 0 0 5" IsChecked="{CompiledBinding Device.DisableDefaultLayout}">
<CheckBox.Content>
<StackPanel Orientation="Horizontal">
<TextBlock>Don't load default layout</TextBlock>
<avalonia:MaterialIcon Kind="HelpCircle"
Margin="5 0"
ToolTip.Tip="With this enabled Artemis will not load a layout for this device unless you specifically provide one." />
</StackPanel>
</CheckBox.Content>
</CheckBox>
<Grid>
<TextBox Text="{CompiledBinding Device.CustomLayoutPath}"
Watermark="Custom layout path"
UseFloatingWatermark="True"
IsReadOnly="True"
PointerReleased="InputElement_OnPointerReleased"
Padding="10 5 36 6" />
<Button Classes="AppBarButton" HorizontalAlignment="Right" Command="{CompiledBinding ClearCustomLayout}" Padding="6" Margin="5 0">
<avalonia:MaterialIcon Kind="CloseCircle" />
</Button>
</Grid>
<controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/en/guides/developer/layouts" Margin="0 20" HorizontalAlignment="Right">
Learn more about layouts on the wiki
</controls:HyperlinkButton>
</StackPanel>
<!-- Buttons -->
<Grid Grid.Row="1" ColumnDefinitions="Auto,*">
<Button Grid.Column="0"
IsVisible="{CompiledBinding RequiresManualSetup}"
Command="{CompiledBinding RestartSetup}"
ToolTip.Tip="Restart device setup, allowing you to select a new physical and logical layout">
Restart setup
</Button>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button IsCancel="True" Command="{CompiledBinding Reset}" Margin="0 0 5 0">Reset</Button>
<Button Classes="accent" IsDefault="True" Command="{CompiledBinding Apply}">Apply</Button>
</StackPanel>
</Grid>
</Grid>
</ScrollViewer>
</UserControl>

View File

@ -1,19 +0,0 @@
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device;
public partial class DevicePropertiesTabView : ReactiveUserControl<DevicePropertiesTabViewModel>
{
public DevicePropertiesTabView()
{
InitializeComponent();
}
private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
ViewModel?.BrowseCustomLayout();
}
}

View File

@ -43,7 +43,7 @@
Under Settings > Plugins you can find your currently installed plugins, these default plugins are created by Artemis developers. We're also keeping track of a list of third-party plugins on our wiki. Under Settings > Plugins you can find your currently installed plugins, these default plugins are created by Artemis developers. We're also keeping track of a list of third-party plugins on our wiki.
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<controls:HyperlinkButton Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins" HorizontalAlignment="Right"> <controls:HyperlinkButton Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins?mtm_campaign=artemis&amp;mtm_kwd=home" HorizontalAlignment="Right">
<controls:HyperlinkButton.ContextMenu> <controls:HyperlinkButton.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="Test"></MenuItem> <MenuItem Header="Test"></MenuItem>
@ -75,7 +75,7 @@
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">GitHub</TextBlock> <TextBlock Margin="8 0 0 0" VerticalAlignment="Center">GitHub</TextBlock>
</StackPanel> </StackPanel>
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton Grid.Row="0" HorizontalAlignment="Right" NavigateUri="https://artemis-rgb.com"> <controls:HyperlinkButton Grid.Row="0" HorizontalAlignment="Right" NavigateUri="https://artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=home">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="Web" /> <avalonia:MaterialIcon Kind="Web" />
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Website</TextBlock> <TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Website</TextBlock>
@ -110,7 +110,7 @@
<controls:HyperlinkButton Grid.Row="1" <controls:HyperlinkButton Grid.Row="1"
Grid.Column="0" Grid.Column="0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
NavigateUri="https://wiki.artemis-rgb.com/en/donating"> NavigateUri="https://wiki.artemis-rgb.com/en/donating?mtm_campaign=artemis&amp;mtm_kwd=home">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="Gift" /> <avalonia:MaterialIcon Kind="Gift" />
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Donate</TextBlock> <TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Donate</TextBlock>

View File

@ -164,44 +164,44 @@
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="_Help"> <MenuItem Header="_Help">
<MenuItem Header="Artemis Wiki" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/"> <MenuItem Header="Artemis Wiki" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="BookEdit" /> <avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="Editor" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/en/guides/user/profiles"> <MenuItem Header="Editor" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/en/guides/user/profiles?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Edit" /> <avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Layers" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/layers"> <MenuItem Header="Layers" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/layers?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Layers" /> <avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Display Conditions" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/conditions"> <MenuItem Header="Display Conditions" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/conditions?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="NotEqual" /> <avalonia:MaterialIcon Kind="NotEqual" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Timeline" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/timeline"> <MenuItem Header="Timeline" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/timeline?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Stopwatch" /> <avalonia:MaterialIcon Kind="Stopwatch" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Data Bindings" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/data-bindings"> <MenuItem Header="Data Bindings" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/data-bindings?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="VectorLink" /> <avalonia:MaterialIcon Kind="VectorLink" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Scripting" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/scripting"> <MenuItem Header="Scripting" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/scripting?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="CodeJson" /> <avalonia:MaterialIcon Kind="CodeJson" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="Report a Bug" Command="{CompiledBinding OpenUri}" CommandParameter="https://github.com/Artemis-RGB/Artemis/issues"> <MenuItem Header="Report a Bug" Command="{CompiledBinding OpenUri}" CommandParameter="https://github.com/Artemis-RGB/Artemis/issues?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Github" /> <avalonia:MaterialIcon Kind="Github" />
</MenuItem.Icon> </MenuItem.Icon>

View File

@ -25,7 +25,7 @@
<controls:HyperlinkButton Grid.Row="0" <controls:HyperlinkButton Grid.Row="0"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Top" VerticalAlignment="Top"
NavigateUri="https://wiki.artemis-rgb.com/guides/user/profiles/layers/adaption-hints"> NavigateUri="https://wiki.artemis-rgb.com/guides/user/profiles/layers/adaption-hints?mtm_campaign=artemis&amp;mtm_kwd=profile-editor">
Learn more about adaption hints Learn more about adaption hints
</controls:HyperlinkButton> </controls:HyperlinkButton>

View File

@ -43,7 +43,7 @@
When you enable data bindings you can no longer use keyframes or normal values for this property. When you enable data bindings you can no longer use keyframes or normal values for this property.
</TextBlock> </TextBlock>
<controls:HyperlinkButton HorizontalAlignment="Center" <controls:HyperlinkButton HorizontalAlignment="Center"
NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/profiles/data-bindings" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/profiles/data-bindings?mtm_campaign=artemis&amp;mtm_kwd=profile-editor"
Margin="0 10"> Margin="0 10">
Learn more Learn more
</controls:HyperlinkButton> </controls:HyperlinkButton>

View File

@ -24,13 +24,13 @@
</TextBlock> </TextBlock>
<StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Orientation="Horizontal"> <StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Orientation="Horizontal">
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View website" NavigateUri="https://artemis-rgb.com"> <controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View website" NavigateUri="https://artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=about">
<avalonia:MaterialIcon Kind="Web" /> <avalonia:MaterialIcon Kind="Web" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub repository" NavigateUri="https://github.com/Artemis-RGB/Artemis"> <controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub repository" NavigateUri="https://github.com/Artemis-RGB/Artemis">
<avalonia:MaterialIcon Kind="Github" /> <avalonia:MaterialIcon Kind="Github" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View Wiki" NavigateUri="https://wiki.artemis-rgb.com"> <controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View Wiki" NavigateUri="https://wiki.artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=about">
<avalonia:MaterialIcon Kind="BookOpenOutline" /> <avalonia:MaterialIcon Kind="BookOpenOutline" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
</StackPanel> </StackPanel>

View File

@ -17,7 +17,7 @@
<TextBox Classes="clearButton" Text="{CompiledBinding SearchPluginInput}" Watermark="Search plugins" Margin="0 0 10 0" /> <TextBox Classes="clearButton" Text="{CompiledBinding SearchPluginInput}" Watermark="Search plugins" Margin="0 0 10 0" />
<StackPanel Spacing="5" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal"> <StackPanel Spacing="5" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal">
<controls:HyperlinkButton VerticalAlignment="Top" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins"> <controls:HyperlinkButton VerticalAlignment="Top" NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/plugins?mtm_campaign=artemis&amp;mtm_kwd=plugins">
Get more plugins Get more plugins
</controls:HyperlinkButton> </controls:HyperlinkButton>
<Button Classes="accent" Command="{CompiledBinding ImportPlugin}">Import plugin</Button> <Button Classes="accent" Command="{CompiledBinding ImportPlugin}">Import plugin</Button>

View File

@ -28,7 +28,7 @@
TextWrapping="Wrap" TextWrapping="Wrap"
Text="{CompiledBinding Channel, StringFormat='Found no releases for the \'{0}\' channel.'}"> Text="{CompiledBinding Channel, StringFormat='Found no releases for the \'{0}\' channel.'}">
</TextBlock> </TextBlock>
<controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/en/channels" <controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/en/channels?mtm_campaign=artemis&amp;mtm_kwd=releases"
HorizontalAlignment="Center"> HorizontalAlignment="Center">
Learn more about channels on the wiki Learn more about channels on the wiki
</controls:HyperlinkButton> </controls:HyperlinkButton>

View File

@ -47,7 +47,7 @@
ToolTip.Tip="View website" ToolTip.Tip="View website"
ToolTip.Placement="Top" ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5" ToolTip.VerticalOffset="-5"
NavigateUri="https://artemis-rgb.com"> NavigateUri="https://artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
<avalonia:MaterialIcon Kind="Web" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="Web" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" <controls:HyperlinkButton Classes="icon-button"
@ -65,7 +65,7 @@
ToolTip.Tip="View Wiki" ToolTip.Tip="View Wiki"
ToolTip.Placement="Top" ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5" ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com"> NavigateUri="https://wiki.artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
<avalonia:MaterialIcon Kind="BookOpenOutline" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="BookOpenOutline" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" <controls:HyperlinkButton Classes="icon-button"
@ -83,7 +83,7 @@
ToolTip.Tip="View donation options" ToolTip.Tip="View donation options"
ToolTip.Placement="Top" ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5" ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com/en/donating"> NavigateUri="https://wiki.artemis-rgb.com/en/donating?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
<avalonia:MaterialIcon Kind="Gift" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="Gift" Width="20" Height="20" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
</WrapPanel> </WrapPanel>

View File

@ -33,10 +33,10 @@
<TextBlock Classes="link-name">Discord</TextBlock> <TextBlock Classes="link-name">Discord</TextBlock>
</StackPanel> </StackPanel>
<StackPanel Grid.Column="1"> <StackPanel Grid.Column="1">
<controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/"> <controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/?mtm_campaign=artemis&amp;mtm_kwd=wizard">
https://wiki.artemis-rgb.com/ https://wiki.artemis-rgb.com/
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/introduction"> <controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/introduction?mtm_campaign=artemis&amp;mtm_kwd=wizard">
https://wiki.artemis-rgb.com/en/guides/user/introduction https://wiki.artemis-rgb.com/en/guides/user/introduction
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/Artemis-RGB/Artemis"> <controls:HyperlinkButton NavigateUri="https://github.com/Artemis-RGB/Artemis">

View File

@ -16,13 +16,13 @@
</TextBlock> </TextBlock>
<StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Orientation="Horizontal"> <StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Orientation="Horizontal">
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View website" NavigateUri="https://artemis-rgb.com"> <controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View website" NavigateUri="https://artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=wizard">
<avalonia:MaterialIcon Kind="Web" /> <avalonia:MaterialIcon Kind="Web" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub repository" NavigateUri="https://github.com/Artemis-RGB/Artemis"> <controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub repository" NavigateUri="https://github.com/Artemis-RGB/Artemis">
<avalonia:MaterialIcon Kind="Github" /> <avalonia:MaterialIcon Kind="Github" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View Wiki" NavigateUri="https://wiki.artemis-rgb.com"> <controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View Wiki" NavigateUri="https://wiki.artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=wizard">
<avalonia:MaterialIcon Kind="BookOpenOutline" /> <avalonia:MaterialIcon Kind="BookOpenOutline" />
</controls:HyperlinkButton> </controls:HyperlinkButton>
</StackPanel> </StackPanel>

View File

@ -116,38 +116,38 @@
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="_Help"> <MenuItem Header="_Help">
<MenuItem Header="Artemis Wiki" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/"> <MenuItem Header="Artemis Wiki" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="BookEdit" /> <avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="Editor" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/en/guides/user/profiles"> <MenuItem Header="Editor" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/en/guides/user/profiles?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Edit" /> <avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Layers" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/layers"> <MenuItem Header="Layers" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/layers?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Layers" /> <avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Display Conditions" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/conditions"> <MenuItem Header="Display Conditions" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/conditions?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="NotEqual" /> <avalonia:MaterialIcon Kind="NotEqual" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Timeline" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/timeline"> <MenuItem Header="Timeline" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/timeline?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="Stopwatch" /> <avalonia:MaterialIcon Kind="Stopwatch" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Data Bindings" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/data-bindings"> <MenuItem Header="Data Bindings" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/data-bindings?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="VectorLink" /> <avalonia:MaterialIcon Kind="VectorLink" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Scripting" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/scripting"> <MenuItem Header="Scripting" Command="{CompiledBinding OpenUri}" CommandParameter="https://wiki.artemis-rgb.com/guides/user/profiles/scripting?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
<MenuItem.Icon> <MenuItem.Icon>
<avalonia:MaterialIcon Kind="CodeJson" /> <avalonia:MaterialIcon Kind="CodeJson" />
</MenuItem.Icon> </MenuItem.Icon>
@ -173,7 +173,7 @@
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Top" VerticalAlignment="Top"
HorizontalAlignment="Right" HorizontalAlignment="Right"
NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/profiles/nodes"> NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/profiles/nodes?mtm_campaign=artemis&amp;mtm_kwd=script-editor">
Learn more about visual scripts Learn more about visual scripts
</controls:HyperlinkButton> </controls:HyperlinkButton>

View File

@ -5,7 +5,7 @@ using NoStringEvaluating.Models.FormulaChecker;
namespace Artemis.VisualScripting.Nodes.Mathematics; namespace Artemis.VisualScripting.Nodes.Mathematics;
[Node("Math Expression", "Outputs the result of a math expression.", "Mathematics", "https://wiki.artemis-rgb.com/en/guides/user/profiles/nodes/mathematics/math-expression", InputType = typeof(Numeric), OutputType = typeof(Numeric))] [Node("Math Expression", "Outputs the result of a math expression.", "Mathematics", "https://wiki.artemis-rgb.com/en/guides/user/profiles/nodes/mathematics/math-expression?mtm_campaign=artemis&amp;mtm_kwd=node-help", InputType = typeof(Numeric), OutputType = typeof(Numeric))]
public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel> public class MathExpressionNode : Node<string, MathExpressionNodeCustomViewModel>
{ {
private readonly IFormulaChecker _checker; private readonly IFormulaChecker _checker;