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

View File

@ -10,6 +10,15 @@
<TextBlock>I'm in a panel yo!</TextBlock>
<Border Classes="card-separator" />
<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>
</Border>
</StackPanel>
@ -50,4 +59,23 @@
<Setter Property="Height" Value="1" />
</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>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows10.0.17763.0</TargetFramework>
@ -29,7 +29,7 @@
<PackageReference Include="Avalonia.Win32" Version="11.0.0-rc1.1" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<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="ReactiveUI" Version="18.4.26" />
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.3" />

View File

@ -41,27 +41,4 @@
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</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>

View File

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

View File

@ -17,7 +17,7 @@
<TextBlock TextWrapping="Wrap" Classes="subtitle" Margin="0 10">
These performance stats are rather basic, for advanced performance profiling check out the wiki.
</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
</controls:HyperlinkButton>
</Grid>

View File

@ -1,58 +1,59 @@
<windowing:AppWindow 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:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Device.DevicePropertiesView"
x:DataType="device:DevicePropertiesViewModel"
Icon="/Assets/Images/Logo/application.ico"
Title="Artemis | Device Properties"
WindowStartupLocation="CenterOwner"
Width="1250"
Height="900">
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:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:device="clr-namespace:Artemis.UI.Screens.Device"
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Device.DevicePropertiesView"
x:DataType="device:DevicePropertiesViewModel"
Icon="/Assets/Images/Logo/application.ico"
Title="Artemis | Device Properties"
WindowStartupLocation="CenterOwner"
Width="1400"
Height="800">
<windowing:AppWindow.KeyBindings>
<KeyBinding Gesture="Escape" Command="{CompiledBinding ClearSelectedLeds}" />
</windowing:AppWindow.KeyBindings>
<Grid ColumnDefinitions="*,0,1.5*">
<Grid.Background>
<VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,25,25">
<VisualBrush.Visual>
<Grid Width="25" Height="25" RowDefinitions="*,*" 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>
<Grid Grid.Column="0" Name="DeviceDisplayGrid" PointerReleased="DeviceDisplayGrid_OnPointerReleased">
<shared:DeviceVisualizer Device="{CompiledBinding Device}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ShowColors="True"
Margin="20"
RenderOptions.BitmapInterpolationMode="MediumQuality"
LedClicked="DeviceVisualizer_OnLedClicked"
Clicked="DeviceVisualizer_OnClicked" />
<Border Grid.Column="0" Classes="card" Margin="5">
<Border.Background>
<VisualBrush TileMode="Tile" Stretch="Uniform" DestinationRect="0,0,25,25">
<VisualBrush.Visual>
<Grid Width="25" Height="25" RowDefinitions="*,*" 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>
</Border.Background>
<Grid Name="DeviceDisplayGrid" PointerReleased="DeviceDisplayGrid_OnPointerReleased">
<shared:DeviceVisualizer Device="{CompiledBinding Device}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ShowColors="True"
Margin="5"
RenderOptions.BitmapInterpolationMode="MediumQuality"
LedClicked="DeviceVisualizer_OnLedClicked"
Clicked="DeviceVisualizer_OnClicked" />
<StackPanel Orientation="Horizontal"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
Margin="15"
IsVisible="{CompiledBinding Device.Layout.RgbLayout.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Classes="h5" Text="Device layout by " />
<TextBlock Classes="h5" FontWeight="Bold" Text="{CompiledBinding Device.Layout.RgbLayout.Author}" />
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
IsVisible="{CompiledBinding Device.Layout.RgbLayout.Author, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Classes="h5" Text="Device layout by " />
<TextBlock Classes="h5" FontWeight="Bold" Text="{CompiledBinding Device.Layout.RgbLayout.Author}" />
</StackPanel>
</Grid>
</Border>
<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>
<TabControl ItemsSource="{CompiledBinding Tabs}" IsVisible="{CompiledBinding Tabs.Count}" Padding="12">
<TabControl.ItemTemplate>
@ -69,6 +70,6 @@
</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>
</windowing:AppWindow>

View File

@ -64,8 +64,8 @@ public class DevicePropertiesViewModel : DialogViewModelBase<object>
private void AddTabs()
{
Tabs.Add(_deviceVmFactory.DevicePropertiesTabViewModel(Device));
Tabs.Add(_deviceVmFactory.DeviceInfoTabViewModel(Device));
Tabs.Add(_deviceVmFactory.DeviceGeneralTabViewModel(Device));
Tabs.Add(_deviceVmFactory.DeviceLayoutTabViewModel(Device));
if (Device.DeviceType == RGBDeviceType.Keyboard)
Tabs.Add(_deviceVmFactory.InputMappingsTabViewModel(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.ComponentModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using ReactiveUI;
using RGB.NET.Core;
using SkiaSharp;
namespace Artemis.UI.Screens.Device;
public class DevicePropertiesTabViewModel : ActivatableViewModelBase
public class DeviceGeneralTabViewModel : ActivatableViewModelBase
{
private readonly List<DeviceCategory> _categories;
private readonly ICoreService _coreService;
private readonly IRgbService _rgbService;
private readonly IWindowService _windowService;
private readonly List<DeviceCategory> _categories;
private readonly float _initialBlueScale;
private readonly float _initialGreenScale;
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 float _scale;
private int _x;
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;
_rgbService = rgbService;
_windowService = windowService;
_notificationService = notificationService;
_categories = new List<DeviceCategory>(device.Categories);
Device = device;
DisplayName = "Properties";
X = (int) Device.X;
Y = (int) Device.Y;
DisplayName = "General";
X = (int)Device.X;
Y = (int)Device.Y;
Scale = Device.Scale;
Rotation = (int) Device.Rotation;
Rotation = (int)Device.Rotation;
RedScale = Device.RedScale * 100f;
GreenScale = Device.GreenScale * 100f;
BlueScale = Device.BlueScale * 100f;
@ -69,10 +73,13 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
{
_coreService.FrameRendering -= OnFrameRendering;
Device.PropertyChanged -= DeviceOnPropertyChanged;
Apply();
}).DisposeWith(d);
});
}
public bool RequiresManualSetup => !Device.DeviceProvider.CanDetectPhysicalLayout || !Device.DeviceProvider.CanDetectLogicalLayout;
public ArtemisDevice Device { get; }
public int X
@ -99,6 +106,38 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
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
{
get => _redScale;
@ -129,72 +168,19 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _displayOnDevices, value);
}
// This solution won't scale well but I don't expect there to be many more categories.
// If for some reason there will be, dynamically creating a view model per category may be more appropriate
public bool HasDeskCategory
private bool GetCategory(DeviceCategory category)
{
get => GetCategory(DeviceCategory.Desk);
set => SetCategory(DeviceCategory.Desk, value);
return _categories.Contains(category);
}
public bool HasMonitorCategory
private void SetCategory(DeviceCategory category, bool value)
{
get => GetCategory(DeviceCategory.Monitor);
set => SetCategory(DeviceCategory.Monitor, value);
}
if (value && !_categories.Contains(category))
_categories.Add(category);
else if (!value)
_categories.Remove(category);
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 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);
}
this.RaisePropertyChanged($"Has{category}Category");
}
public async Task RestartSetup()
@ -211,12 +197,12 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
_rgbService.ApplyBestDeviceLayout(Device);
}
public async Task Apply()
private void Apply()
{
// TODO: Validation
_coreService.ProfileRenderingDisabled = true;
await Task.Delay(100);
Thread.Sleep(100);
Device.X = X;
Device.Y = Y;
@ -234,40 +220,28 @@ public class DevicePropertiesTabViewModel : ActivatableViewModelBase
_coreService.ProfileRenderingDisabled = false;
}
public void Reset()
public void ApplyScaling()
{
HasDeskCategory = Device.Categories.Contains(DeviceCategory.Desk);
HasMonitorCategory = Device.Categories.Contains(DeviceCategory.Monitor);
HasCaseCategory = Device.Categories.Contains(DeviceCategory.Case);
HasRoomCategory = Device.Categories.Contains(DeviceCategory.Room);
HasPeripheralsCategory = Device.Categories.Contains(DeviceCategory.Peripherals);
Device.RedScale = RedScale / 100f;
Device.GreenScale = GreenScale / 100f;
Device.BlueScale = BlueScale / 100f;
_rgbService.FlushLeds = true;
}
public void ResetScaling()
{
RedScale = _initialRedScale * 100;
GreenScale = _initialGreenScale * 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)
{
if (!_displayOnDevices)
if (!DisplayOnDevices)
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);
}

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.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device;
public partial class DeviceInfoTabView : ReactiveUserControl<DeviceInfoTabViewModel>
public partial class DeviceLayoutTabView : ReactiveUserControl<DeviceLayoutTabViewModel>
{
public DeviceInfoTabView()
public DeviceLayoutTabView()
{
InitializeComponent();
}
@ -15,10 +17,12 @@ public partial class DeviceInfoTabView : ReactiveUserControl<DeviceInfoTabViewMo
private void LayoutPathButton_OnClick(object? sender, RoutedEventArgs e)
{
TopLevel.GetTopLevel(this).Clipboard.SetTextAsync(ViewModel.DefaultLayoutPath);
ViewModel.ShowCopiedNotification();
}
private void ImagePathButton_OnClick(object? sender, RoutedEventArgs e)
{
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.
</TextBlock>
</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>
<ContextMenu>
<MenuItem Header="Test"></MenuItem>
@ -75,7 +75,7 @@
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">GitHub</TextBlock>
</StackPanel>
</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">
<avalonia:MaterialIcon Kind="Web" />
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Website</TextBlock>
@ -110,7 +110,7 @@
<controls:HyperlinkButton Grid.Row="1"
Grid.Column="0"
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">
<avalonia:MaterialIcon Kind="Gift" />
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Donate</TextBlock>

View File

@ -164,44 +164,44 @@
</MenuItem>
</MenuItem>
<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>
<avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon>
</MenuItem>
<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>
<avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="NotEqual" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="Stopwatch" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="VectorLink" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="CodeJson" />
</MenuItem.Icon>
</MenuItem>
<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>
<avalonia:MaterialIcon Kind="Github" />
</MenuItem.Icon>

View File

@ -25,7 +25,7 @@
<controls:HyperlinkButton Grid.Row="0"
Grid.Column="1"
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
</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.
</TextBlock>
<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">
Learn more
</controls:HyperlinkButton>

View File

@ -24,13 +24,13 @@
</TextBlock>
<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" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub repository" NavigateUri="https://github.com/Artemis-RGB/Artemis">
<avalonia:MaterialIcon Kind="Github" />
</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" />
</controls:HyperlinkButton>
</StackPanel>

View File

@ -17,7 +17,7 @@
<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">
<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
</controls:HyperlinkButton>
<Button Classes="accent" Command="{CompiledBinding ImportPlugin}">Import plugin</Button>

View File

@ -28,7 +28,7 @@
TextWrapping="Wrap"
Text="{CompiledBinding Channel, StringFormat='Found no releases for the \'{0}\' channel.'}">
</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">
Learn more about channels on the wiki
</controls:HyperlinkButton>

View File

@ -47,7 +47,7 @@
ToolTip.Tip="View website"
ToolTip.Placement="Top"
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" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button"
@ -65,7 +65,7 @@
ToolTip.Tip="View Wiki"
ToolTip.Placement="Top"
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" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button"
@ -83,7 +83,7 @@
ToolTip.Tip="View donation options"
ToolTip.Placement="Top"
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" />
</controls:HyperlinkButton>
</WrapPanel>

View File

@ -33,10 +33,10 @@
<TextBlock Classes="link-name">Discord</TextBlock>
</StackPanel>
<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/
</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
</controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/Artemis-RGB/Artemis">

View File

@ -16,13 +16,13 @@
</TextBlock>
<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" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub repository" NavigateUri="https://github.com/Artemis-RGB/Artemis">
<avalonia:MaterialIcon Kind="Github" />
</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" />
</controls:HyperlinkButton>
</StackPanel>

View File

@ -116,38 +116,38 @@
</MenuItem>
</MenuItem>
<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>
<avalonia:MaterialIcon Kind="BookEdit" />
</MenuItem.Icon>
</MenuItem>
<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>
<avalonia:MaterialIcon Kind="Edit" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="Layers" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="NotEqual" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="Stopwatch" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="VectorLink" />
</MenuItem.Icon>
</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>
<avalonia:MaterialIcon Kind="CodeJson" />
</MenuItem.Icon>
@ -173,7 +173,7 @@
Grid.Column="1"
VerticalAlignment="Top"
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
</controls:HyperlinkButton>

View File

@ -5,7 +5,7 @@ using NoStringEvaluating.Models.FormulaChecker;
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>
{
private readonly IFormulaChecker _checker;