mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Meta - Replace WPF projects with Avalonia projects
This commit is contained in:
1 parent
fcf0376a9a
commit
d7f3ee1190
1110 files changed
+10872
-52240
No files matched your search
@@ -1,83 +0,0 @@
|
||||
<mde:MaterialWindow x:Class="Artemis.UI.Screens.Settings.Debug.DebugView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:mde="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
|
||||
xmlns:debug="clr-namespace:Artemis.UI.Screens.Settings.Debug"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
mc:Ignorable="d"
|
||||
Title="Artemis debugger"
|
||||
TitleBarIcon="{svgc:SvgImage Source=/Resources/Images/Logo/bow-white.svg}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Background="{DynamicResource MaterialDesignPaper}"
|
||||
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
|
||||
UseLayoutRounding="True"
|
||||
FadeContentIfInactive="False"
|
||||
Width="1200"
|
||||
Height="800"
|
||||
d:DesignHeight="800" d:DesignWidth="800" d:DataContext="{d:DesignInstance debug:DebugViewModel}"
|
||||
Icon="/Resources/Images/Logo/bow.ico"
|
||||
Topmost="{Binding StayOnTopSetting.Value}">
|
||||
<materialDesign:DialogHost IsTabStop="False" Focusable="False" Identifier="DebuggerDialog" DialogTheme="Inherit">
|
||||
<DockPanel>
|
||||
<mde:AppBar Type="Dense" Title="Debugger" DockPanel.Dock="Top" Margin="-18 0 0 0">
|
||||
<mde:AppBar.AppIcon>
|
||||
<materialDesign:PackIcon Kind="Matrix" Width="20" Height="28" />
|
||||
</mde:AppBar.AppIcon>
|
||||
|
||||
<materialDesign:PopupBox DockPanel.Dock="Right" PlacementMode="BottomAndAlignRightEdges" StaysOpen="False">
|
||||
<StackPanel>
|
||||
<Button Command="{s:Action ToggleStayOnTop}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}"
|
||||
Margin="0 0 10 0"
|
||||
IsChecked="{Binding StayOnTopSetting.Value}" />
|
||||
<TextBlock>Stay on top</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action ForceGarbageCollection}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="DeleteEmpty" />
|
||||
<TextBlock Margin="10 0">Force garbage collection</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Elevate}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull" />
|
||||
<TextBlock Margin="10 0">Elevate permissions</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Drop}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="ChevronDoubleDown" />
|
||||
<TextBlock Margin="10 0">Drop permissions</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Restart}">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
|
||||
<materialDesign:PackIcon Kind="Restart" />
|
||||
<TextBlock Margin="10 0">Restart</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</mde:AppBar>
|
||||
|
||||
<TabControl ItemsSource="{Binding Items}"
|
||||
SelectedItem="{Binding ActiveItem}"
|
||||
DisplayMemberPath="DisplayName"
|
||||
Style="{StaticResource MaterialDesignTabControl}">
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<materialDesign:TransitioningContent OpeningEffect="{materialDesign:TransitionEffect FadeIn}">
|
||||
<ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" Margin="10" />
|
||||
</materialDesign:TransitioningContent>
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</materialDesign:DialogHost>
|
||||
</mde:MaterialWindow>
|
||||
@@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.Settings.Debug.Tabs;
|
||||
using Artemis.UI.Screens.Settings.Debug.Tabs.Performance;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug
|
||||
{
|
||||
public class DebugViewModel : Conductor<Screen>.Collection.OneActive
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
|
||||
public DebugViewModel(
|
||||
ISettingsService settingsService,
|
||||
ICoreService coreService,
|
||||
RenderDebugViewModel renderDebugViewModel,
|
||||
DataModelDebugViewModel dataModelDebugViewModel,
|
||||
LogsDebugViewModel logsDebugViewModel,
|
||||
PerformanceDebugViewModel performanceDebugViewModel)
|
||||
{
|
||||
_coreService = coreService;
|
||||
Items.Add(renderDebugViewModel);
|
||||
Items.Add(dataModelDebugViewModel);
|
||||
Items.Add(logsDebugViewModel);
|
||||
Items.Add(performanceDebugViewModel);
|
||||
ActiveItem = renderDebugViewModel;
|
||||
|
||||
StayOnTopSetting = settingsService.GetSetting("Debugger.StayOnTop", false);
|
||||
}
|
||||
|
||||
public PluginSetting<bool> StayOnTopSetting { get; }
|
||||
|
||||
public string Title => "Debugger";
|
||||
public bool CanElevate => !_coreService.IsElevated;
|
||||
public bool CanDrop => _coreService.IsElevated;
|
||||
|
||||
public void ToggleStayOnTop()
|
||||
{
|
||||
StayOnTopSetting.Value = !StayOnTopSetting.Value;
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
StayOnTopSetting.Save();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
public void ForceGarbageCollection()
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
public void Elevate()
|
||||
{
|
||||
Core.Utilities.Restart(true, TimeSpan.FromMilliseconds(500), "--force-elevation");
|
||||
}
|
||||
|
||||
public void Drop()
|
||||
{
|
||||
Core.Utilities.Restart(false, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
Core.Utilities.Restart(_coreService.IsElevated, TimeSpan.FromMilliseconds(500));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Debug.Tabs.DataModelDebugView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Debug.Tabs"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:wpf="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:dataModel="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:DataModelDebugViewModel}">
|
||||
<UserControl.Resources>
|
||||
<dataModel:TypeToStringConverter x:Key="TypeToStringConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" Margin="0 0 0 5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="0.5*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="0.5*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Property searching (nyi) -->
|
||||
<wpf:PackIcon Grid.Column="0" Kind="Search" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" wpf:HintAssist.Hint="Search property" VerticalAlignment="Center" Margin="5 0" IsEnabled="False" />
|
||||
|
||||
<!-- Module filtering -->
|
||||
<TextBlock Grid.Row="0" Grid.Column="3" VerticalAlignment="Center">Filter module</TextBlock>
|
||||
<ToggleButton Grid.Row="0"
|
||||
Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
Margin="5 0"
|
||||
Style="{StaticResource MaterialDesignSwitchToggleButton}"
|
||||
IsChecked="{Binding IsModuleFilterEnabled}" />
|
||||
<ComboBox Grid.Row="0"
|
||||
Grid.Column="5"
|
||||
VerticalAlignment="Center"
|
||||
wpf:HintAssist.Hint="Select a module"
|
||||
IsEditable="True"
|
||||
TextSearch.TextPath="DisplayName"
|
||||
DisplayMemberPath="DisplayName"
|
||||
Margin="5 0 0 0"
|
||||
IsEnabled="{Binding IsModuleFilterEnabled}"
|
||||
SelectedItem="{Binding SelectedModule}"
|
||||
ItemsSource="{Binding Modules}" />
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Orientation="Horizontal"
|
||||
Margin="0 10 0 0"
|
||||
ToolTip="Check to update values every half second instead of realtime">
|
||||
<TextBlock VerticalAlignment="Center">Slow updates</TextBlock>
|
||||
<ToggleButton VerticalAlignment="Center" Margin="5 0" Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding SlowUpdates}" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
<TreeView Grid.Row="1"
|
||||
ItemsSource="{Binding MainDataModel.Children}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VirtualizingStackPanel.IsVirtualizing="True">
|
||||
<TreeView.Resources>
|
||||
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource MaterialDesignTreeViewItem}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
|
||||
</Style>
|
||||
|
||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{Binding Children}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold">
|
||||
<Run>[</Run><Run Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /><Run>]</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding DisplayValue}"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
|
||||
</HierarchicalDataTemplate>
|
||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}" ItemsSource="{Binding ListChildren}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold">
|
||||
<Run>[</Run><Run Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /><Run>]</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding CountDisplay, Mode=OneWay}"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelPropertyViewModel}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Value description -->
|
||||
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold">
|
||||
<Run>[</Run><Run Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /><Run>]</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
|
||||
|
||||
<!-- Value display -->
|
||||
<ContentControl Grid.Column="2" s:View.Model="{Binding DisplayViewModel}" FontFamily="Consolas" />
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelListPropertyViewModel}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Value description -->
|
||||
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold">
|
||||
<Run>[</Run><Run Text="{Binding ListType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /><Run>]</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1" ToolTip="{Binding PropertyDescription.Description}">
|
||||
<Run>List item [</Run><Run Text="{Binding Index, Mode=OneWay}" /><Run>]</Run>
|
||||
</TextBlock>
|
||||
|
||||
<!-- Value display -->
|
||||
<ContentControl Grid.Column="2" s:View.Model="{Binding DisplayViewModel}" FontFamily="Consolas" />
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{Binding Children}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold">
|
||||
<Run>[</Run><Run Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" /><Run>]</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding CountDisplay, Mode=OneWay}"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelListPropertiesViewModel}" ItemsSource="{Binding DisplayViewModel.Children}">
|
||||
<TextBlock>
|
||||
<Run>List item [</Run><Run Text="{Binding Index, Mode=OneWay}" /><Run>]</Run>
|
||||
</TextBlock>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.Resources>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,166 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
{
|
||||
public sealed class DataModelDebugViewModel : Screen, IDisposable
|
||||
{
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly Timer _updateTimer;
|
||||
|
||||
private bool _isModuleFilterEnabled;
|
||||
private DataModelPropertiesViewModel _mainDataModel;
|
||||
private string _propertySearch;
|
||||
private Module _selectedModule;
|
||||
private bool _slowUpdates;
|
||||
|
||||
public DataModelDebugViewModel(IDataModelUIService dataModelUIService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
DisplayName = "DATA MODEL";
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_updateTimer = new Timer(25);
|
||||
|
||||
Modules = new BindableCollection<Module>();
|
||||
}
|
||||
|
||||
public DataModelPropertiesViewModel MainDataModel
|
||||
{
|
||||
get => _mainDataModel;
|
||||
set => SetAndNotify(ref _mainDataModel, value);
|
||||
}
|
||||
|
||||
public string PropertySearch
|
||||
{
|
||||
get => _propertySearch;
|
||||
set => SetAndNotify(ref _propertySearch, value);
|
||||
}
|
||||
|
||||
public bool SlowUpdates
|
||||
{
|
||||
get => _slowUpdates;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _slowUpdates, value);
|
||||
_updateTimer.Interval = _slowUpdates ? 500 : 25;
|
||||
}
|
||||
}
|
||||
|
||||
public BindableCollection<Module> Modules { get; }
|
||||
|
||||
public Module SelectedModule
|
||||
{
|
||||
get => _selectedModule;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _selectedModule, value)) return;
|
||||
GetDataModel();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModuleFilterEnabled
|
||||
{
|
||||
get => _isModuleFilterEnabled;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _isModuleFilterEnabled, value);
|
||||
|
||||
if (!IsModuleFilterEnabled)
|
||||
SelectedModule = null;
|
||||
else
|
||||
GetDataModel();
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnClose()
|
||||
{
|
||||
_updateTimer.Dispose();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
GetDataModel();
|
||||
_updateTimer.Start();
|
||||
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
|
||||
_pluginManagementService.PluginFeatureEnabled += OnPluginFeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled += OnPluginFeatureToggled;
|
||||
|
||||
PopulateModules();
|
||||
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
_updateTimer.Stop();
|
||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||
MainDataModel?.Dispose();
|
||||
MainDataModel = null;
|
||||
|
||||
_pluginManagementService.PluginFeatureEnabled -= OnPluginFeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled -= OnPluginFeatureToggled;
|
||||
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
private void OnPluginFeatureToggled(object sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature is Module)
|
||||
PopulateModules();
|
||||
}
|
||||
|
||||
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs args)
|
||||
{
|
||||
if (MainDataModel == null)
|
||||
return;
|
||||
|
||||
lock (MainDataModel)
|
||||
{
|
||||
MainDataModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(true));
|
||||
}
|
||||
}
|
||||
|
||||
private void GetDataModel()
|
||||
{
|
||||
MainDataModel = SelectedModule != null
|
||||
? _dataModelUIService.GetPluginDataModelVisualization(new List<Module>() {SelectedModule}, false)
|
||||
: _dataModelUIService.GetMainDataModelVisualization();
|
||||
}
|
||||
|
||||
private void PopulateModules()
|
||||
{
|
||||
Modules.Clear();
|
||||
Modules.AddRange(_pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled).OrderBy(m => m.Info.Name));
|
||||
|
||||
if (SelectedModule == null)
|
||||
_dataModelUIService.UpdateModules(MainDataModel);
|
||||
else if (!SelectedModule.IsEnabled)
|
||||
SelectedModule = null;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_updateTimer?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Debug.Tabs.LogsDebugView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Debug.Tabs"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:LogsDebugViewModel}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<materialDesign:Card Grid.Row="0" Grid.ColumnSpan="2" Margin="0 0 0 5" Background="#FF424242">
|
||||
<FlowDocumentScrollViewer Document="{Binding LogsDocument}" FontFamily="Consolas" HorizontalScrollBarVisibility="Auto">
|
||||
<FlowDocumentScrollViewer.Resources>
|
||||
<Style TargetType="FlowDocument">
|
||||
<Setter Property="TextElement.FontFamily" Value="Consolas" />
|
||||
<Setter Property="TextElement.FontSize" Value="12" />
|
||||
<Setter Property="PageWidth" Value="2024" />
|
||||
</Style>
|
||||
<Style TargetType="Paragraph">
|
||||
<Setter Property="Margin" Value="0 1" />
|
||||
</Style>
|
||||
</FlowDocumentScrollViewer.Resources>
|
||||
</FlowDocumentScrollViewer>
|
||||
</materialDesign:Card>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" TextWrapping="Wrap" Margin="0 0 5 0" VerticalAlignment="Center">
|
||||
<materialDesign:PackIcon Kind="Text" Margin="0 0 0 -3"></materialDesign:PackIcon>
|
||||
When reporting errors please don't take a screenshot of the logs, instead upload the full log or select & copy a part of it, thanks!
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 5">
|
||||
<Button Style="{StaticResource MaterialDesignRaisedButton}" Command="{s:Action UploadLogs}" Width="150" Margin="0 0 5 0">
|
||||
UPLOAD LOGS
|
||||
</Button>
|
||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowLogsFolder}" Width="150" Margin="5 0 0 0">
|
||||
SHOW LOGS
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LogsDebugView.xaml
|
||||
/// </summary>
|
||||
public partial class LogsDebugView : UserControl
|
||||
{
|
||||
public LogsDebugView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Serilog.Events;
|
||||
using Serilog.Formatting.Display;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
{
|
||||
public class LogsDebugViewModel : Screen
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly MessageTemplateTextFormatter _formatter;
|
||||
private ScrollViewer _scrollViewer;
|
||||
|
||||
public LogsDebugViewModel(IDialogService dialogService)
|
||||
{
|
||||
DisplayName = "LOGS";
|
||||
_dialogService = dialogService;
|
||||
_formatter = new MessageTemplateTextFormatter(
|
||||
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
|
||||
);
|
||||
}
|
||||
|
||||
public FlowDocument LogsDocument { get; } = new();
|
||||
|
||||
public void ShowLogsFolder()
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs"));
|
||||
}
|
||||
|
||||
public async Task UploadLogs()
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialogAt(
|
||||
"DebuggerDialog",
|
||||
"Upload logs",
|
||||
"Automatically uploading logs is not yet implemented.\r\n\r\n" +
|
||||
"To manually upload a log simply drag the log file from the logs folder\r\n" +
|
||||
"into Discord or the GitHub issue textbox, depending on what you're using.",
|
||||
"OPEN LOGS FOLDER",
|
||||
"CANCEL");
|
||||
|
||||
if (confirmed)
|
||||
ShowLogsFolder();
|
||||
}
|
||||
|
||||
private Paragraph CreateLogEventParagraph(LogEvent logEvent)
|
||||
{
|
||||
Paragraph paragraph = new(new Run(RenderLogEvent(logEvent)))
|
||||
{
|
||||
// But mah MVVM
|
||||
Foreground = logEvent.Level switch
|
||||
{
|
||||
LogEventLevel.Verbose => new SolidColorBrush(Colors.White),
|
||||
LogEventLevel.Debug => new SolidColorBrush(Color.FromRgb(216, 216, 216)),
|
||||
LogEventLevel.Information => new SolidColorBrush(Color.FromRgb(93, 201, 255)),
|
||||
LogEventLevel.Warning => new SolidColorBrush(Color.FromRgb(255, 177, 53)),
|
||||
LogEventLevel.Error => new SolidColorBrush(Color.FromRgb(255, 63, 63)),
|
||||
LogEventLevel.Fatal => new SolidColorBrush(Colors.Red),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
}
|
||||
};
|
||||
|
||||
return paragraph;
|
||||
}
|
||||
|
||||
private string RenderLogEvent(LogEvent logEvent)
|
||||
{
|
||||
using StringWriter writer = new();
|
||||
_formatter.Format(logEvent, writer);
|
||||
return writer.ToString().Trim();
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnActivate()
|
||||
{
|
||||
LogsDocument.Blocks.AddRange(LogStore.Events.Select(e => CreateLogEventParagraph(e)));
|
||||
LogStore.EventAdded += LogStoreOnEventAdded;
|
||||
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
private void LogStoreOnEventAdded(object sender, LogEventEventArgs e)
|
||||
{
|
||||
Execute.PostToUIThread(() =>
|
||||
{
|
||||
LogsDocument.Blocks.Add(CreateLogEventParagraph(e.LogEvent));
|
||||
while (LogsDocument.Blocks.Count > 500)
|
||||
LogsDocument.Blocks.Remove(LogsDocument.Blocks.FirstBlock);
|
||||
|
||||
if (_scrollViewer != null && Math.Abs(_scrollViewer.VerticalOffset - _scrollViewer.ScrollableHeight) < 10)
|
||||
_scrollViewer.ScrollToBottom();
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
LogStore.EventAdded -= LogStoreOnEventAdded;
|
||||
LogsDocument.Blocks.Clear();
|
||||
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnViewLoaded()
|
||||
{
|
||||
ScrollViewer scrollViewer = VisualTreeUtilities.FindChild<ScrollViewer>(View, null);
|
||||
_scrollViewer = scrollViewer;
|
||||
_scrollViewer?.ScrollToBottom();
|
||||
|
||||
base.OnViewLoaded();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs.Performance
|
||||
{
|
||||
public class PerformanceDebugMeasurementViewModel : PropertyChangedBase
|
||||
{
|
||||
private string _average;
|
||||
private string _last;
|
||||
private string _max;
|
||||
private string _min;
|
||||
private string _percentile;
|
||||
|
||||
public PerformanceDebugMeasurementViewModel(ProfilingMeasurement measurement)
|
||||
{
|
||||
Measurement = measurement;
|
||||
}
|
||||
|
||||
public ProfilingMeasurement Measurement { get; }
|
||||
|
||||
public string Last
|
||||
{
|
||||
get => _last;
|
||||
set => SetAndNotify(ref _last, value);
|
||||
}
|
||||
|
||||
public string Average
|
||||
{
|
||||
get => _average;
|
||||
set => SetAndNotify(ref _average, value);
|
||||
}
|
||||
|
||||
public string Min
|
||||
{
|
||||
get => _min;
|
||||
set => SetAndNotify(ref _min, value);
|
||||
}
|
||||
|
||||
public string Max
|
||||
{
|
||||
get => _max;
|
||||
set => SetAndNotify(ref _max, value);
|
||||
}
|
||||
|
||||
public string Percentile
|
||||
{
|
||||
get => _percentile;
|
||||
set => SetAndNotify(ref _percentile, value);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
Last = Measurement.GetLast().TotalMilliseconds + " ms";
|
||||
Average = Measurement.GetAverage().TotalMilliseconds + " ms";
|
||||
Min = Measurement.GetMin().TotalMilliseconds + " ms";
|
||||
Max = Measurement.GetMax().TotalMilliseconds + " ms";
|
||||
Percentile = Measurement.GetPercentile(0.95).TotalMilliseconds + " ms";
|
||||
}
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Debug.Tabs.Performance.PerformanceDebugPluginView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Debug.Tabs.Performance"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:PerformanceDebugPluginViewModel}">
|
||||
<materialDesign:Card Margin="0 5" Padding="10">
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="40"/>
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<shared:ArtemisIcon Grid.Column="0" Icon="{Binding Plugin.Info.ResolvedIcon}" Width="24" Height="24" />
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Text="{Binding Plugin.Info.Name}" />
|
||||
</Grid>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Profilers}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
</UserControl>
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs.Performance
|
||||
{
|
||||
public class PerformanceDebugPluginViewModel : Screen
|
||||
{
|
||||
public PerformanceDebugPluginViewModel(Plugin plugin)
|
||||
{
|
||||
Plugin = plugin;
|
||||
}
|
||||
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
public BindableCollection<PerformanceDebugProfilerViewModel> Profilers { get; } = new();
|
||||
|
||||
public void Update()
|
||||
{
|
||||
foreach (Profiler pluginProfiler in Plugin.Profilers.Where(p => p.Measurements.Any()))
|
||||
if (Profilers.All(p => p.Profiler != pluginProfiler))
|
||||
Profilers.Add(new PerformanceDebugProfilerViewModel(pluginProfiler));
|
||||
|
||||
foreach (PerformanceDebugProfilerViewModel profilerViewModel in Profilers)
|
||||
profilerViewModel.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Debug.Tabs.Performance.PerformanceDebugProfilerView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Debug.Tabs.Performance"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:PerformanceDebugProfilerViewModel}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody2TextBlock}" Text="{Binding Profiler.Name}" Margin="10 10 0 0" />
|
||||
|
||||
<DataGrid ItemsSource="{Binding Measurements}"
|
||||
d:DataContext="{d:DesignInstance Type={x:Type local:PerformanceDebugMeasurementViewModel}}"
|
||||
CanUserSortColumns="True"
|
||||
IsReadOnly="True"
|
||||
CanUserAddRows="False"
|
||||
AutoGenerateColumns="False"
|
||||
materialDesign:DataGridAssist.CellPadding="16 5 5 5"
|
||||
materialDesign:DataGridAssist.ColumnHeaderPadding="16 5 5 5"
|
||||
CanUserResizeRows="False"
|
||||
Margin="10 5 10 10">
|
||||
<DataGrid.Columns>
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Measurement.Identifier}" Header="Identifier" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Last}" Header="Last" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Min}" Header="Min" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Max}" Header="Max" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Average}" Header="Average" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Percentile}" Header="95th percentile" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs.Performance
|
||||
{
|
||||
public class PerformanceDebugProfilerViewModel : Screen
|
||||
{
|
||||
public Profiler Profiler { get; }
|
||||
|
||||
public PerformanceDebugProfilerViewModel(Profiler profiler)
|
||||
{
|
||||
Profiler = profiler;
|
||||
}
|
||||
|
||||
public BindableCollection<PerformanceDebugMeasurementViewModel> Measurements { get; } = new();
|
||||
|
||||
public void Update()
|
||||
{
|
||||
foreach ((string _, ProfilingMeasurement measurement) in Profiler.Measurements)
|
||||
{
|
||||
if (Measurements.All(m => m.Measurement != measurement))
|
||||
Measurements.Add(new PerformanceDebugMeasurementViewModel(measurement));
|
||||
}
|
||||
|
||||
foreach (PerformanceDebugMeasurementViewModel profilingMeasurementViewModel in Measurements)
|
||||
profilingMeasurementViewModel.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Debug.Tabs.Performance.PerformanceDebugView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Debug.Tabs.Performance"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:PerformanceDebugViewModel}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" TextWrapping="Wrap">
|
||||
In this window you can see how much CPU time different plugin features are taking.
|
||||
If you are having performance issues, below you can find out which plugin might be the culprit.
|
||||
</TextBlock>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
|
||||
<ItemsControl ItemsSource="{Binding Items}" Margin="0 0 10 0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<materialDesign:TransitioningContent OpeningEffect="{materialDesign:TransitionEffect SlideInFromLeft}"
|
||||
OpeningEffectsOffset="{materialDesign:IndexedItemOffsetMultiplier 0:0:0.05}">
|
||||
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
</materialDesign:TransitioningContent>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs.Performance
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for PerformanceDebugView.xaml
|
||||
/// </summary>
|
||||
public partial class PerformanceDebugView : UserControl
|
||||
{
|
||||
public PerformanceDebugView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
ScrollViewer scv = (ScrollViewer)sender;
|
||||
scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs.Performance
|
||||
{
|
||||
public class PerformanceDebugViewModel : Conductor<PerformanceDebugPluginViewModel>.Collection.AllActive
|
||||
{
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly Timer _updateTimer;
|
||||
|
||||
public PerformanceDebugViewModel(IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_updateTimer = new Timer(500);
|
||||
|
||||
DisplayName = "PERFORMANCE";
|
||||
_updateTimer.Elapsed += UpdateTimerOnElapsed;
|
||||
}
|
||||
|
||||
private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
foreach (PerformanceDebugPluginViewModel viewModel in Items)
|
||||
viewModel.Update();
|
||||
}
|
||||
|
||||
private void FeatureToggled(object sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
Items.Clear();
|
||||
PopulateItems();
|
||||
}
|
||||
|
||||
private void PluginToggled(object sender, PluginEventArgs e)
|
||||
{
|
||||
Items.Clear();
|
||||
PopulateItems();
|
||||
}
|
||||
|
||||
private void PopulateItems()
|
||||
{
|
||||
Items.AddRange(_pluginManagementService.GetAllPlugins()
|
||||
.Where(p => p.IsEnabled && p.Profilers.Any(pr => pr.Measurements.Any()))
|
||||
.OrderBy(p => p.Info.Name)
|
||||
.Select(p => new PerformanceDebugPluginViewModel(p)));
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnActivate()
|
||||
{
|
||||
PopulateItems();
|
||||
_updateTimer.Start();
|
||||
_pluginManagementService.PluginDisabled += PluginToggled;
|
||||
_pluginManagementService.PluginDisabled += PluginToggled;
|
||||
_pluginManagementService.PluginFeatureEnabled += FeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled += FeatureToggled;
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
_updateTimer.Stop();
|
||||
_pluginManagementService.PluginDisabled -= PluginToggled;
|
||||
_pluginManagementService.PluginDisabled -= PluginToggled;
|
||||
_pluginManagementService.PluginFeatureEnabled -= FeatureToggled;
|
||||
_pluginManagementService.PluginFeatureDisabled -= FeatureToggled;
|
||||
Items.Clear();
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Debug.Tabs.RenderDebugView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Debug.Tabs"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:RenderDebugViewModel}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" TextWrapping="Wrap">
|
||||
In this window you can view the inner workings of Artemis.
|
||||
Please note that having this window open can have a performance impact on your system.
|
||||
</TextBlock>
|
||||
|
||||
<Grid Grid.Row="1" Margin="0,10,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0">
|
||||
This image shows what is being rendered and dispatched to RGB.NET
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,5,0">
|
||||
<Run Text="FPS: " />
|
||||
<Run FontWeight="Bold" Text="{Binding CurrentFps}" />
|
||||
<Run Text="at" />
|
||||
<Run Text="{Binding RenderWidth}" /><Run Text="x" /><Run Text="{Binding RenderHeight}" />
|
||||
<Run Text="- Renderer:"></Run>
|
||||
<Run Text="{Binding Renderer}"></Run>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<materialDesign:Card Grid.Row="2" Margin="0 5" Background="{StaticResource Checkerboard}">
|
||||
<Image Source="{Binding CurrentFrame}" />
|
||||
</materialDesign:Card>
|
||||
|
||||
<StackPanel Grid.Row="3" HorizontalAlignment="Right" Margin="0 5">
|
||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action SaveFrame}">SAVE FRAME</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,154 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Timers;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.Views.WPF;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Debug.Tabs
|
||||
{
|
||||
public class RenderDebugViewModel : Screen
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly Timer _fpsTimer;
|
||||
private double _currentFps;
|
||||
private WriteableBitmap _currentFrame;
|
||||
private int _renderWidth;
|
||||
private int _renderHeight;
|
||||
private string _frameTargetPath;
|
||||
private string _renderer;
|
||||
private int _frames;
|
||||
|
||||
public RenderDebugViewModel(ICoreService coreService)
|
||||
{
|
||||
DisplayName = "RENDERING";
|
||||
_coreService = coreService;
|
||||
_fpsTimer = new Timer(1000);
|
||||
_fpsTimer.Start();
|
||||
}
|
||||
|
||||
public WriteableBitmap CurrentFrame
|
||||
{
|
||||
get => _currentFrame;
|
||||
set => SetAndNotify(ref _currentFrame, value);
|
||||
}
|
||||
|
||||
public double CurrentFps
|
||||
{
|
||||
get => _currentFps;
|
||||
set => SetAndNotify(ref _currentFps, value);
|
||||
}
|
||||
|
||||
public int RenderWidth
|
||||
{
|
||||
get => _renderWidth;
|
||||
set => SetAndNotify(ref _renderWidth, value);
|
||||
}
|
||||
|
||||
public int RenderHeight
|
||||
{
|
||||
get => _renderHeight;
|
||||
set => SetAndNotify(ref _renderHeight, value);
|
||||
}
|
||||
|
||||
public string Renderer
|
||||
{
|
||||
get => _renderer;
|
||||
set => SetAndNotify(ref _renderer, value);
|
||||
}
|
||||
|
||||
public void SaveFrame()
|
||||
{
|
||||
VistaSaveFileDialog dialog = new VistaSaveFileDialog {Filter = "Portable network graphic (*.png)|*.png", Title = "Save render frame"};
|
||||
dialog.FileName = $"Artemis frame {DateTime.Now:yyyy-dd-M--HH-mm-ss}.png";
|
||||
bool? result = dialog.ShowDialog();
|
||||
if (result == true)
|
||||
{
|
||||
if (dialog.FileName.EndsWith(".png"))
|
||||
_frameTargetPath = dialog.FileName;
|
||||
else
|
||||
_frameTargetPath = dialog.FileName + ".png";
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
_fpsTimer.Elapsed += FpsTimerOnElapsed;
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
|
||||
_fpsTimer.Elapsed -= FpsTimerOnElapsed;
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
_fpsTimer.Dispose();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
|
||||
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
_frames++;
|
||||
|
||||
using SKImage skImage = e.Texture.Surface.Snapshot();
|
||||
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||
|
||||
if (_frameTargetPath != null)
|
||||
{
|
||||
using (SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100))
|
||||
{
|
||||
using (FileStream stream = File.OpenWrite(_frameTargetPath))
|
||||
{
|
||||
data.SaveTo(stream);
|
||||
}
|
||||
}
|
||||
|
||||
_frameTargetPath = null;
|
||||
}
|
||||
|
||||
RenderHeight = bitmapInfo.Height;
|
||||
RenderWidth = bitmapInfo.Width;
|
||||
|
||||
Execute.OnUIThreadSync(() =>
|
||||
{
|
||||
// ReSharper disable twice CompareOfFloatsByEqualityOperator
|
||||
if (CurrentFrame == null || CurrentFrame.Width != bitmapInfo.Width || CurrentFrame.Height != bitmapInfo.Height)
|
||||
{
|
||||
CurrentFrame = e.Texture.Surface.Snapshot().ToWriteableBitmap();
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentFrame.Lock();
|
||||
using (SKPixmap pixmap = new(bitmapInfo, CurrentFrame.BackBuffer, CurrentFrame.BackBufferStride))
|
||||
{
|
||||
// ReSharper disable once AccessToDisposedClosure - Looks fine
|
||||
skImage.ReadPixels(pixmap, 0, 0);
|
||||
}
|
||||
|
||||
CurrentFrame.AddDirtyRect(new Int32Rect(0, 0, CurrentFrame.PixelWidth, CurrentFrame.PixelHeight));
|
||||
CurrentFrame.Unlock();
|
||||
});
|
||||
}
|
||||
|
||||
private void FpsTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
CurrentFps = _frames;
|
||||
// Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||
Renderer = $"HighAccuracyTimers: {Stopwatch.IsHighResolution}";
|
||||
_frames = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
<mde:MaterialWindow x:Class="Artemis.UI.Screens.Settings.Device.DeviceDialogView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:mde="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:device="clr-namespace:Artemis.UI.Screens.Settings.Device"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
mc:Ignorable="d"
|
||||
Title="{Binding DisplayName}"
|
||||
TitleBarIcon="{svgc:SvgImage Source=/Resources/Images/Logo/bow-white.svg}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Background="{DynamicResource MaterialDesignPaper}"
|
||||
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
|
||||
UseLayoutRounding="True"
|
||||
FadeContentIfInactive="False"
|
||||
Width="1400"
|
||||
Height="800"
|
||||
d:DesignHeight="800" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance device:DeviceDialogViewModel}"
|
||||
Icon="/Resources/Images/Logo/bow.ico">
|
||||
<mde:MaterialWindow.Resources>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
|
||||
</mde:MaterialWindow.Resources>
|
||||
<mde:MaterialWindow.InputBindings>
|
||||
<KeyBinding Command="{s:Action ClearSelection}" Key="Escape" />
|
||||
</mde:MaterialWindow.InputBindings>
|
||||
|
||||
<materialDesign:DialogHost IsTabStop="False"
|
||||
Focusable="False"
|
||||
Identifier="DeviceDialog"
|
||||
DialogTheme="Inherit"
|
||||
SnackbarMessageQueue="{Binding DeviceMessageQueue}">
|
||||
<DockPanel>
|
||||
<mde:AppBar Type="Dense"
|
||||
Title="{Binding Device.RgbDevice.DeviceInfo.Model}"
|
||||
ShowShadow="True"
|
||||
DockPanel.Dock="Top"
|
||||
Margin="-18 0 0 0">
|
||||
<mde:AppBar.AppIcon>
|
||||
<materialDesign:PackIcon Kind="HammerWrench" Width="20" Height="28" />
|
||||
</mde:AppBar.AppIcon>
|
||||
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Identify" Command="{s:Action IdentifyDevice}">
|
||||
<materialDesign:PackIcon Kind="AlarmLight" />
|
||||
</Button>
|
||||
<materialDesign:PopupBox PlacementMode="BottomAndAlignRightEdges" StaysOpen="False">
|
||||
<StackPanel>
|
||||
<Button Command="{s:Action OpenPluginDirectory}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Plugin" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Open plugin directory</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action OpenImageDirectory}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Image" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Open layout image directory</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Command="{s:Action ReloadLayout}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Reload" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Reload layout</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action ExportLayout}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Xml" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Export layout</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</StackPanel>
|
||||
</mde:AppBar>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="0" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Name="DeviceDisplayGrid" Grid.Column="0">
|
||||
<Grid.Background>
|
||||
<VisualBrush TileMode="Tile" Stretch="Uniform" Viewport="0 0 25 25" ViewportUnits="Absolute">
|
||||
<VisualBrush.Visual>
|
||||
<Grid Width="20" Height="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" Opacity="0.15" />
|
||||
<Rectangle Grid.Row="0" Grid.Column="1" />
|
||||
<Rectangle Grid.Row="1" Grid.Column="0" />
|
||||
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Black" Opacity="0.15" />
|
||||
</Grid>
|
||||
</VisualBrush.Visual>
|
||||
</VisualBrush>
|
||||
</Grid.Background>
|
||||
|
||||
<!-- No need to provide LEDs to highlight as LEDs are already physically highlighted -->
|
||||
<shared:DeviceVisualizer Device="{Binding Device}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
ShowColors="True"
|
||||
LedClicked="{s:Action OnLedClicked}"
|
||||
Margin="20"/>
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Visibility="{Binding Device.Layout.RgbLayout.Author, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="15">
|
||||
Device layout by <Run FontWeight="Bold" Text="{Binding Device.Layout.RgbLayout.Author}"/>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="15" Margin="-15 0 0 0" Background="Transparent" HorizontalAlignment="Stretch" Panel.ZIndex="3" />
|
||||
|
||||
<materialDesign:Card Grid.Column="2"
|
||||
materialDesign:ShadowAssist.ShadowDepth="Depth3"
|
||||
Background="{DynamicResource MaterialDesignPaper}">
|
||||
<Grid>
|
||||
<TabControl
|
||||
Style="{StaticResource MaterialDesignTabControl}"
|
||||
ItemsSource="{Binding Items}"
|
||||
SelectedItem="{Binding ActiveItem}"
|
||||
DisplayMemberPath="DisplayName">
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}"
|
||||
VerticalContentAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
IsTabStop="False"
|
||||
TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
<materialDesign:Snackbar x:Name="DeviceSnackbar"
|
||||
MessageQueue="{Binding DeviceMessageQueue}"
|
||||
materialDesign:SnackbarMessage.InlineActionButtonMaxHeight="80"
|
||||
materialDesign:SnackbarMessage.ContentMaxHeight="200" />
|
||||
</Grid>
|
||||
|
||||
</materialDesign:Card>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</materialDesign:DialogHost>
|
||||
</mde:MaterialWindow>
|
||||
@@ -1,261 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using System.Xml.Serialization;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.Shared;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Layout;
|
||||
using SkiaSharp;
|
||||
using Stylet;
|
||||
using Constants = Artemis.Core.Constants;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device
|
||||
{
|
||||
public class DeviceDialogViewModel : Conductor<Screen>.Collection.OneActive
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IDeviceDebugVmFactory _factory;
|
||||
private readonly IRgbService _rgbService;
|
||||
private SnackbarMessageQueue _deviceMessageQueue;
|
||||
private BindableCollection<ArtemisLed> _selectedLeds;
|
||||
private ArtemisDevice _device;
|
||||
|
||||
public DeviceDialogViewModel(ArtemisDevice device,
|
||||
ICoreService coreService,
|
||||
IDeviceService deviceService,
|
||||
IRgbService rgbService,
|
||||
IDialogService dialogService,
|
||||
IDeviceDebugVmFactory factory)
|
||||
{
|
||||
_coreService = coreService;
|
||||
_deviceService = deviceService;
|
||||
_rgbService = rgbService;
|
||||
_dialogService = dialogService;
|
||||
_factory = factory;
|
||||
|
||||
PanZoomViewModel = new PanZoomViewModel();
|
||||
SelectedLeds = new BindableCollection<ArtemisLed>();
|
||||
|
||||
Initialize(device);
|
||||
}
|
||||
|
||||
private void Initialize(ArtemisDevice device)
|
||||
{
|
||||
if (SelectedLeds.Any())
|
||||
SelectedLeds.Clear();
|
||||
|
||||
if (Device != null)
|
||||
Device.DeviceUpdated -= DeviceOnDeviceUpdated;
|
||||
Device = device;
|
||||
Device.DeviceUpdated += DeviceOnDeviceUpdated;
|
||||
|
||||
int activeTabIndex = 0;
|
||||
if (Items.Any())
|
||||
{
|
||||
activeTabIndex = Items.IndexOf(ActiveItem);
|
||||
Items.Clear();
|
||||
}
|
||||
Items.Add(_factory.DevicePropertiesTabViewModel(Device));
|
||||
if (Device.DeviceType == RGBDeviceType.Keyboard)
|
||||
Items.Add(_factory.InputMappingsTabViewModel(Device));
|
||||
Items.Add(_factory.DeviceInfoTabViewModel(Device));
|
||||
Items.Add(_factory.DeviceLedsTabViewModel(Device));
|
||||
|
||||
ActiveItem = Items[activeTabIndex];
|
||||
DisplayName = $"{Device.RgbDevice.DeviceInfo.Model} | Artemis";
|
||||
}
|
||||
|
||||
public ArtemisDevice Device
|
||||
{
|
||||
get => _device;
|
||||
set => SetAndNotify(ref _device, value);
|
||||
}
|
||||
|
||||
public PanZoomViewModel PanZoomViewModel { get; }
|
||||
|
||||
public SnackbarMessageQueue DeviceMessageQueue
|
||||
{
|
||||
get => _deviceMessageQueue;
|
||||
set => SetAndNotify(ref _deviceMessageQueue, value);
|
||||
}
|
||||
|
||||
public bool CanExportLayout => Device.Layout?.IsValid ?? false;
|
||||
|
||||
public BindableCollection<ArtemisLed> SelectedLeds
|
||||
{
|
||||
get => _selectedLeds;
|
||||
set => SetAndNotify(ref _selectedLeds, value);
|
||||
}
|
||||
|
||||
public bool CanOpenImageDirectory => Device.Layout?.Image != null;
|
||||
|
||||
public void OnLedClicked(object sender, LedClickedEventArgs e)
|
||||
{
|
||||
if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
|
||||
SelectedLeds.Clear();
|
||||
SelectedLeds.Add(e.Led);
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
_coreService.FrameRendering += CoreServiceOnFrameRendering;
|
||||
_rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
DeviceMessageQueue = new SnackbarMessageQueue(TimeSpan.FromSeconds(5));
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
private void RgbServiceOnDeviceAdded(object sender, DeviceEventArgs e)
|
||||
{
|
||||
if (e.Device != Device && e.Device.Identifier == Device.Identifier)
|
||||
Execute.OnUIThread(() => Initialize(e.Device));
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
_coreService.FrameRendering -= CoreServiceOnFrameRendering;
|
||||
_rgbService.DeviceAdded -= RgbServiceOnDeviceAdded;
|
||||
Device.DeviceUpdated -= DeviceOnDeviceUpdated;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e)
|
||||
{
|
||||
if (SelectedLeds == null || !SelectedLeds.Any())
|
||||
return;
|
||||
|
||||
using SKPaint highlightPaint = new() {Color = SKColors.White};
|
||||
using SKPaint dimPaint = new() {Color = new SKColor(0, 0, 0, 192)};
|
||||
foreach (ArtemisLed artemisLed in Device.Leds)
|
||||
e.Canvas.DrawRect(artemisLed.AbsoluteRectangle, SelectedLeds.Contains(artemisLed) ? highlightPaint : dimPaint);
|
||||
}
|
||||
|
||||
private void DeviceOnDeviceUpdated(object sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(CanExportLayout));
|
||||
}
|
||||
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
#region Command handlers
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
SelectedLeds.Clear();
|
||||
}
|
||||
|
||||
public void IdentifyDevice()
|
||||
{
|
||||
_deviceService.IdentifyDevice(Device);
|
||||
}
|
||||
|
||||
public void OpenPluginDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Device.DeviceProvider.Plugin.Directory.FullName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenImageDirectory()
|
||||
{
|
||||
if (!CanOpenImageDirectory)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.GetDirectoryName(Device.Layout.Image.AbsolutePath));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's image folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReloadLayout()
|
||||
{
|
||||
_rgbService.ApplyBestDeviceLayout(Device);
|
||||
}
|
||||
|
||||
public void ExportLayout()
|
||||
{
|
||||
if (Device.Layout == null)
|
||||
return;
|
||||
|
||||
VistaFolderBrowserDialog dialog = new()
|
||||
{
|
||||
Description = "Select layout export target folder",
|
||||
UseDescriptionForTitle = true,
|
||||
ShowNewFolderButton = true,
|
||||
SelectedPath = Path.Combine(Constants.DataFolder, "user layouts")
|
||||
};
|
||||
|
||||
bool? result = dialog.ShowDialog();
|
||||
if (result != true)
|
||||
return;
|
||||
|
||||
string directory = Path.Combine(
|
||||
dialog.SelectedPath,
|
||||
Device.RgbDevice.DeviceInfo.Manufacturer,
|
||||
Device.DeviceType.ToString()
|
||||
);
|
||||
|
||||
string filePath = Path.Combine(directory, Device.DeviceProvider.GetDeviceLayoutName(Device));
|
||||
Core.Utilities.CreateAccessibleDirectory(directory);
|
||||
|
||||
// XML
|
||||
XmlSerializer serializer = new(typeof(DeviceLayout));
|
||||
using StreamWriter writer = new(filePath);
|
||||
serializer.Serialize(writer, Device.Layout!.RgbLayout);
|
||||
|
||||
// Device images
|
||||
if (!Uri.IsWellFormedUriString(Device.Layout.FilePath, UriKind.Absolute))
|
||||
return;
|
||||
|
||||
Uri targetDirectory = new(directory + "/", UriKind.Absolute);
|
||||
Uri sourceDirectory = new(Path.GetDirectoryName(Device.Layout.FilePath)! + "/", UriKind.Absolute);
|
||||
Uri deviceImageTarget = new(targetDirectory, Device.Layout.LayoutCustomDeviceData.DeviceImage);
|
||||
|
||||
// Create folder (if needed) and copy image
|
||||
Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(deviceImageTarget.LocalPath)!);
|
||||
if (Device.Layout.Image != null && File.Exists(Device.Layout.Image.LocalPath) && !File.Exists(deviceImageTarget.LocalPath))
|
||||
File.Copy(Device.Layout.Image.LocalPath, deviceImageTarget.LocalPath);
|
||||
|
||||
foreach (ArtemisLedLayout ledLayout in Device.Layout.Leds)
|
||||
{
|
||||
if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
|
||||
continue;
|
||||
|
||||
// Only the image of the current logical layout is available as an URI, iterate each layout and find the images manually
|
||||
foreach (LayoutCustomLedDataLogicalLayout logicalLayout in ledLayout.LayoutCustomLedData.LogicalLayouts)
|
||||
{
|
||||
Uri image = new(sourceDirectory, logicalLayout.Image);
|
||||
Uri imageTarget = new(targetDirectory, logicalLayout.Image);
|
||||
|
||||
// Create folder (if needed) and copy image
|
||||
Core.Utilities.CreateAccessibleDirectory(Path.GetDirectoryName(imageTarget.LocalPath)!);
|
||||
if (File.Exists(image.LocalPath) && !File.Exists(imageTarget.LocalPath))
|
||||
File.Copy(image.LocalPath, imageTarget.LocalPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// ReSharper restore UnusedMember.Global
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Device.DeviceLayoutDialogView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Device"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mde="https://spiegelp.github.io/MaterialDesignExtensions/winfx/xaml"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="1200" d:DesignWidth="1200"
|
||||
d:DataContext="{d:DesignInstance local:DeviceLayoutDialogViewModel}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" Margin="16" Width="1200" Height="800" Visibility="{Binding SelectPhysicalLayout, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 0 0 20">
|
||||
Select a physical layout
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap">
|
||||
Artemis couldn't automatically determine the physical layout of your <Run Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName, Mode=OneWay}" />. <LineBreak />
|
||||
In order for Artemis to know which keys are on your keyboard and where they're located, select the matching layout below.
|
||||
<LineBreak />
|
||||
<LineBreak />
|
||||
P.S. Don't worry about missing special keys like num keys/function keys or macro keys, they aren't important here.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" Margin="0 25">
|
||||
<WrapPanel HorizontalAlignment="Center">
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="ISO"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/iso.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
ISO
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in the EU (tall enter)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="ANSI"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/ansi.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
ANSI
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in the US (short enter)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="ABNT"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/abnt.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
ABNT
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in Brazil/Portugal (based on ISO)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="KS"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/ks.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
KS
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in South Korea
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Command="{s:Action ApplyPhysicalLayout}"
|
||||
CommandParameter="JIS"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="550"
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<Image Source="/Resources/Images/PhysicalLayouts/jis.png" />
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0 10 0 0">
|
||||
JIS
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Style="{StaticResource MaterialDesignBody2TextBlock}" TextWrapping="Wrap">
|
||||
Most commonly used in Japan (based on ISO)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
</WrapPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="0" Margin="16" Width="800" Visibility="{Binding SelectPhysicalLayout, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 0 0 20">
|
||||
Select a logical layout
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap">
|
||||
Artemis couldn't automatically determine the logical layout of your <Run Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName, Mode=OneWay}" />. <LineBreak /><LineBreak />
|
||||
While not as important as the physical layout, setting the correct logical layout will allow Artemis to show the right keycaps (if a matching layout file is present)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<mde:Autocomplete Style="{StaticResource MaterialDesignAutocomplete}"
|
||||
AutocompleteSource="{Binding Path=AutocompleteSource}"
|
||||
SelectedItem="{Binding SelectedRegion, Mode=TwoWay}"
|
||||
SearchOnInitialFocus="True"
|
||||
Hint="Select a logical layout"
|
||||
Margin="0,16,0,0"
|
||||
FontSize="15"
|
||||
Foreground="{DynamicResource MaterialDesignBody}"
|
||||
Background="{DynamicResource MaterialDesignPaper}">
|
||||
<mde:Autocomplete.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock FontSize="14" VerticalAlignment="Center">
|
||||
<Run Text="{Binding EnglishName, Mode=OneWay}" />
|
||||
(<Run FontWeight="SemiBold" Text="{Binding TwoLetterISORegionName, Mode=OneWay}" />)
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</mde:Autocomplete.ItemTemplate>
|
||||
</mde:Autocomplete>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="16">
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Focusable="False"
|
||||
IsCancel="True"
|
||||
Command="{s:Action Cancel}"
|
||||
Content="CANCEL" />
|
||||
<Button x:Name="ConfirmButton"
|
||||
Style="{StaticResource MaterialDesignFlatButton}"
|
||||
IsDefault="True"
|
||||
Focusable="True"
|
||||
Command="{s:Action Confirm}"
|
||||
Content="CONFIRM" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -1,128 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using MaterialDesignExtensions.Model;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device
|
||||
{
|
||||
public class DeviceLayoutDialogViewModel : DialogViewModelBase
|
||||
{
|
||||
private readonly IRgbService _rgbService;
|
||||
private bool _selectPhysicalLayout;
|
||||
private RegionInfo _selectedRegion;
|
||||
|
||||
public DeviceLayoutDialogViewModel(ArtemisDevice device, IRgbService rgbService, IDialogService dialogService)
|
||||
{
|
||||
_rgbService = rgbService;
|
||||
Device = device;
|
||||
SelectPhysicalLayout = !device.DeviceProvider.CanDetectPhysicalLayout;
|
||||
|
||||
try
|
||||
{
|
||||
AutocompleteSource = new RegionInfoAutocompleteSource();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
dialogService.ShowExceptionDialog("Failed to get region information for keyboard layout selection", e);
|
||||
Session?.Close(false);
|
||||
}
|
||||
}
|
||||
|
||||
public ArtemisDevice Device { get; }
|
||||
|
||||
public RegionInfoAutocompleteSource AutocompleteSource { get; }
|
||||
|
||||
public RegionInfo SelectedRegion
|
||||
{
|
||||
get => _selectedRegion;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _selectedRegion, value);
|
||||
NotifyOfPropertyChange(nameof(CanConfirm));
|
||||
}
|
||||
}
|
||||
|
||||
public bool SelectPhysicalLayout
|
||||
{
|
||||
get => _selectPhysicalLayout;
|
||||
set => SetAndNotify(ref _selectPhysicalLayout, value);
|
||||
}
|
||||
|
||||
public bool CanConfirm => SelectedRegion != null;
|
||||
|
||||
public void ApplyPhysicalLayout(string physicalLayout)
|
||||
{
|
||||
Device.PhysicalLayout = Enum.Parse<KeyboardLayoutType>(physicalLayout);
|
||||
|
||||
_rgbService.SaveDevice(Device);
|
||||
_rgbService.ApplyBestDeviceLayout(Device);
|
||||
|
||||
SelectPhysicalLayout = false;
|
||||
}
|
||||
|
||||
private void ApplyLogicalLayout(string logicalLayout)
|
||||
{
|
||||
Device.LogicalLayout = logicalLayout;
|
||||
|
||||
_rgbService.SaveDevice(Device);
|
||||
_rgbService.ApplyBestDeviceLayout(Device);
|
||||
}
|
||||
|
||||
public void Confirm()
|
||||
{
|
||||
if (!CanConfirm || Session == null || Session.IsEnded)
|
||||
return;
|
||||
|
||||
ApplyLogicalLayout(SelectedRegion.TwoLetterISORegionName);
|
||||
Session?.Close(true);
|
||||
}
|
||||
}
|
||||
|
||||
public class RegionInfoAutocompleteSource : IAutocompleteSource<RegionInfo>
|
||||
{
|
||||
private const int LOCALE_NEUTRAL = 0x0000;
|
||||
private const int LOCALE_CUSTOM_DEFAULT = 0x0c00;
|
||||
private const int LOCALE_INVARIANT = 0x007F;
|
||||
|
||||
public List<RegionInfo> Regions { get; set; }
|
||||
|
||||
public RegionInfoAutocompleteSource()
|
||||
{
|
||||
// RegionInfo does not support some LCIDs and they show up with certain locale settings
|
||||
Regions = CultureInfo.GetCultures(CultureTypes.SpecificCultures)
|
||||
.Where(c => c.LCID != LOCALE_INVARIANT && c.LCID != LOCALE_NEUTRAL && c.LCID != LOCALE_CUSTOM_DEFAULT)
|
||||
.Select(c => new RegionInfo(c.LCID))
|
||||
.GroupBy(r => r.EnglishName)
|
||||
.Select(g => g.First())
|
||||
.OrderBy(r => r.EnglishName)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
IEnumerable<RegionInfo> IAutocompleteSource<RegionInfo>.Search(string searchTerm)
|
||||
{
|
||||
if (searchTerm == null)
|
||||
return Regions;
|
||||
|
||||
searchTerm = searchTerm.ToLower();
|
||||
return Regions.Where(r => r.EnglishName.ToLower().Contains(searchTerm) ||
|
||||
r.NativeName.ToLower().Contains(searchTerm) ||
|
||||
r.TwoLetterISORegionName.ToLower().Contains(searchTerm));
|
||||
}
|
||||
|
||||
public IEnumerable Search(string searchTerm)
|
||||
{
|
||||
if (searchTerm == null)
|
||||
return Regions;
|
||||
|
||||
searchTerm = searchTerm.ToLower();
|
||||
return Regions.Where(r => r.EnglishName.ToLower().Contains(searchTerm) ||
|
||||
r.NativeName.ToLower().Contains(searchTerm) ||
|
||||
r.TwoLetterISORegionName.ToLower().Contains(searchTerm));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DeviceInfoTabView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Settings.Device.Tabs"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance tabs:DeviceInfoTabViewModel}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- First row -->
|
||||
<StackPanel HorizontalAlignment="Stretch" Grid.Column="0" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Top" Margin="15 15 7.5 0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Device name</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Manufacturer</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Device type</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.DeviceType}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
|
||||
<StackPanel Visibility="{Binding IsKeyboard, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Physical layout</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.PhysicalLayout}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Top" Margin="7.5 15 15 0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Size (1px = 1mm)</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.RgbDevice.Size}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Location (1px = 1mm)</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.RgbDevice.Location}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Rotation (degrees)</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.RgbDevice.Rotation.Degrees}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
|
||||
<StackPanel Visibility="{Binding IsKeyboard, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Logical layout</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.LogicalLayout}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Second row -->
|
||||
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="15 0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Default layout file path</TextBlock>
|
||||
<Button Grid.Column="1"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="Copy path to clipboard"
|
||||
Width="24"
|
||||
Height="24"
|
||||
Command="{s:Action CopyToClipboard}"
|
||||
CommandParameter="{Binding DefaultLayoutPath}">
|
||||
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding DefaultLayoutPath}" />
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="0 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Image file path</TextBlock>
|
||||
<Button Grid.Column="1" Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="Copy path to clipboard"
|
||||
Width="24"
|
||||
Height="24"
|
||||
Command="{s:Action CopyToClipboard}"
|
||||
CommandParameter="{Binding Device.Layout.Image.LocalPath}">
|
||||
<materialDesign:PackIcon Kind="ContentCopy" Width="18" Height="18" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Device.Layout.Image.LocalPath}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.Windows;
|
||||
using Artemis.Core;
|
||||
using RGB.NET.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
{
|
||||
public class DeviceInfoTabViewModel : Screen
|
||||
{
|
||||
private string _defaultLayoutPath;
|
||||
|
||||
public DeviceInfoTabViewModel(ArtemisDevice device)
|
||||
{
|
||||
Device = device;
|
||||
DisplayName = "INFO";
|
||||
}
|
||||
|
||||
public bool IsKeyboard => Device.DeviceType == RGBDeviceType.Keyboard;
|
||||
public ArtemisDevice Device { get; }
|
||||
|
||||
public string DefaultLayoutPath
|
||||
{
|
||||
get => _defaultLayoutPath;
|
||||
set => SetAndNotify(ref _defaultLayoutPath, value);
|
||||
}
|
||||
|
||||
public void CopyToClipboard(string content)
|
||||
{
|
||||
Clipboard.SetText(content);
|
||||
((DeviceDialogViewModel) Parent).DeviceMessageQueue.Enqueue("Copied path to clipboard.");
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath;
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Settings.Device.Tabs"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DeviceLedsTabView"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type tabs:DeviceLedsTabViewModel}}">
|
||||
<UserControl.Resources>
|
||||
<converters:UriToFileNameConverter x:Key="UriToFileNameConverter" />
|
||||
<converters:LedIdToStringConverter x:Key="LedIdToStringConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<DataGrid ItemsSource="{Binding LedViewModels}"
|
||||
d:DataContext="{d:DesignInstance Type={x:Type tabs:DeviceLedsTabLedViewModel}}"
|
||||
CanUserSortColumns="True"
|
||||
IsReadOnly="True"
|
||||
CanUserAddRows="False"
|
||||
AutoGenerateColumns="False"
|
||||
materialDesign:DataGridAssist.CellPadding="16 5 5 5"
|
||||
materialDesign:DataGridAssist.ColumnHeaderPadding="16 5 5 5"
|
||||
CanUserResizeRows="False"
|
||||
VirtualizingStackPanel.VirtualizationMode="Standard"
|
||||
Margin="10">
|
||||
<DataGrid.Resources>
|
||||
<Style TargetType="DataGridRow" BasedOn="{StaticResource MaterialDesignDataGridRow}">
|
||||
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
<DataGrid.Columns>
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Id, Converter={StaticResource LedIdToStringConverter}, Mode=OneWay}" Header="LED ID" Width="Auto" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Color}" Header="Color (ARGB)" Width="Auto" CanUserSort="False" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding ArtemisLed.Layout.Image, Converter={StaticResource UriToFileNameConverter}, Mode=OneWay}" Header="Image file" CanUserSort="False" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Shape}" Header="Shape" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.Size}" Header="Size" Width="Auto" CanUserSort="False" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding ArtemisLed.RgbLed.CustomData}" Header="LED data" Width="Auto" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,90 +0,0 @@
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
{
|
||||
public class DeviceLedsTabViewModel : Screen
|
||||
{
|
||||
public DeviceLedsTabViewModel(ArtemisDevice device)
|
||||
{
|
||||
Device = device;
|
||||
DisplayName = "LEDS";
|
||||
LedViewModels = new BindableCollection<DeviceLedsTabLedViewModel>();
|
||||
}
|
||||
|
||||
public ArtemisDevice Device { get; }
|
||||
public BindableCollection<DeviceLedsTabLedViewModel> LedViewModels { get; }
|
||||
|
||||
private void SelectedLedsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateSelectedLeds();
|
||||
}
|
||||
|
||||
private void UpdateSelectedLeds()
|
||||
{
|
||||
foreach (DeviceLedsTabLedViewModel deviceLedsTabLedViewModel in LedViewModels)
|
||||
deviceLedsTabLedViewModel.Update();
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
BindableCollection<ArtemisLed> selectedLeds = ((DeviceDialogViewModel) Parent).SelectedLeds;
|
||||
LedViewModels.Clear();
|
||||
LedViewModels.AddRange(Device.Leds.Select(l => new DeviceLedsTabLedViewModel(l, selectedLeds)));
|
||||
selectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged;
|
||||
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
((DeviceDialogViewModel) Parent).SelectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged;
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class DeviceLedsTabLedViewModel : PropertyChangedBase
|
||||
{
|
||||
private readonly BindableCollection<ArtemisLed> _selectedLeds;
|
||||
private bool _isSelected;
|
||||
|
||||
public DeviceLedsTabLedViewModel(ArtemisLed artemisLed, BindableCollection<ArtemisLed> selectedLeds)
|
||||
{
|
||||
_selectedLeds = selectedLeds;
|
||||
ArtemisLed = artemisLed;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
public ArtemisLed ArtemisLed { get; }
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _isSelected, value)) return;
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
IsSelected = _selectedLeds.Contains(ArtemisLed);
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (IsSelected && !_selectedLeds.Contains(ArtemisLed))
|
||||
_selectedLeds.Add(ArtemisLed);
|
||||
else if (!IsSelected && _selectedLeds.Contains(ArtemisLed))
|
||||
_selectedLeds.Remove(ArtemisLed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Device.Tabs.DevicePropertiesTabView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Device.Tabs"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:DevicePropertiesTabViewModel}">
|
||||
<UserControl.Resources>
|
||||
<shared:SKColorToColorConverter x:Key="SKColorToColorConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<!-- Body -->
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<!-- Layout -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
|
||||
Categories
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Justify">
|
||||
Artemis uses categories to determine where the layers of imported profiles are applied to. <LineBreak />
|
||||
You can hover over a category for a more detailed description.
|
||||
</TextBlock>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox Style="{StaticResource MaterialDesignFilterChipOutlineCheckBox}"
|
||||
IsChecked="{Binding HasDeskCategory}"
|
||||
ToolTip="A device acting as desk ornamentation such as a LED strip"
|
||||
Content="Desk" />
|
||||
<CheckBox Style="{StaticResource MaterialDesignFilterChipOutlineCheckBox}"
|
||||
IsChecked="{Binding HasMonitorCategory}"
|
||||
ToolTip="A device attached to the monitor such as ambilight LEDs"
|
||||
Content="Monitor" />
|
||||
<CheckBox Style="{StaticResource MaterialDesignFilterChipOutlineCheckBox}"
|
||||
ToolTip="A device inside your computer case"
|
||||
IsChecked="{Binding HasCaseCategory}"
|
||||
Content="Case" />
|
||||
<CheckBox Style="{StaticResource MaterialDesignFilterChipOutlineCheckBox}"
|
||||
IsChecked="{Binding HasRoomCategory}"
|
||||
ToolTip="A device elsewhere in the room"
|
||||
Content="Room" />
|
||||
<CheckBox Style="{StaticResource MaterialDesignFilterChipOutlineCheckBox}"
|
||||
IsChecked="{Binding HasPeripheralsCategory}"
|
||||
ToolTip="A peripheral such as a mouse or keyboard"
|
||||
Content="Peripheral" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid Margin="0 25">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="40" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
|
||||
Surface properties
|
||||
</TextBlock>
|
||||
|
||||
<TextBox materialDesign:HintAssist.Hint="X-coordinate"
|
||||
materialDesign:TextFieldAssist.SuffixText="mm"
|
||||
Text="{Binding X, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Margin="0 5" />
|
||||
<TextBox materialDesign:HintAssist.Hint="Y-coordinate"
|
||||
materialDesign:TextFieldAssist.SuffixText="mm"
|
||||
Text="{Binding Y, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Margin="0 5" />
|
||||
<TextBox materialDesign:HintAssist.Hint="Scale"
|
||||
materialDesign:TextFieldAssist.SuffixText="times"
|
||||
Text="{Binding Scale, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Margin="0 5" />
|
||||
<TextBox materialDesign:HintAssist.Hint="Rotation"
|
||||
materialDesign:TextFieldAssist.SuffixText="deg"
|
||||
Text="{Binding Rotation, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Margin="0 5 0 12" />
|
||||
</StackPanel>
|
||||
|
||||
<Rectangle Grid.Column="1" VerticalAlignment="Stretch" Fill="{StaticResource MaterialDesignTextBoxBorder}" Width="1" Margin="0 0 0 5" />
|
||||
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
|
||||
Color calibration
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Justify">
|
||||
Use the sliders below to adjust the colors of your device so that it matches your other devices.
|
||||
</TextBlock>
|
||||
|
||||
<Grid Margin="0 3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" Content="R" VerticalAlignment="Center" />
|
||||
<Slider Grid.Column="1"
|
||||
Minimum="0"
|
||||
Maximum="200"
|
||||
ValueChanged="{s:Action ApplyScaling}"
|
||||
Value="{Binding RedScale, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="10"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Width="70"
|
||||
Text="{Binding RedScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
|
||||
materialDesign:TextFieldAssist.SuffixText="%"
|
||||
materialDesign:HintAssist.IsFloating="False" />
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="0 3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" Content="G" VerticalAlignment="Center" />
|
||||
<Slider Grid.Column="1"
|
||||
Minimum="0"
|
||||
Maximum="200"
|
||||
ValueChanged="{s:Action ApplyScaling}"
|
||||
Value="{Binding GreenScale, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="10"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Width="70"
|
||||
Text="{Binding GreenScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
|
||||
materialDesign:TextFieldAssist.SuffixText="%"
|
||||
materialDesign:HintAssist.IsFloating="False" />
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="0 2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Grid.Column="0" Content="B" VerticalAlignment="Center" />
|
||||
<Slider Grid.Column="1"
|
||||
Minimum="0"
|
||||
Maximum="200"
|
||||
ValueChanged="{s:Action ApplyScaling}"
|
||||
Value="{Binding BlueScale, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="10"
|
||||
Ticks="100"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Width="70"
|
||||
Text="{Binding BlueScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
|
||||
materialDesign:TextFieldAssist.SuffixText="%"
|
||||
materialDesign:HintAssist.IsFloating="False" />
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="0 3 0 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Column="0"
|
||||
IsChecked="{Binding DisplayOnDevices}"
|
||||
Content="Show preview"
|
||||
VerticalAlignment="Center" />
|
||||
<shared:ColorPicker Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
|
||||
TextBoxStyle="{StaticResource MaterialDesignFilledTextBox}"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Layout -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
|
||||
Layout
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Justify">
|
||||
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" Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding Device.DisableDefaultLayout, Delay=300}">
|
||||
<CheckBox.Content>
|
||||
<TextBlock Margin="0 -5 0 0">
|
||||
Don't load default layout
|
||||
<materialDesign:PackIcon Kind="HelpCircle"
|
||||
ToolTip="With this enabled Artemis will not load a layout for this device unless you specifically provide one." />
|
||||
</TextBlock>
|
||||
</CheckBox.Content>
|
||||
</CheckBox>
|
||||
|
||||
<TextBox Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Text="{Binding Device.CustomLayoutPath}"
|
||||
VerticalAlignment="Center"
|
||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||
IsReadOnly="True"
|
||||
PreviewMouseLeftButtonUp="{s:Action BrowseCustomLayout}">
|
||||
<materialDesign:HintAssist.Hint>
|
||||
<StackPanel Orientation="Horizontal" Margin="-2 0 0 0">
|
||||
<materialDesign:PackIcon Kind="Xml" Width="20" />
|
||||
<TextBlock>Custom layout path</TextBlock>
|
||||
</StackPanel>
|
||||
</materialDesign:HintAssist.Hint>
|
||||
</TextBox>
|
||||
<TextBlock Style="{StaticResource MaterialDesignCaptionTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Justify">
|
||||
Select a custom layout below if you want to change the appearance and/or LEDs of this device.
|
||||
For info on how to create layouts, check out
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
NavigateUri="https://wiki.artemis-rgb.com/en/guides/developer/layouts">
|
||||
this wiki article
|
||||
<b:Interaction.Behaviors>
|
||||
<shared:OpenInBrowser />
|
||||
</b:Interaction.Behaviors>
|
||||
</Hyperlink>
|
||||
.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Buttons -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0"
|
||||
Style="{StaticResource MaterialDesignOutlinedButton}"
|
||||
Margin="0 8 8 0"
|
||||
Command="{s:Action SelectPhysicalLayout}"
|
||||
ToolTip="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 Style="{StaticResource MaterialDesignOutlinedButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Reset}">
|
||||
RESET
|
||||
</Button>
|
||||
<Button Style="{StaticResource MaterialDesignRaisedButton}" IsDefault="True" Margin="0 8 8 0" Command="{s:Action Apply}">
|
||||
APPLY
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,278 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using RGB.NET.Core;
|
||||
using SkiaSharp;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
{
|
||||
public class DevicePropertiesTabViewModel : Screen
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private float _blueScale;
|
||||
private SKColor _currentColor;
|
||||
private bool _displayOnDevices;
|
||||
private float _greenScale;
|
||||
private List<DeviceCategory> _categories;
|
||||
private float _initialBlueScale;
|
||||
private float _initialGreenScale;
|
||||
private float _initialRedScale;
|
||||
private float _redScale;
|
||||
private int _rotation;
|
||||
private float _scale;
|
||||
private int _x;
|
||||
private int _y;
|
||||
|
||||
public DevicePropertiesTabViewModel(ArtemisDevice device,
|
||||
ICoreService coreService,
|
||||
IRgbService rgbService,
|
||||
IDialogService dialogService,
|
||||
IModelValidator<DevicePropertiesTabViewModel> validator) : base(validator)
|
||||
{
|
||||
_coreService = coreService;
|
||||
_rgbService = rgbService;
|
||||
_dialogService = dialogService;
|
||||
|
||||
Device = device;
|
||||
DisplayName = "PROPERTIES";
|
||||
}
|
||||
|
||||
public ArtemisDevice Device { get; }
|
||||
|
||||
public int X
|
||||
{
|
||||
get => _x;
|
||||
set => SetAndNotify(ref _x, value);
|
||||
}
|
||||
|
||||
public int Y
|
||||
{
|
||||
get => _y;
|
||||
set => SetAndNotify(ref _y, value);
|
||||
}
|
||||
|
||||
public float Scale
|
||||
{
|
||||
get => _scale;
|
||||
set => SetAndNotify(ref _scale, value);
|
||||
}
|
||||
|
||||
public int Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
set => SetAndNotify(ref _rotation, value);
|
||||
}
|
||||
|
||||
public float RedScale
|
||||
{
|
||||
get => _redScale;
|
||||
set => SetAndNotify(ref _redScale, value);
|
||||
}
|
||||
|
||||
public float GreenScale
|
||||
{
|
||||
get => _greenScale;
|
||||
set => SetAndNotify(ref _greenScale, value);
|
||||
}
|
||||
|
||||
public float BlueScale
|
||||
{
|
||||
get => _blueScale;
|
||||
set => SetAndNotify(ref _blueScale, value);
|
||||
}
|
||||
|
||||
public SKColor CurrentColor
|
||||
{
|
||||
get => _currentColor;
|
||||
set => SetAndNotify(ref _currentColor, value);
|
||||
}
|
||||
|
||||
public bool DisplayOnDevices
|
||||
{
|
||||
get => _displayOnDevices;
|
||||
set => SetAndNotify(ref _displayOnDevices, value);
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
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 void ApplyScaling()
|
||||
{
|
||||
Device.RedScale = RedScale / 100f;
|
||||
Device.GreenScale = GreenScale / 100f;
|
||||
Device.BlueScale = BlueScale / 100f;
|
||||
|
||||
_rgbService.FlushLeds = true;
|
||||
}
|
||||
|
||||
public void BrowseCustomLayout(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is Button)
|
||||
{
|
||||
Device.CustomLayoutPath = null;
|
||||
((DeviceDialogViewModel) Parent).DeviceMessageQueue.Enqueue("Cleared imported layout.");
|
||||
return;
|
||||
}
|
||||
|
||||
VistaOpenFileDialog dialog = new();
|
||||
dialog.Filter = "Layout files (*.xml)|*.xml";
|
||||
dialog.Title = "Select device layout file";
|
||||
bool? result = dialog.ShowDialog();
|
||||
if (result == true)
|
||||
{
|
||||
Device.CustomLayoutPath = dialog.FileName;
|
||||
((DeviceDialogViewModel) Parent).DeviceMessageQueue.Enqueue($"Imported layout from {dialog.FileName}.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SelectPhysicalLayout()
|
||||
{
|
||||
await _dialogService.ShowDialogAt<DeviceLayoutDialogViewModel>("DeviceDialog", new Dictionary<string, object> {{"device", Device}});
|
||||
}
|
||||
|
||||
public async Task Apply()
|
||||
{
|
||||
await ValidateAsync();
|
||||
if (HasErrors)
|
||||
return;
|
||||
|
||||
_coreService.ProfileRenderingDisabled = true;
|
||||
await Task.Delay(100);
|
||||
|
||||
Device.X = X;
|
||||
Device.Y = Y;
|
||||
Device.Scale = Scale;
|
||||
Device.Rotation = Rotation;
|
||||
Device.RedScale = RedScale / 100f;
|
||||
Device.GreenScale = GreenScale / 100f;
|
||||
Device.BlueScale = BlueScale / 100f;
|
||||
Device.Categories.Clear();
|
||||
foreach (DeviceCategory deviceCategory in _categories)
|
||||
Device.Categories.Add(deviceCategory);
|
||||
|
||||
_rgbService.SaveDevice(Device);
|
||||
|
||||
_coreService.ProfileRenderingDisabled = false;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
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 = _initialRedScale;
|
||||
Device.GreenScale = _initialGreenScale;
|
||||
Device.BlueScale = _initialBlueScale;
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
X = (int) Device.X;
|
||||
Y = (int) Device.Y;
|
||||
Scale = Device.Scale;
|
||||
Rotation = (int) Device.Rotation;
|
||||
RedScale = Device.RedScale * 100f;
|
||||
GreenScale = Device.GreenScale * 100f;
|
||||
BlueScale = Device.BlueScale * 100f;
|
||||
//we need to store the initial values to be able to restore them when the user clicks "Cancel"
|
||||
_initialRedScale = Device.RedScale;
|
||||
_initialGreenScale = Device.GreenScale;
|
||||
_initialBlueScale = Device.BlueScale;
|
||||
_categories = new List<DeviceCategory>(Device.Categories);
|
||||
CurrentColor = SKColors.White;
|
||||
|
||||
_coreService.FrameRendering += OnFrameRendering;
|
||||
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
_coreService.FrameRendering -= OnFrameRendering;
|
||||
Device.PropertyChanged -= DeviceOnPropertyChanged;
|
||||
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
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
|
||||
_categories.Remove(category);
|
||||
|
||||
NotifyOfPropertyChange($"Has{category}Category");
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void DeviceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Device.CustomLayoutPath) || e.PropertyName == nameof(Device.DisableDefaultLayout))
|
||||
{
|
||||
Task.Run(() => _rgbService.ApplyBestDeviceLayout(Device));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFrameRendering(object sender, FrameRenderingEventArgs e)
|
||||
{
|
||||
if (!_displayOnDevices)
|
||||
return;
|
||||
|
||||
using SKPaint overlayPaint = new()
|
||||
{
|
||||
Color = CurrentColor
|
||||
};
|
||||
e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Device.Tabs"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:Shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" x:Class="Artemis.UI.Screens.Settings.Device.Tabs.InputMappingsTabView"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type local:InputMappingsTabViewModel}}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<Shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
<Style x:Key="CenteredTextColumn" TargetType="TextBlock">
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}">
|
||||
<Run Text="Introduction" />
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" TextWrapping="Wrap">
|
||||
In some cases you may want Artemis to map key presses to different LEDs.
|
||||
This is useful when your logical layout swaps keys around (like Hungarian layouts where the Z and Y keys are swapped).<LineBreak /><LineBreak />
|
||||
In this tab you can set up these custom input mappings, simply click on a LED and press a key, Artemis will from then on consider that LED pressed whenever you press the same key again.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Margin="0 20" Visibility="{Binding SelectedLed, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextAlignment="Center">Select a LED in the preview on the left side to get started...</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Margin="0 20" Visibility="{Binding SelectedLed, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextAlignment="Center">
|
||||
<Run Text="Current target LED: " /><Run Text="{Binding SelectedLed.RgbLed.Id, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" TextAlignment="Center">
|
||||
<Run Text="Press the key you want to remap" /><Run Text=" " /><Run FontWeight="Bold" Text="{Binding SelectedLed.RgbLed.Id, Mode=OneWay}" /><Run Text=" " /><Run Text="to..." />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<DataGrid Grid.Row="2"
|
||||
ItemsSource="{Binding InputMappings}"
|
||||
CanUserSortColumns="True"
|
||||
IsReadOnly="True"
|
||||
CanUserAddRows="False"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserResizeRows="False"
|
||||
materialDesign:DataGridAssist.CellPadding="16 5 5 5"
|
||||
materialDesign:DataGridAssist.ColumnHeaderPadding="16 5 5 5"
|
||||
VirtualizingPanel.VirtualizationMode="Standard"
|
||||
Margin="10">
|
||||
<DataGrid.Columns>
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Item1.RgbLed.Id}" ElementStyle="{StaticResource CenteredTextColumn}" Header="Original LED ID" Width="*" />
|
||||
<materialDesign:DataGridTextColumn Binding="{Binding Item2.RgbLed.Id}" ElementStyle="{StaticResource CenteredTextColumn}" Header="Remapped LED ID" Width="*" />
|
||||
|
||||
<DataGridTemplateColumn Width="45" IsReadOnly="True">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button Command="{s:Action DeleteMapping}"
|
||||
CommandParameter="{Binding}"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="Delete mapping"
|
||||
Width="25"
|
||||
Height="25">
|
||||
<materialDesign:PackIcon Kind="Delete" Width="18" Height="18" />
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,103 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Exceptions;
|
||||
using RGB.NET.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
{
|
||||
public class InputMappingsTabViewModel : Screen
|
||||
{
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IInputService _inputService;
|
||||
private ArtemisLed _selectedLed;
|
||||
|
||||
public InputMappingsTabViewModel(ArtemisDevice device, IRgbService rgbService, IInputService inputService)
|
||||
{
|
||||
if (device.DeviceType != RGBDeviceType.Keyboard)
|
||||
throw new ArtemisUIException("The input mappings tab only supports keyboards");
|
||||
_rgbService = rgbService;
|
||||
_inputService = inputService;
|
||||
|
||||
Device = device;
|
||||
DisplayName = "INPUT MAPPINGS";
|
||||
InputMappings = new BindableCollection<Tuple<ArtemisLed, ArtemisLed>>();
|
||||
}
|
||||
|
||||
public ArtemisDevice Device { get; }
|
||||
|
||||
public ArtemisLed SelectedLed
|
||||
{
|
||||
get => _selectedLed;
|
||||
set => SetAndNotify(ref _selectedLed, value);
|
||||
}
|
||||
|
||||
public BindableCollection<Tuple<ArtemisLed, ArtemisLed>> InputMappings { get; }
|
||||
|
||||
public void DeleteMapping(Tuple<ArtemisLed, ArtemisLed> inputMapping)
|
||||
{
|
||||
Device.InputMappings.Remove(inputMapping.Item1);
|
||||
UpdateInputMappings();
|
||||
}
|
||||
|
||||
private void InputServiceOnKeyboardKeyUp(object sender, ArtemisKeyboardKeyEventArgs e)
|
||||
{
|
||||
if (SelectedLed == null || e.Led == null)
|
||||
return;
|
||||
|
||||
// Locate the original LED the same way the InputService did it, but supply false to Device.GetLed
|
||||
bool foundLedId = InputKeyUtilities.KeyboardKeyLedIdMap.TryGetValue(e.Key, out LedId ledId);
|
||||
if (!foundLedId)
|
||||
return;
|
||||
ArtemisLed artemisLed = Device.GetLed(ledId, false);
|
||||
if (artemisLed == null)
|
||||
return;
|
||||
|
||||
// Apply the new LED mapping
|
||||
Device.InputMappings[SelectedLed] = artemisLed;
|
||||
_rgbService.SaveDevice(Device);
|
||||
((DeviceDialogViewModel) Parent).SelectedLeds.Clear();
|
||||
|
||||
UpdateInputMappings();
|
||||
}
|
||||
|
||||
private void UpdateInputMappings()
|
||||
{
|
||||
if (InputMappings.Any())
|
||||
InputMappings.Clear();
|
||||
|
||||
InputMappings.AddRange(Device.InputMappings.Select(m => new Tuple<ArtemisLed, ArtemisLed>(m.Key, m.Value)));
|
||||
}
|
||||
|
||||
private void SelectedLedsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
SelectedLed = ((DeviceDialogViewModel) Parent).SelectedLeds.FirstOrDefault();
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnActivate()
|
||||
{
|
||||
UpdateInputMappings();
|
||||
_inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
|
||||
((DeviceDialogViewModel) Parent).SelectedLeds.CollectionChanged += SelectedLedsOnCollectionChanged;
|
||||
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDeactivate()
|
||||
{
|
||||
InputMappings.Clear();
|
||||
_inputService.KeyboardKeyUp -= InputServiceOnKeyboardKeyUp;
|
||||
((DeviceDialogViewModel) Parent).SelectedLeds.CollectionChanged -= SelectedLedsOnCollectionChanged;
|
||||
base.OnDeactivate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Device.Tabs
|
||||
{
|
||||
public class DevicePropertiesTabViewModelValidator : AbstractValidator<DevicePropertiesTabViewModel>
|
||||
{
|
||||
public DevicePropertiesTabViewModelValidator()
|
||||
{
|
||||
RuleFor(m => m.X).GreaterThanOrEqualTo(0).WithMessage("X-coordinate must be 0 or greater");
|
||||
|
||||
RuleFor(m => m.Y).GreaterThanOrEqualTo(0).WithMessage("Y-coordinate must be 0 or greater");
|
||||
|
||||
RuleFor(m => m.Scale).GreaterThanOrEqualTo(0.2f).WithMessage("Scale must be 0.2 or greater");
|
||||
|
||||
RuleFor(m => m.Rotation).GreaterThanOrEqualTo(0).WithMessage("Rotation must be 0 or greater");
|
||||
RuleFor(m => m.Rotation).LessThanOrEqualTo(359).WithMessage("Rotation must be 359 or less");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Dialogs.UpdateDialogView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<StackPanel Margin="16">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 0 0 20">
|
||||
Update available
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
TextWrapping="Wrap">
|
||||
A new Artemis update is available! 🥳<LineBreak />
|
||||
You are currently running build <Run Text="{Binding CurrentBuild, Mode=OneWay}" /> while the latest build is <Run Text="{Binding LatestBuild, Mode=OneWay}" />. <LineBreak /> <LineBreak />
|
||||
Updating Artemis will give you the latest bug(fixes), features and improvements.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" Margin="0 20">
|
||||
Changes
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Visibility="{Binding RetrievingChanges, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0 10">
|
||||
Retrieving changes...
|
||||
</TextBlock>
|
||||
<ProgressBar IsIndeterminate="True" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid Visibility="{Binding RetrievingChanges, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||
<ScrollViewer MaxHeight="400" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl ItemsSource="{Binding Changes}" Visibility="{Binding HasChanges, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Foreground="{DynamicResource MaterialDesignBodyLight}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
<TextBlock Visibility="{Binding HasChanges, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"
|
||||
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}">
|
||||
Couldn't retrieve changes, sorry :(
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 8 0 0">
|
||||
<Button Style="{StaticResource MaterialDesignFlatButton}"
|
||||
Focusable="False"
|
||||
IsCancel="True"
|
||||
Command="{s:Action Cancel}"
|
||||
Content="LATER" />
|
||||
<Button x:Name="ConfirmButton"
|
||||
Style="{StaticResource MaterialDesignFlatButton}"
|
||||
IsDefault="True"
|
||||
Focusable="True"
|
||||
Command="{s:Action Update}"
|
||||
Content="UPDATE" />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,128 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Services;
|
||||
using Artemis.UI.Services.Models.UpdateService;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Dialogs
|
||||
{
|
||||
public class UpdateDialogViewModel : DialogViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
|
||||
// Based on https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#skipping-ci-for-individual-commits
|
||||
private readonly string[] _excludedCommitMessages =
|
||||
{
|
||||
"[skip ci]",
|
||||
"[ci skip]",
|
||||
"skip-checks: true",
|
||||
"skip-checks:true",
|
||||
"[skip azurepipelines]",
|
||||
"[azurepipelines skip]",
|
||||
"[skip azpipelines]",
|
||||
"[azpipelines skip]",
|
||||
"[skip azp]",
|
||||
"[azp skip]",
|
||||
"***NO_CI***"
|
||||
};
|
||||
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IUpdateService _updateService;
|
||||
private bool _canUpdate = true;
|
||||
private bool _hasChanges;
|
||||
private bool _retrievingChanges;
|
||||
|
||||
public UpdateDialogViewModel(DevOpsBuild buildInfo, IUpdateService updateService, IDialogService dialogService, IMessageService messageService)
|
||||
{
|
||||
_updateService = updateService;
|
||||
_dialogService = dialogService;
|
||||
_messageService = messageService;
|
||||
|
||||
CurrentBuild = Constants.BuildInfo.BuildNumberDisplay;
|
||||
LatestBuild = buildInfo.BuildNumber;
|
||||
Changes = new BindableCollection<string>();
|
||||
|
||||
Task.Run(GetBuildChanges);
|
||||
}
|
||||
|
||||
public string CurrentBuild { get; }
|
||||
public string LatestBuild { get; }
|
||||
public BindableCollection<string> Changes { get; }
|
||||
|
||||
public bool RetrievingChanges
|
||||
{
|
||||
get => _retrievingChanges;
|
||||
set => SetAndNotify(ref _retrievingChanges, value);
|
||||
}
|
||||
|
||||
public bool HasChanges
|
||||
{
|
||||
get => _hasChanges;
|
||||
set => SetAndNotify(ref _hasChanges, value);
|
||||
}
|
||||
|
||||
public bool CanUpdate
|
||||
{
|
||||
get => _canUpdate;
|
||||
set => SetAndNotify(ref _canUpdate, value);
|
||||
}
|
||||
|
||||
public async Task Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
CanUpdate = false;
|
||||
await _updateService.ApplyUpdate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("An exception occurred while applying the update", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CanUpdate = true;
|
||||
}
|
||||
|
||||
Session.Close(true);
|
||||
}
|
||||
|
||||
private async Task GetBuildChanges()
|
||||
{
|
||||
try
|
||||
{
|
||||
RetrievingChanges = true;
|
||||
Task<DevOpsBuild> currentTask = _updateService.GetBuildInfo(1, CurrentBuild);
|
||||
Task<DevOpsBuild> latestTask = _updateService.GetBuildInfo(1, LatestBuild);
|
||||
|
||||
DevOpsBuild current = await currentTask;
|
||||
DevOpsBuild latest = await latestTask;
|
||||
|
||||
if (current != null && latest != null)
|
||||
{
|
||||
GitHubDifference difference = await _updateService.GetBuildDifferences(current, latest);
|
||||
|
||||
// Only take commits with one parents (no merges)
|
||||
Changes.Clear();
|
||||
Changes.AddRange(difference.Commits.Where(c => c.Parents.Count == 1)
|
||||
.SelectMany(c => c.Commit.Message.Split("\n"))
|
||||
.Select(m => m.Trim())
|
||||
.Where(m => !string.IsNullOrWhiteSpace(m) && !_excludedCommitMessages.Contains(m))
|
||||
.OrderBy(m => m)
|
||||
);
|
||||
HasChanges = Changes.Any();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_messageService.ShowMessage($"Failed to retrieve build changes - {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
RetrievingChanges = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.SettingsTabsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:SettingsTabsViewModel}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.TextBlock.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<TabControl Style="{StaticResource MaterialDesignAppBarTabControl}"
|
||||
ItemsSource="{Binding Items}"
|
||||
SelectedItem="{Binding ActiveItem}"
|
||||
DisplayMemberPath="DisplayName">
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}"
|
||||
VerticalContentAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
IsTabStop="False"
|
||||
TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
|
||||
<!-- Bug: materialDesign:RippleAssist.RippleOnTop doesn't look as nice but otherwise it doesn't work at all, not sure why -->
|
||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right"
|
||||
ToolTip="Open debugger"
|
||||
Command="{s:Action ShowDebugger}"
|
||||
materialDesign:RippleAssist.RippleOnTop="True">
|
||||
<materialDesign:PackIcon Kind="Matrix" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -1,37 +0,0 @@
|
||||
using Artemis.UI.Screens.Settings.Tabs.About;
|
||||
using Artemis.UI.Screens.Settings.Tabs.Devices;
|
||||
using Artemis.UI.Screens.Settings.Tabs.General;
|
||||
using Artemis.UI.Screens.Settings.Tabs.Plugins;
|
||||
using Artemis.UI.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class SettingsTabsViewModel : Conductor<Screen>.Collection.OneActive
|
||||
{
|
||||
private readonly IDebugService _debugService;
|
||||
|
||||
public SettingsTabsViewModel(
|
||||
GeneralSettingsTabViewModel generalSettingsTabViewModel,
|
||||
PluginSettingsTabViewModel pluginSettingsTabViewModel,
|
||||
DeviceSettingsTabViewModel deviceSettingsTabViewModel,
|
||||
AboutTabViewModel aboutTabViewModel,
|
||||
IDebugService debugService)
|
||||
{
|
||||
_debugService = debugService;
|
||||
DisplayName = "Settings";
|
||||
|
||||
Items.Add(generalSettingsTabViewModel);
|
||||
Items.Add(pluginSettingsTabViewModel);
|
||||
Items.Add(deviceSettingsTabViewModel);
|
||||
Items.Add(aboutTabViewModel);
|
||||
|
||||
ActiveItem = generalSettingsTabViewModel;
|
||||
}
|
||||
|
||||
public void ShowDebugger()
|
||||
{
|
||||
_debugService.ShowDebugger();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Settings.SettingsView">
|
||||
<Border Classes="router-container">
|
||||
<TabControl Margin="12" Items="{Binding SettingTabs}">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DisplayName}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ItemTemplate>
|
||||
</TabControl>
|
||||
</Border>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class SettingsView : ReactiveUserControl<SettingsViewModel>
|
||||
{
|
||||
public SettingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.SettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance settings:SettingsViewModel}"
|
||||
d:DesignHeight="600" d:DesignWidth="600">
|
||||
<ContentControl s:View.Model="{Binding ActiveItem}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
</UserControl>
|
||||
@@ -1,17 +1,26 @@
|
||||
using Stylet;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.UI.Shared;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class SettingsViewModel : MainScreenViewModel
|
||||
{
|
||||
public SettingsViewModel(SettingsTabsViewModel settingsTabsViewModel)
|
||||
public SettingsViewModel(IScreen hostScreen,
|
||||
GeneralTabViewModel generalTabViewModel,
|
||||
PluginsTabViewModel pluginsTabViewModel,
|
||||
DevicesTabViewModel devicesTabViewModel,
|
||||
AboutTabViewModel aboutTabViewModel) : base(hostScreen, "settings")
|
||||
{
|
||||
DisplayName = "Settings";
|
||||
|
||||
settingsTabsViewModel.ConductWith(this);
|
||||
ActiveItem = settingsTabsViewModel;
|
||||
SettingTabs = new ObservableCollection<ActivatableViewModelBase>
|
||||
{
|
||||
generalTabViewModel,
|
||||
pluginsTabViewModel,
|
||||
devicesTabViewModel,
|
||||
aboutTabViewModel
|
||||
};
|
||||
}
|
||||
|
||||
public SettingsTabsViewModel ActiveItem { get; }
|
||||
public ObservableCollection<ActivatableViewModelBase> SettingTabs { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,436 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.About.AboutTabView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.About"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:AboutTabViewModel}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Margin="15" MaxWidth="800">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" Source="{svgc:SvgImage Source=/Resources/Images/Logo/bow.svg}" Width="60" Height="80" Margin="0 0 20 0" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Style="{StaticResource MaterialDesignHeadline2TextBlock}">
|
||||
Artemis 2
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="View website"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://artemis-rgb.com">
|
||||
<materialDesign:PackIcon Kind="Web" />
|
||||
</Button>
|
||||
<Button
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="View GitHub repository"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://github.com/Artemis-RGB/Artemis">
|
||||
<materialDesign:PackIcon Kind="Github" />
|
||||
</Button>
|
||||
<Button
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="View Wiki"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://wiki.artemis-rgb.com"
|
||||
Margin="0 0 -10 0">
|
||||
<materialDesign:PackIcon Kind="BookOpenOutline" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Style="{StaticResource MaterialDesignSubtitle1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
Text="{Binding Version}" />
|
||||
<TextBlock Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}" RequestNavigate="{s:Action OpenHyperlink}" NavigateUri="https://github.com/Artemis-RGB/Artemis/blob/master/LICENSE">
|
||||
PolyForm Noncommercial License 1.0.0
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<materialDesign:Card Margin="0 25 0 0">
|
||||
<StackPanel Margin="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
RenderOptions.BitmapScalingMode="HighQuality">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/8858506" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Padding="0">
|
||||
Robert Beekman
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
Padding="0">
|
||||
Project owner, main contributor
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="View GitHub profile"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://github.com/RobertWasTaken/">
|
||||
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
RenderOptions.BitmapScalingMode="HighQuality">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/1094841" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Padding="0">
|
||||
Darth Affe
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
Padding="0">
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/DarthAffe/RGB.NET">
|
||||
RGB.NET
|
||||
</Hyperlink>
|
||||
developer, main contributor
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="View GitHub profile"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://github.com/DarthAffe/">
|
||||
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
RenderOptions.BitmapScalingMode="HighQuality">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush ImageSource="https://avatars.githubusercontent.com/u/29486064" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Padding="0">
|
||||
Diogo 'DrMeteor' Trindade
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
Padding="0">
|
||||
Main contributor
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="View GitHub profile"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://github.com/diogotr7/">
|
||||
<materialDesign:PackIcon Kind="Github" Width="20" Height="20" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
RenderOptions.BitmapScalingMode="HighQuality">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush ImageSource="https://i.imgur.com/8mPWY1j.png" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Padding="0">
|
||||
Kai Werling
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
Padding="0">
|
||||
Graphics design
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-10">
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="View website"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://kwer.online/">
|
||||
<materialDesign:PackIcon Kind="Web" Width="20" Height="20" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="AccountHeart" Width="30" Height="30" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="5 3 0 0">
|
||||
Special thanks
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" LineHeight="25" Margin="10 0 0 0">
|
||||
- The various people creating PRs to Artemis.Plugins and the main repository<LineBreak />
|
||||
- All the people on Discord providing feedback and testing
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0 20 0 0">
|
||||
<materialDesign:PackIcon Kind="LibraryShelves" Width="30" Height="30" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="5 3 0 0">
|
||||
External libraries
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" LineHeight="25" Margin="10 0 0 0">
|
||||
- EmbedIO
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://unosquare.github.io/embedio/">
|
||||
https://unosquare.github.io/embedio/
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- FluentValidation
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://fluentvalidation.net/">
|
||||
https://fluentvalidation.net/
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Furl.Http
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://flurl.dev//">
|
||||
https://flurl.dev/
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- gong-wpf-dragdrop
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/punker76/gong-wpf-dragdrop">
|
||||
https://github.com/punker76/gong-wpf-dragdrop
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Hardcodet.NotifyIcon.Wpf.NetCore
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/HavenDV/H.NotifyIcon.WPF">
|
||||
https://github.com/HavenDV/H.NotifyIcon.WPF
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Humanizer
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/Humanizr/Humanizer">
|
||||
https://github.com/Humanizr/Humanizer
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- LiteDB
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://www.litedb.org/">
|
||||
https://www.litedb.org/
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- MaterialDesignThemes
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit">
|
||||
https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- MaterialDesignExtensions
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://spiegelp.github.io/MaterialDesignExtensions/">
|
||||
https://spiegelp.github.io/MaterialDesignExtensions/
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- McMaster.NETCore.Plugins
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/natemcmaster/DotNetCorePlugins">
|
||||
https://github.com/natemcmaster/DotNetCorePlugins
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Newtonsoft.Json
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://www.newtonsoft.com/json">
|
||||
https://www.newtonsoft.com/json
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Ninject
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="http://www.ninject.org/">
|
||||
http://www.ninject.org/
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- NoStringEvaluating
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/KovtunV/NoStringEvaluating">
|
||||
https://github.com/KovtunV/NoStringEvaluating
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Ookii.Dialogs.Wpf
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/ookii-dialogs/ookii-dialogs-wpf">
|
||||
https://github.com/ookii-dialogs/ookii-dialogs-wpf
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- RawInput.Sharp
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/mfakane/rawinput-sharp">
|
||||
https://github.com/mfakane/rawinput-sharp
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- RGB.NET
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/DarthAffe/RGB.NET">
|
||||
https://github.com/DarthAffe/RGB.NET
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Serilog
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://serilog.net/">
|
||||
https://serilog.net/
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- SkiaSharp
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/mono/SkiaSharp/">
|
||||
https://github.com/mono/SkiaSharp
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Stylet
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://github.com/canton7/Stylet">
|
||||
https://github.com/canton7/Stylet
|
||||
</Hyperlink>
|
||||
<LineBreak />
|
||||
- Unclassified.NetRevisionTask
|
||||
<Hyperlink Style="{StaticResource ArtemisHyperlink}"
|
||||
RequestNavigate="{s:Action OpenHyperlink}"
|
||||
NavigateUri="https://unclassified.software/en/apps/netrevisiontask">
|
||||
https://unclassified.software/en/apps/netrevisiontask
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -1,46 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Windows.Navigation;
|
||||
using Artemis.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.About
|
||||
{
|
||||
public class AboutTabViewModel : Screen
|
||||
{
|
||||
private string _version;
|
||||
|
||||
public AboutTabViewModel()
|
||||
{
|
||||
DisplayName = "ABOUT";
|
||||
}
|
||||
|
||||
public string Version
|
||||
{
|
||||
get => _version;
|
||||
set => SetAndNotify(ref _version, value);
|
||||
}
|
||||
|
||||
public void OpenHyperlink(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Core.Utilities.OpenUrl(e.Uri.AbsoluteUri);
|
||||
}
|
||||
|
||||
public void OpenUrl(string url)
|
||||
{
|
||||
Core.Utilities.OpenUrl(url);
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnActivate()
|
||||
{
|
||||
AssemblyInformationalVersionAttribute versionAttribute = typeof(RootViewModel).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
Version = $"Version {versionAttribute?.InformationalVersion} build {Constants.BuildInfo.BuildNumberDisplay}";
|
||||
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
<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:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="1400"
|
||||
x:Class="Artemis.UI.Screens.Settings.AboutTabView">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Margin="15" MaxWidth="800">
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="Auto,*,Auto">
|
||||
<Image Grid.Column="0" Grid.RowSpan="2" Width="140" Height="140" VerticalAlignment="Center">
|
||||
<Image.Source>
|
||||
<svg:SvgImage Source="/Assets/Images/Logo/bow.svg" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" FontSize="36" VerticalAlignment="Bottom">
|
||||
Artemis 2
|
||||
</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">
|
||||
<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">
|
||||
<avalonia:MaterialIcon Kind="BookOpenOutline" />
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Top"
|
||||
Foreground="{DynamicResource MaterialDesignBodyLight}"
|
||||
Text="{Binding Version}" />
|
||||
|
||||
<controls:HyperlinkButton Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Top"
|
||||
NavigateUri="https://github.com/Artemis-RGB/Artemis/blob/master/LICENSE">
|
||||
PolyForm Noncommercial License 1.0.0
|
||||
</controls:HyperlinkButton>
|
||||
</Grid>
|
||||
|
||||
<Border Classes="card" Margin="0 20 0 10">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*,*" ColumnDefinitions="Auto,*">
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
IsVisible="{Binding RobertProfileImage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush Source="{Binding RobertProfileImage}" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Padding="0">
|
||||
Robert Beekman
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" Grid.Column="1" Grid.Row="1" Padding="0">
|
||||
Project owner, main contributor
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-6">
|
||||
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub profile" NavigateUri="https://github.com/RobertBeekman/">
|
||||
<avalonia:MaterialIcon Kind="Github" Width="20" Height="20" />
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
IsVisible="{Binding DarthAffeProfileImage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush Source="{Binding DarthAffeProfileImage}" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Padding="0">
|
||||
Darth Affe
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" Grid.Column="1" Grid.Row="1" Padding="0">
|
||||
RGB.NET, main contributor
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-6">
|
||||
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub profile" NavigateUri="https://github.com/DarthAffe/">
|
||||
<avalonia:MaterialIcon Kind="Github" Width="20" Height="20" />
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*,*" ColumnDefinitions="Auto,*">
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
IsVisible="{Binding DrMeteorProfileImage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush Source="{Binding DrMeteorProfileImage}" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Padding="0">
|
||||
Diogo 'DrMeteor' Trindade
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" Grid.Column="1" Grid.Row="1" Padding="0">
|
||||
Main contributor
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-6">
|
||||
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View GitHub profile" NavigateUri="https://github.com/diogotr7/">
|
||||
<avalonia:MaterialIcon Kind="Github" Width="20" Height="20" />
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*,*" ColumnDefinitions="Auto,*">
|
||||
<Ellipse Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="3"
|
||||
VerticalAlignment="Top"
|
||||
Height="75"
|
||||
Width="75"
|
||||
Margin="0 0 15 0"
|
||||
IsVisible="{Binding KaiProfileImage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush Source="{Binding KaiProfileImage}" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Padding="0">
|
||||
Kai Werling
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" Grid.Column="1" Grid.Row="1" Padding="0">
|
||||
Graphics design
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" Margin="-6">
|
||||
<controls:HyperlinkButton Classes="icon-button" ToolTip.Tip="View website" NavigateUri="https://kwer.online/">
|
||||
<avalonia:MaterialIcon Kind="Web" Width="20" Height="20" />
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="card" Margin="0 10">
|
||||
<StackPanel>
|
||||
<StackPanel Margin="0 0 0 12" Orientation="Horizontal">
|
||||
<avalonia:MaterialIcon Kind="UserHeart" Height="35" Width="35" Margin="0 0 10 0" />
|
||||
<TextBlock Classes="h4">
|
||||
Special Thanks
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock>
|
||||
- The various people creating PRs to Artemis.Plugins and the main repository
|
||||
</TextBlock>
|
||||
<TextBlock>
|
||||
- All the people on Discord providing feedback and testing
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
</Border>
|
||||
|
||||
<Border Classes="card" Margin="0 10">
|
||||
<StackPanel>
|
||||
<StackPanel Margin="0 0 0 12" Orientation="Horizontal">
|
||||
<avalonia:MaterialIcon Kind="Dependency" Height="35" Width="35" Margin="0 0 10 0" />
|
||||
<TextBlock Classes="h4">
|
||||
External Libraries
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<Grid.Styles>
|
||||
<Style Selector="TextBlock.library-name">
|
||||
<Setter Property="Margin" Value="0 7 15 6"/>
|
||||
<Setter Property="FontWeight" Value="600"/>
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="0">
|
||||
<TextBlock Classes="library-name">Avalonia</TextBlock>
|
||||
<TextBlock Classes="library-name">FluentAvalonia</TextBlock>
|
||||
<TextBlock Classes="library-name">EmbedIO</TextBlock>
|
||||
<TextBlock Classes="library-name">Furl.Http</TextBlock>
|
||||
<TextBlock Classes="library-name">Humanizer</TextBlock>
|
||||
<TextBlock Classes="library-name">LiteDB</TextBlock>
|
||||
<TextBlock Classes="library-name">McMaster.NETCore.Plugins</TextBlock>
|
||||
<TextBlock Classes="library-name">Newtonsoft.Json</TextBlock>
|
||||
<TextBlock Classes="library-name">Ninject</TextBlock>
|
||||
<TextBlock Classes="library-name">RGB.NET</TextBlock>
|
||||
<TextBlock Classes="library-name">Serilog</TextBlock>
|
||||
<TextBlock Classes="library-name">SkiaSharp</TextBlock>
|
||||
<TextBlock Classes="library-name">Unclassified.NetRevisionTask</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<controls:HyperlinkButton NavigateUri="https://avaloniaui.net/">
|
||||
https://avaloniaui.net/
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://github.com/amwx/FluentAvalonia">
|
||||
https://github.com/amwx/FluentAvalonia
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://unosquare.github.io/embedio/">
|
||||
https://unosquare.github.io/embedio/
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://flurl.dev/">
|
||||
https://flurl.dev/
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://github.com/Humanizr/Humanizer">
|
||||
https://github.com/Humanizr/Humanizer
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://www.litedb.org/">
|
||||
https://www.litedb.org/
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://github.com/natemcmaster/DotNetCorePlugins">
|
||||
https://github.com/natemcmaster/DotNetCorePlugins
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://www.newtonsoft.com/json">
|
||||
https://www.newtonsoft.com/json
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="http://www.ninject.org/">
|
||||
http://www.ninject.org/
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://github.com/DarthAffe/RGB.NET">
|
||||
https://github.com/DarthAffe/RGB.NET
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://serilog.net/">
|
||||
https://serilog.net/
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://github.com/mono/SkiaSharp">
|
||||
https://github.com/mono/SkiaSharp
|
||||
</controls:HyperlinkButton>
|
||||
<controls:HyperlinkButton NavigateUri="https://unclassified.software/en/apps/netrevisiontask">
|
||||
https://unclassified.software/en/apps/netrevisiontask
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public partial class AboutTabView : ReactiveUserControl<AboutTabViewModel>
|
||||
{
|
||||
public AboutTabView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Flurl.Http;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class AboutTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private Bitmap? _darthAffeProfileImage;
|
||||
private Bitmap? _drMeteorProfileImage;
|
||||
private Bitmap? _kaiProfileImage;
|
||||
private Bitmap? _robertProfileImage;
|
||||
private string? _version;
|
||||
|
||||
public AboutTabViewModel()
|
||||
{
|
||||
DisplayName = "About";
|
||||
this.WhenActivated((CompositeDisposable _) => Task.Run(Activate));
|
||||
}
|
||||
|
||||
public string? Version
|
||||
{
|
||||
get => _version;
|
||||
set => RaiseAndSetIfChanged(ref _version, value);
|
||||
}
|
||||
|
||||
public Bitmap? RobertProfileImage
|
||||
{
|
||||
get => _robertProfileImage;
|
||||
set => RaiseAndSetIfChanged(ref _robertProfileImage, value);
|
||||
}
|
||||
|
||||
public Bitmap? DarthAffeProfileImage
|
||||
{
|
||||
get => _darthAffeProfileImage;
|
||||
set => RaiseAndSetIfChanged(ref _darthAffeProfileImage, value);
|
||||
}
|
||||
|
||||
public Bitmap? DrMeteorProfileImage
|
||||
{
|
||||
get => _drMeteorProfileImage;
|
||||
set => RaiseAndSetIfChanged(ref _drMeteorProfileImage, value);
|
||||
}
|
||||
|
||||
public Bitmap? KaiProfileImage
|
||||
{
|
||||
get => _kaiProfileImage;
|
||||
set => RaiseAndSetIfChanged(ref _kaiProfileImage, value);
|
||||
}
|
||||
|
||||
private async Task Activate()
|
||||
{
|
||||
AssemblyInformationalVersionAttribute? versionAttribute = typeof(AboutTabViewModel).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
Version = $"Version {versionAttribute?.InformationalVersion} build {Constants.BuildInfo.BuildNumberDisplay}";
|
||||
|
||||
try
|
||||
{
|
||||
RobertProfileImage = new Bitmap(await "https://avatars.githubusercontent.com/u/8858506".GetStreamAsync());
|
||||
RobertProfileImage = new Bitmap(await "https://avatars.githubusercontent.com/u/8858506".GetStreamAsync());
|
||||
DarthAffeProfileImage = new Bitmap(await "https://avatars.githubusercontent.com/u/1094841".GetStreamAsync());
|
||||
DrMeteorProfileImage = new Bitmap(await "https://avatars.githubusercontent.com/u/29486064".GetStreamAsync());
|
||||
KaiProfileImage = new Bitmap(await "https://i.imgur.com/8mPWY1j.png".GetStreamAsync());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored, unluckyyyy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Devices.DeviceSettingsTabView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Devices"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:DeviceSettingsTabViewModel}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="0 12 0 0">
|
||||
<DockPanel Margin="15" MaxWidth="1230" HorizontalAlignment="Center">
|
||||
<StackPanel DockPanel.Dock="Top">
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}">Device management</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Below you view and manage the devices that were detected by Artemis. <LineBreak/>
|
||||
Disabling a device will cause it to stop updating. Some SDKs will even go back to using manufacturer lighting (Artemis restart may be required).
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Items}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Top" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</DockPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -1,88 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Devices
|
||||
{
|
||||
public class DeviceSettingsTabViewModel : Conductor<DeviceSettingsViewModel>.Collection.AllActive
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||
private bool _confirmedDisable;
|
||||
|
||||
public DeviceSettingsTabViewModel(IRgbService rgbService, IDialogService dialogService, ISettingsVmFactory settingsVmFactory)
|
||||
{
|
||||
DisplayName = "DEVICES";
|
||||
|
||||
_rgbService = rgbService;
|
||||
_dialogService = dialogService;
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
}
|
||||
|
||||
public async Task<bool> ShowDeviceDisableDialog()
|
||||
{
|
||||
if (_confirmedDisable)
|
||||
return true;
|
||||
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog(
|
||||
"Disabling device",
|
||||
"Disabling a device will cause it to stop updating. " +
|
||||
"\r\nSome SDKs will even go back to using manufacturer lighting (Artemis restart may be required)."
|
||||
);
|
||||
if (confirmed)
|
||||
_confirmedDisable = true;
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
private void RgbServiceOnDeviceRemoved(object sender, DeviceEventArgs e)
|
||||
{
|
||||
DeviceSettingsViewModel viewModel = Items.FirstOrDefault(i => i.Device == e.Device);
|
||||
if (viewModel != null)
|
||||
Items.Remove(viewModel);
|
||||
}
|
||||
|
||||
private void RgbServiceOnDeviceAdded(object sender, DeviceEventArgs e)
|
||||
{
|
||||
Items.Add(_settingsVmFactory.CreateDeviceSettingsViewModel(e.Device));
|
||||
}
|
||||
|
||||
#region Overrides of AllActive
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnActivate()
|
||||
{
|
||||
_rgbService.DeviceAdded += RgbServiceOnDeviceAdded;
|
||||
_rgbService.DeviceRemoved += RgbServiceOnDeviceRemoved;
|
||||
// Take it off the UI thread to avoid freezing on tab change
|
||||
Task.Run(async () =>
|
||||
{
|
||||
if (Items.Any())
|
||||
Items.Clear();
|
||||
|
||||
await Task.Delay(200);
|
||||
|
||||
List<DeviceSettingsViewModel> instances = _rgbService.Devices.Select(d => _settingsVmFactory.CreateDeviceSettingsViewModel(d)).ToList();
|
||||
foreach (DeviceSettingsViewModel deviceSettingsViewModel in instances)
|
||||
Items.Add(deviceSettingsViewModel);
|
||||
});
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnClose()
|
||||
{
|
||||
_rgbService.DeviceAdded -= RgbServiceOnDeviceAdded;
|
||||
_rgbService.DeviceRemoved -= RgbServiceOnDeviceRemoved;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Devices.DeviceSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:devices="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Devices"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
d:DataContext="{d:DesignInstance devices:DeviceSettingsViewModel}"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Button.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.TextBlock.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<materialDesign:Card Width="200">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="140" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Rectangle Grid.Row="0" Fill="{DynamicResource MaterialDesignPaper}" />
|
||||
<shared:DeviceVisualizer VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="10"
|
||||
Device="{Binding Device}"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Grid.Row="0" />
|
||||
<Button Grid.Row="0"
|
||||
Style="{StaticResource MaterialDesignFloatingActionMiniButton}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="0 0 16 -20"
|
||||
ToolTip="Identify"
|
||||
Command="{s:Action IdentifyDevice}">
|
||||
<materialDesign:PackIcon Kind="AlarmLight" />
|
||||
</Button>
|
||||
<StackPanel Grid.Row="1" Margin="8 24 8 0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Text="{Binding Device.RgbDevice.DeviceInfo.Model}" />
|
||||
<TextBlock TextWrapping="Wrap" Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
|
||||
<Run Text="{Binding Device.RgbDevice.DeviceInfo.Manufacturer, Mode=OneWay}" />
|
||||
-
|
||||
<Run Text="{Binding Device.DeviceType, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel HorizontalAlignment="Right" Grid.Row="2" Orientation="Horizontal" Margin="8">
|
||||
<CheckBox Style="{StaticResource MaterialDesignAccentCheckBox}" IsChecked="{Binding IsDeviceEnabled}">
|
||||
Device enabled
|
||||
</CheckBox>
|
||||
<materialDesign:PopupBox Style="{StaticResource MaterialDesignToolPopupBox}" Padding="2 0 2 0" Foreground="{StaticResource MaterialDesignBody}">
|
||||
<StackPanel>
|
||||
<Button Command="{s:Action OpenPluginDirectory}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="FolderOpen" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Open plugin directory</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Command="{s:Action DetectInput}" ToolTip="Teach Artemis which keypresses and/or button presses belong to this device">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="GestureDoubleTap" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Identify input</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action ViewProperties}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Gear" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Device properties</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
</UserControl>
|
||||
@@ -1,107 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.SurfaceEditor.Dialogs;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Humanizer;
|
||||
using RGB.NET.Core;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Devices
|
||||
{
|
||||
public class DeviceSettingsViewModel : Screen
|
||||
{
|
||||
private readonly IDeviceDebugVmFactory _deviceDebugVmFactory;
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowManager _windowManager;
|
||||
|
||||
public DeviceSettingsViewModel(ArtemisDevice device,
|
||||
IDeviceService deviceService,
|
||||
IDialogService dialogService,
|
||||
IWindowManager windowManager,
|
||||
IDeviceDebugVmFactory deviceDebugVmFactory,
|
||||
IRgbService rgbService)
|
||||
{
|
||||
_deviceService = deviceService;
|
||||
_dialogService = dialogService;
|
||||
_windowManager = windowManager;
|
||||
_deviceDebugVmFactory = deviceDebugVmFactory;
|
||||
_rgbService = rgbService;
|
||||
Device = device;
|
||||
|
||||
Type = Device.DeviceType.ToString().Humanize();
|
||||
Name = Device.RgbDevice.DeviceInfo.Model;
|
||||
Manufacturer = Device.RgbDevice.DeviceInfo.Manufacturer;
|
||||
}
|
||||
|
||||
public ArtemisDevice Device { get; }
|
||||
|
||||
public string Type { get; }
|
||||
public string Name { get; }
|
||||
public string Manufacturer { get; }
|
||||
|
||||
public bool CanDetectInput => Device.DeviceType == RGBDeviceType.Keyboard ||
|
||||
Device.DeviceType == RGBDeviceType.Mouse;
|
||||
|
||||
public bool IsDeviceEnabled
|
||||
{
|
||||
get => Device.IsEnabled;
|
||||
set { Task.Run(() => UpdateIsDeviceEnabled(value)); }
|
||||
}
|
||||
|
||||
public void IdentifyDevice()
|
||||
{
|
||||
_deviceService.IdentifyDevice(Device);
|
||||
}
|
||||
|
||||
public void OpenPluginDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Device.DeviceProvider.Plugin.Directory.FullName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DetectInput()
|
||||
{
|
||||
object madeChanges = await _dialogService.ShowDialog<SurfaceDeviceDetectInputViewModel>(
|
||||
new Dictionary<string, object> {{"device", Device}}
|
||||
);
|
||||
|
||||
if ((bool) madeChanges)
|
||||
_rgbService.SaveDevice(Device);
|
||||
}
|
||||
|
||||
public void ViewProperties()
|
||||
{
|
||||
_windowManager.ShowWindow(_deviceDebugVmFactory.DeviceDialogViewModel(Device));
|
||||
}
|
||||
private async Task UpdateIsDeviceEnabled(bool value)
|
||||
{
|
||||
if (!value)
|
||||
value = !await ((DeviceSettingsTabViewModel) Parent).ShowDeviceDisableDialog();
|
||||
|
||||
if (value)
|
||||
_rgbService.EnableDevice(Device);
|
||||
else
|
||||
_rgbService.DisableDevice(Device);
|
||||
NotifyOfPropertyChange(nameof(IsDeviceEnabled));
|
||||
SaveDevice();
|
||||
}
|
||||
|
||||
private void SaveDevice()
|
||||
{
|
||||
_rgbService.SaveDevice(Device);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Settings.DevicesTabView">
|
||||
<Grid MaxWidth="1050">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="h4">Device management</TextBlock>
|
||||
<TextBlock>
|
||||
Below you view and manage the devices that were detected by Artemis.
|
||||
</TextBlock>
|
||||
<TextBlock>
|
||||
Disabling a device will cause it to stop updating. Some SDKs will even go back to using manufacturer lighting (Artemis restart may be required).
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl Items="{Binding Devices}" Margin="-5 0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class DevicesTabView : ReactiveUserControl<DevicesTabViewModel>
|
||||
{
|
||||
public DevicesTabView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.Device;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class DevicesTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IDeviceVmFactory _deviceVmFactory;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IWindowService _windowService;
|
||||
private bool _confirmedDisable;
|
||||
|
||||
public DevicesTabViewModel(IRgbService rgbService, IWindowService windowService, IDeviceVmFactory deviceVmFactory)
|
||||
{
|
||||
DisplayName = "Devices";
|
||||
|
||||
_rgbService = rgbService;
|
||||
_windowService = windowService;
|
||||
_deviceVmFactory = deviceVmFactory;
|
||||
|
||||
Devices = new ObservableCollection<DeviceSettingsViewModel>();
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
GetDevices();
|
||||
|
||||
Observable.FromEventPattern<DeviceEventArgs>(x => rgbService.DeviceAdded += x, x => rgbService.DeviceAdded -= x)
|
||||
.Subscribe(d => AddDevice(d.EventArgs.Device))
|
||||
.DisposeWith(disposables);
|
||||
Observable.FromEventPattern<DeviceEventArgs>(x => rgbService.DeviceRemoved += x, x => rgbService.DeviceRemoved -= x)
|
||||
.Subscribe(d => RemoveDevice(d.EventArgs.Device))
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
private void GetDevices()
|
||||
{
|
||||
Devices.Clear();
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Devices.AddRange(_rgbService.Devices.Select(d => _deviceVmFactory.DeviceSettingsViewModel(d, this)));
|
||||
}, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
public ObservableCollection<DeviceSettingsViewModel> Devices { get; }
|
||||
|
||||
public async Task<bool> ShowDeviceDisableDialog()
|
||||
{
|
||||
if (_confirmedDisable)
|
||||
return true;
|
||||
|
||||
bool confirmed = await _windowService.ShowConfirmContentDialog(
|
||||
"Disabling device",
|
||||
"Disabling a device will cause it to stop updating. " +
|
||||
"\r\nSome SDKs will even go back to using manufacturer lighting (Artemis restart may be required)."
|
||||
);
|
||||
if (confirmed)
|
||||
_confirmedDisable = true;
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
private void AddDevice(ArtemisDevice device)
|
||||
{
|
||||
// If the device was only enabled, don't add it
|
||||
if (Devices.Any(d => d.Device == device))
|
||||
return;
|
||||
|
||||
Devices.Add(_deviceVmFactory.DeviceSettingsViewModel(device, this));
|
||||
}
|
||||
|
||||
private void RemoveDevice(ArtemisDevice device)
|
||||
{
|
||||
// If the device was only disabled don't remove it
|
||||
if (_rgbService.Devices.Contains(device))
|
||||
return;
|
||||
|
||||
DeviceSettingsViewModel? viewModel = Devices.FirstOrDefault(i => i.Device == device);
|
||||
if (viewModel != null)
|
||||
Devices.Remove(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,497 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.General"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:dataTemplateSelectors="clr-namespace:Artemis.UI.DataTemplateSelectors"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime"
|
||||
xmlns:Converters="clr-namespace:Artemis.UI.Converters" x:Class="Artemis.UI.Screens.Settings.Tabs.General.GeneralSettingsTabView"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type local:GeneralSettingsTabViewModel}}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/LayerBrushDescriptors.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
||||
<Converters:NormalizedPercentageConverter x:Key="NormalizedPercentageConverter" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Margin="15" MaxWidth="800">
|
||||
<!-- General settings -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">
|
||||
<Run Text="General" />
|
||||
</TextBlock>
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel Margin="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Start up with Windows</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding UIAutoRun.Value}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Start up with Windows minimized</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}"
|
||||
IsChecked="{Binding UIShowOnStartup.Value, Converter={StaticResource InverseBooleanConverter}}"
|
||||
IsEnabled="{Binding UIAutoRun.Value}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Startup delay</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
<Run Text="Set the amount of seconds to wait before running Artemis with Windows." /><LineBreak />
|
||||
<Run Text="If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value." />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<TextBox Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Text="{Binding UIAutoRunDelay.Value}"
|
||||
IsEnabled="{Binding UIAutoRun.Value}"
|
||||
Width="100"
|
||||
materialDesign:TextFieldAssist.SuffixText="sec"
|
||||
materialDesign:HintAssist.IsFloating="false" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Color scheme</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
Pick between a light and dark color scheme, the automatic option copies your Windows settings.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
|
||||
Width="100"
|
||||
SelectedValue="{Binding UIColorScheme.Value}"
|
||||
ItemsSource="{Binding ColorSchemes}"
|
||||
SelectedValuePath="Value"
|
||||
DisplayMemberPath="Description"
|
||||
materialDesign:HintAssist.IsFloating="false" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Log level
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
Sets the logging level, a higher logging level will result in more log files.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
|
||||
Width="100"
|
||||
SelectedValue="{Binding CoreLoggingLevel.Value}"
|
||||
ItemsSource="{Binding LogLevels}"
|
||||
SelectedValuePath="Value"
|
||||
DisplayMemberPath="Description"
|
||||
materialDesign:HintAssist.IsFloating="false" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Logs
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
|
||||
Opens the directory where logs are stored.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowLogsFolder}" Width="150" Content="SHOW LOGS" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
|
||||
<!-- Web server settings -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">
|
||||
Web server
|
||||
</TextBlock>
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel Margin="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Web server port
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
<Run Text="Artemis runs a local web server that can be used to externally interact with the application." /><LineBreak />
|
||||
<Run Text="This web server can only be accessed by applications running on your own computer, e.g. supported games." />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<TextBox Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Text="{Binding WebServerPort.Value}"
|
||||
Width="100"
|
||||
materialDesign:HintAssist.IsFloating="false" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
|
||||
<!-- Update settings -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">
|
||||
<Run Text="Updating" />
|
||||
</TextBlock>
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel Margin="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Check for updates
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
If enabled, we'll check for updates on startup and periodically while running.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding UICheckForUpdates.Value}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Update
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
|
||||
Use the button on the right to check for updates now.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action OfferUpdatesIfFound}" Width="150" Content="CHECK NOW" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
|
||||
<!-- Profile editor settings -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">
|
||||
<Run Text="Profile editor" />
|
||||
</TextBlock>
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel Margin="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Show condition data model values
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
While selecting a condition target, show the current values of the data model.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding ProfileEditorShowDataModelValues.Value}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Default brush
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
Sets the default brush that is applied to new layers
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
|
||||
Width="150"
|
||||
materialDesign:ComboBoxAssist.ClassicMode="True"
|
||||
materialDesign:ValidationAssist.UsePopup="True"
|
||||
materialDesign:HintAssist.IsFloating="false"
|
||||
HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding LayerBrushDescriptors}"
|
||||
SelectedValue="{Binding SelectedLayerBrushDescriptor}"
|
||||
ItemTemplateSelector="{dataTemplateSelectors:ComboBoxTemplateSelector DropdownItemsTemplate={StaticResource ExtendedLayerBrushDescriptorTemplate}, SelectedItemTemplate={StaticResource SimpleLayerBrushDescriptorTemplate}}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
|
||||
<!-- Rendering settings -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">
|
||||
Rendering
|
||||
</TextBlock>
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel Margin="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Preferred render method
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
Software-based rendering is done purely on the CPU while Vulkan uses GPU-acceleration.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
|
||||
Width="100"
|
||||
SelectedItem="{Binding CorePreferredGraphicsContext.Value}"
|
||||
materialDesign:HintAssist.IsFloating="false">
|
||||
<system:String>Software</system:String>
|
||||
<system:String>Vulkan</system:String>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Render scale
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
Sets the resolution Artemis renders at, higher scale means more CPU-usage, especially on large surfaces.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
|
||||
Width="100"
|
||||
SelectedItem="{Binding SelectedRenderScale}"
|
||||
ItemsSource="{Binding RenderScales}"
|
||||
DisplayMemberPath="Item1"
|
||||
materialDesign:HintAssist.IsFloating="false" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Target frame rate
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
Sets the FPS Artemis tries to render at, higher FPS means more CPU-usage but smoother animations. <LineBreak/>
|
||||
The options past 45 FPS are mostly useless unless you are using a custom device.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
|
||||
Width="100"
|
||||
SelectedItem="{Binding SelectedTargetFrameRate}"
|
||||
ItemsSource="{Binding TargetFrameRates}"
|
||||
DisplayMemberPath="Item1"
|
||||
materialDesign:HintAssist.IsFloating="false" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
|
||||
|
||||
<!-- Tools -->
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">
|
||||
Tools
|
||||
</TextBlock>
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel Margin="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
<Run Text="Setup wizard" />
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
|
||||
<Run Text="Opens the startup wizard usually shown when Artemis first starts." />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowSetupWizard}" Width="150" Content="SHOW WIZARD" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Debugger
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
|
||||
Use the debugger to see the raw image Artemis is rendering on the surface.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowDebugger}" Width="150" Content="SHOW DEBUGGER" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
|
||||
Application files
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
|
||||
Opens the directory where application files like plugins and settings are stored.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowDataFolder}" Width="150" Content="SHOW APP FILES" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -1,264 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.StartupWizard;
|
||||
using Artemis.UI.Services;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Utilities;
|
||||
using Ninject;
|
||||
using Serilog.Events;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.General
|
||||
{
|
||||
public class GeneralSettingsTabViewModel : Screen
|
||||
{
|
||||
private readonly IDebugService _debugService;
|
||||
private readonly PluginSetting<LayerBrushReference> _defaultLayerBrushDescriptor;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IRegistrationService _registrationService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IUpdateService _updateService;
|
||||
private readonly IWindowManager _windowManager;
|
||||
private bool _canOfferUpdatesIfFound = true;
|
||||
|
||||
public GeneralSettingsTabViewModel(
|
||||
IKernel kernel,
|
||||
IWindowManager windowManager,
|
||||
IDialogService dialogService,
|
||||
IDebugService debugService,
|
||||
ISettingsService settingsService,
|
||||
IUpdateService updateService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IRegistrationService registrationService,
|
||||
IMessageService messageService
|
||||
)
|
||||
{
|
||||
DisplayName = "GENERAL";
|
||||
|
||||
_kernel = kernel;
|
||||
_windowManager = windowManager;
|
||||
_dialogService = dialogService;
|
||||
_debugService = debugService;
|
||||
_settingsService = settingsService;
|
||||
_updateService = updateService;
|
||||
_registrationService = registrationService;
|
||||
_messageService = messageService;
|
||||
|
||||
RenderScales = new BindableCollection<Tuple<string, double>>
|
||||
{
|
||||
new("25%", 0.25),
|
||||
new("50%", 0.5),
|
||||
new("100%", 1)
|
||||
};
|
||||
TargetFrameRates = new BindableCollection<Tuple<string, int>>
|
||||
{
|
||||
new("10 FPS", 10),
|
||||
new("20 FPS", 20),
|
||||
new("30 FPS", 30),
|
||||
new("45 FPS", 45),
|
||||
new("60 FPS (lol)", 60),
|
||||
new("144 FPS (omegalol)", 144)
|
||||
};
|
||||
|
||||
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
|
||||
LayerBrushDescriptors = new BindableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
|
||||
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
||||
{
|
||||
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
|
||||
BrushType = "SolidBrush"
|
||||
});
|
||||
}
|
||||
|
||||
public BindableCollection<LayerBrushDescriptor> LayerBrushDescriptors { get; }
|
||||
|
||||
public LayerBrushDescriptor SelectedLayerBrushDescriptor
|
||||
{
|
||||
get => LayerBrushDescriptors.FirstOrDefault(d => d.MatchesLayerBrushReference(_defaultLayerBrushDescriptor.Value));
|
||||
set => _defaultLayerBrushDescriptor.Value = new LayerBrushReference(value);
|
||||
}
|
||||
|
||||
public BindableCollection<ValueDescription> LogLevels { get; }
|
||||
public BindableCollection<ValueDescription> ColorSchemes { get; }
|
||||
public BindableCollection<Tuple<string, double>> RenderScales { get; }
|
||||
public BindableCollection<Tuple<string, int>> TargetFrameRates { get; }
|
||||
|
||||
public Tuple<string, double> SelectedRenderScale
|
||||
{
|
||||
get => RenderScales.FirstOrDefault(s => Math.Abs(s.Item2 - CoreRenderScale.Value) < 0.01);
|
||||
set => CoreRenderScale.Value = value.Item2;
|
||||
}
|
||||
|
||||
public Tuple<string, int> SelectedTargetFrameRate
|
||||
{
|
||||
get => TargetFrameRates.FirstOrDefault(s => s.Item2 == CoreTargetFrameRate.Value);
|
||||
set => CoreTargetFrameRate.Value = value.Item2;
|
||||
}
|
||||
|
||||
public PluginSetting<bool> UIAutoRun => _settingsService.GetSetting("UI.AutoRun", false);
|
||||
public PluginSetting<int> UIAutoRunDelay => _settingsService.GetSetting("UI.AutoRunDelay", 15);
|
||||
public PluginSetting<bool> UIShowOnStartup => _settingsService.GetSetting("UI.ShowOnStartup", true);
|
||||
public PluginSetting<bool> UICheckForUpdates => _settingsService.GetSetting("UI.CheckForUpdates", true);
|
||||
public PluginSetting<ApplicationColorScheme> UIColorScheme => _settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic);
|
||||
public PluginSetting<bool> ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
||||
public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
|
||||
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan");
|
||||
public PluginSetting<double> CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.25);
|
||||
public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
|
||||
|
||||
private void UIAutoRunOnSettingChanged(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() => ApplyAutorun(false));
|
||||
}
|
||||
|
||||
private void UIAutoRunDelayOnSettingChanged(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() => ApplyAutorun(true));
|
||||
}
|
||||
|
||||
private void CorePreferredGraphicsContextOnSettingChanged(object sender, EventArgs e)
|
||||
{
|
||||
_registrationService.ApplyPreferredGraphicsContext();
|
||||
}
|
||||
|
||||
|
||||
private void ApplyAutorun(bool recreate)
|
||||
{
|
||||
if (!UIAutoRun.Value)
|
||||
UIShowOnStartup.Value = true;
|
||||
|
||||
// Remove the old auto-run method of placing a shortcut in shell:startup
|
||||
string autoRunFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Artemis.lnk");
|
||||
if (File.Exists(autoRunFile))
|
||||
File.Delete(autoRunFile);
|
||||
|
||||
if (Constants.BuildInfo.IsLocalBuild)
|
||||
return;
|
||||
|
||||
// Create or remove the task if necessary
|
||||
try
|
||||
{
|
||||
bool taskCreated = false;
|
||||
if (!recreate)
|
||||
taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
|
||||
|
||||
if (UIAutoRun.Value && !taskCreated)
|
||||
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(UIAutoRunDelay.Value));
|
||||
else if (!UIAutoRun.Value && taskCreated)
|
||||
SettingsUtilities.RemoveAutoRunTask();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Execute.PostToUIThread(() => _dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#region View methods
|
||||
|
||||
public bool CanOfferUpdatesIfFound
|
||||
{
|
||||
get => _canOfferUpdatesIfFound;
|
||||
set => SetAndNotify(ref _canOfferUpdatesIfFound, value);
|
||||
}
|
||||
|
||||
public void ShowDebugger()
|
||||
{
|
||||
_debugService.ShowDebugger();
|
||||
}
|
||||
|
||||
public void ShowLogsFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowSetupWizard()
|
||||
{
|
||||
_windowManager.ShowDialog(_kernel.Get<StartupWizardViewModel>());
|
||||
}
|
||||
|
||||
public void ShowDataFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Constants.DataFolder);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn\'t open the data folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async void OfferUpdatesIfFound()
|
||||
{
|
||||
if (!CanOfferUpdatesIfFound)
|
||||
return;
|
||||
|
||||
CanOfferUpdatesIfFound = false;
|
||||
try
|
||||
{
|
||||
bool updateFound = await _updateService.OfferUpdateIfFound();
|
||||
if (!updateFound)
|
||||
_messageService.ShowMessage("You are already running the latest Artemis build. (☞゚ヮ゚)☞");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_messageService.ShowMessage($"Failed to check for updates: {exception.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
CanOfferUpdatesIfFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
Task.Run(() => ApplyAutorun(false));
|
||||
|
||||
UIAutoRun.SettingChanged += UIAutoRunOnSettingChanged;
|
||||
UIAutoRunDelay.SettingChanged += UIAutoRunDelayOnSettingChanged;
|
||||
CorePreferredGraphicsContext.SettingChanged += CorePreferredGraphicsContextOnSettingChanged;
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
UIAutoRun.SettingChanged -= UIAutoRunOnSettingChanged;
|
||||
UIAutoRunDelay.SettingChanged -= UIAutoRunDelayOnSettingChanged;
|
||||
CorePreferredGraphicsContext.SettingChanged -= CorePreferredGraphicsContextOnSettingChanged;
|
||||
|
||||
_settingsService.SaveAllSettings();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum ApplicationColorScheme
|
||||
{
|
||||
Light,
|
||||
Dark,
|
||||
Automatic
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
<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:layerBrushes="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
|
||||
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
|
||||
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="2400"
|
||||
x:Class="Artemis.UI.Screens.Settings.GeneralTabView"
|
||||
x:DataType="settings:GeneralTabViewModel">
|
||||
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="15" MaxWidth="1000">
|
||||
<!-- General settings -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
General
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Auto-run on startup</TextBlock>
|
||||
</StackPanel>
|
||||
<ToggleSwitch Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" IsChecked="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Hide window on auto-run</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding !UIShowOnStartup.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Startup delay</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Set the amount of seconds to wait before auto-running Artemis.
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<TextBox Text="{CompiledBinding UIAutoRunDelay.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120" />
|
||||
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Color scheme</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Pick between a light and dark color scheme, the automatic option copies your system settings.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<controls:EnumComboBox Width="150" Value="{CompiledBinding UIColorScheme.Value}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Log level
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Sets the logging level, a higher logging level will result in more log files.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<controls:EnumComboBox Width="150" Value="{CompiledBinding CoreLoggingLevel.Value}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock>
|
||||
Logs
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
Opens the directory where logs are stored.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Command="{CompiledBinding ShowLogs}" Width="150" Content="Show logs" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Web server settings -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
Web server
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Web server port
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Artemis runs a local web server that can be used to externally interact with the application.
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
This web server can only be accessed by applications running on your own computer, e.g. supported games.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<TextBox Text="{CompiledBinding WebServerPort.Value}" Width="150" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Update settings -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
Updating
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Check for updates
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If enabled, we'll check for updates on startup and periodically while running.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock>
|
||||
Update
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
Use the button on the right to check for updates now.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Command="{Binding OfferUpdatesIfFound}" Width="150" Content="Check now" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Profile editor settings -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
Profile editor
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Show condition data model values
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
While selecting a condition target, show the current values of the data model.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding ProfileEditorShowDataModelValues.Value}" MinWidth="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Default brush
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Sets the default brush that is applied to new layers
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<StackPanel.Styles>
|
||||
<Style Selector="ComboBox.brush /template/ ContentControl#ContentPresenter">
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Height="20" Width="20" VerticalAlignment="Center" Margin="0 0 5 0"/>
|
||||
<TextBlock Text="{CompiledBinding DisplayName}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</StackPanel.Styles>
|
||||
<ComboBox Classes="brush"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Items="{CompiledBinding LayerBrushDescriptors}"
|
||||
SelectedItem="{CompiledBinding SelectedLayerBrushDescriptor}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type layerBrushes:LayerBrushDescriptor}">
|
||||
<Grid ColumnDefinitions="30,*" RowDefinitions="Auto,Auto">
|
||||
<avalonia:MaterialIcon Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Kind="{CompiledBinding Icon}"
|
||||
Height="20"
|
||||
Width="20"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{CompiledBinding DisplayName}" TextWrapping="Wrap" MaxWidth="350" />
|
||||
<TextBlock Classes="subtitle" Grid.Row="1" Grid.Column="1" Text="{CompiledBinding Description}" TextWrapping="Wrap" MaxWidth="350" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Rendering settings -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
Rendering
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Preferred render method
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Software-based rendering is done purely on the CPU while Vulkan uses GPU-acceleration.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Width="150"
|
||||
SelectedItem="{CompiledBinding CorePreferredGraphicsContext.Value}"
|
||||
Items="{CompiledBinding GraphicsContexts}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Render scale
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Sets the resolution Artemis renders at, higher scale means more CPU-usage, especially on large surfaces.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Width="150"
|
||||
SelectedItem="{CompiledBinding SelectedRenderScale}"
|
||||
Items="{CompiledBinding RenderScales}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding [0]}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Target frame rate
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Sets the FPS Artemis tries to render at, higher FPS means more CPU-usage but smoother animations.
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
The options past 45 FPS are mostly useless unless you are using a custom device.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Width="150"
|
||||
SelectedItem="{CompiledBinding SelectedTargetFrameRate}"
|
||||
Items="{CompiledBinding TargetFrameRates}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding [0]}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Tools -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
Tools
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock>
|
||||
Setup wizard
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
Opens the startup wizard usually shown when Artemis first starts.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Command="{CompiledBinding ShowSetupWizard}" Width="150" Content="Show wizard" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock>
|
||||
Debugger
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
Use the debugger to see the raw image Artemis is rendering on the surface.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Command="{CompiledBinding ShowDebugger}" Width="150" Content="Show debugger" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock>
|
||||
Application files
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
Opens the directory where application files like plugins and settings are stored.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Command="{CompiledBinding ShowDataFolder}" Width="150" Content="Show app files" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public partial class GeneralTabView : ReactiveUserControl<GeneralTabViewModel>
|
||||
{
|
||||
public GeneralTabView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using FluentAvalonia.Styling;
|
||||
using ReactiveUI;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class GeneralTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly PluginSetting<LayerBrushReference> _defaultLayerBrushDescriptor;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDebugService _debugService;
|
||||
private readonly FluentAvaloniaTheme _fluentAvaloniaTheme;
|
||||
|
||||
public GeneralTabViewModel(ISettingsService settingsService, IPluginManagementService pluginManagementService, IDebugService debugService)
|
||||
{
|
||||
DisplayName = "General";
|
||||
_settingsService = settingsService;
|
||||
_debugService = debugService;
|
||||
_fluentAvaloniaTheme = AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>(); ;
|
||||
|
||||
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
|
||||
LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
|
||||
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
||||
{
|
||||
LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
|
||||
BrushType = "SolidBrush"
|
||||
});
|
||||
|
||||
ShowLogs = ReactiveCommand.Create(ExecuteShowLogs);
|
||||
CheckForUpdate = ReactiveCommand.CreateFromTask(ExecuteCheckForUpdate);
|
||||
ShowSetupWizard = ReactiveCommand.Create(ExecuteShowSetupWizard);
|
||||
ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger);
|
||||
ShowDataFolder = ReactiveCommand.Create(ExecuteShowDataFolder);
|
||||
|
||||
UIColorScheme.SettingChanged += UIColorSchemeOnSettingChanged;
|
||||
}
|
||||
|
||||
private void UIColorSchemeOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (UIColorScheme.Value == ApplicationColorScheme.Automatic)
|
||||
_fluentAvaloniaTheme.RequestedTheme = null;
|
||||
else if (UIColorScheme.Value == ApplicationColorScheme.Light)
|
||||
_fluentAvaloniaTheme.RequestedTheme = "Light";
|
||||
else if (UIColorScheme.Value == ApplicationColorScheme.Dark)
|
||||
_fluentAvaloniaTheme.RequestedTheme = "Dark";
|
||||
else
|
||||
_fluentAvaloniaTheme.RequestedTheme = _fluentAvaloniaTheme.RequestedTheme;
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ShowLogs { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckForUpdate { get; }
|
||||
public ReactiveCommand<Unit, Unit> ShowSetupWizard { get; }
|
||||
public ReactiveCommand<Unit, Unit> ShowDebugger { get; }
|
||||
public ReactiveCommand<Unit, Unit> ShowDataFolder { get; }
|
||||
|
||||
public ObservableCollection<LayerBrushDescriptor> LayerBrushDescriptors { get; }
|
||||
|
||||
public ObservableCollection<string> GraphicsContexts { get; } = new()
|
||||
{
|
||||
"Software",
|
||||
"Vulkan"
|
||||
};
|
||||
|
||||
public ObservableCollection<(string, double)> RenderScales { get; } = new()
|
||||
{
|
||||
new ValueTuple<string, double>("25%", 0.25),
|
||||
new ValueTuple<string, double>("50%", 0.5),
|
||||
new ValueTuple<string, double>("100%", 1)
|
||||
};
|
||||
|
||||
public ObservableCollection<(string, int)> TargetFrameRates { get; } = new()
|
||||
{
|
||||
new ValueTuple<string, int>("10 FPS", 10),
|
||||
new ValueTuple<string, int>("20 FPS", 20),
|
||||
new ValueTuple<string, int>("30 FPS", 30),
|
||||
new ValueTuple<string, int>("45 FPS", 45),
|
||||
new ValueTuple<string, int>("60 FPS (lol)", 60),
|
||||
new ValueTuple<string, int>("144 FPS (omegalol)", 144)
|
||||
};
|
||||
|
||||
public LayerBrushDescriptor? SelectedLayerBrushDescriptor
|
||||
{
|
||||
get => LayerBrushDescriptors.FirstOrDefault(d => d.MatchesLayerBrushReference(_defaultLayerBrushDescriptor.Value));
|
||||
set
|
||||
{
|
||||
if (value != null) _defaultLayerBrushDescriptor.Value = new LayerBrushReference(value);
|
||||
}
|
||||
}
|
||||
|
||||
public (string, double)? SelectedRenderScale
|
||||
{
|
||||
get => RenderScales.FirstOrDefault(s => Math.Abs(s.Item2 - CoreRenderScale.Value) < 0.01);
|
||||
set
|
||||
{
|
||||
if (value != null) CoreRenderScale.Value = value.Value.Item2;
|
||||
}
|
||||
}
|
||||
|
||||
public (string, int)? SelectedTargetFrameRate
|
||||
{
|
||||
get => TargetFrameRates.FirstOrDefault(s => s.Item2 == CoreTargetFrameRate.Value);
|
||||
set
|
||||
{
|
||||
if (value != null) CoreTargetFrameRate.Value = value.Value.Item2;
|
||||
}
|
||||
}
|
||||
|
||||
public PluginSetting<bool> UIAutoRun => _settingsService.GetSetting("UI.AutoRun", false);
|
||||
public PluginSetting<int> UIAutoRunDelay => _settingsService.GetSetting("UI.AutoRunDelay", 15);
|
||||
public PluginSetting<bool> UIShowOnStartup => _settingsService.GetSetting("UI.ShowOnStartup", true);
|
||||
public PluginSetting<bool> UICheckForUpdates => _settingsService.GetSetting("UI.CheckForUpdates", true);
|
||||
public PluginSetting<ApplicationColorScheme> UIColorScheme => _settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic);
|
||||
public PluginSetting<bool> ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
||||
public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
|
||||
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan");
|
||||
public PluginSetting<double> CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.25);
|
||||
public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
|
||||
|
||||
#region General
|
||||
|
||||
private void ExecuteShowLogs()
|
||||
{
|
||||
OpenFolder(Constants.LogsFolder);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Updating
|
||||
|
||||
private Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
private void ExecuteShowSetupWizard()
|
||||
{
|
||||
}
|
||||
|
||||
private void ExecuteShowDebugger()
|
||||
{
|
||||
_debugService.ShowDebugger();
|
||||
}
|
||||
|
||||
|
||||
private void ExecuteShowDataFolder()
|
||||
{
|
||||
OpenFolder(Constants.DataFolder);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OpenFolder(string path)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ApplicationColorScheme
|
||||
{
|
||||
Light,
|
||||
Dark,
|
||||
Automatic
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Plugins.PluginFeatureView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:PluginFeatureViewModel}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary
|
||||
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="-3 -8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="30" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon column -->
|
||||
<shared:ArtemisIcon Grid.Column="0"
|
||||
Icon="{Binding FeatureInfo.ResolvedIcon}"
|
||||
Width="20"
|
||||
Height="20"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" />
|
||||
|
||||
<Button Grid.Column="0"
|
||||
Margin="-8"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, FallbackValue=Collapsed}"
|
||||
Style="{StaticResource MaterialDesignIconButton}"
|
||||
Foreground="#E74C4C"
|
||||
ToolTip="An exception occurred while enabling this feature, click to view"
|
||||
Command="{s:Action ViewLoadException}">
|
||||
<materialDesign:PackIcon Kind="AlertCircle" />
|
||||
</Button>
|
||||
|
||||
<!-- Display name column -->
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding FeatureInfo.Name}"
|
||||
Style="{StaticResource MaterialDesignTextBlock}"
|
||||
VerticalAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip="{Binding FeatureInfo.Description}" />
|
||||
|
||||
<!-- Enable toggle column -->
|
||||
<StackPanel Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="8"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Top"
|
||||
ToolTip="This feature cannot be disabled without disabling the whole plugin"
|
||||
ToolTipService.IsEnabled="{Binding FeatureInfo.AlwaysEnabled}">
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull"
|
||||
ToolTip="Plugin requires admin rights"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 5 0"
|
||||
Visibility="{Binding ShowShield, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
||||
IsChecked="{Binding IsEnabled}"
|
||||
IsEnabled="{Binding CanToggleEnabled}">
|
||||
Feature enabled
|
||||
</CheckBox>
|
||||
|
||||
<materialDesign:PopupBox Style="{StaticResource MaterialDesignToolPopupBox}"
|
||||
Margin="0 -4 -10 -4"
|
||||
Foreground="{StaticResource MaterialDesignBody}"
|
||||
IsEnabled="{Binding IsPopupEnabled}">
|
||||
<StackPanel>
|
||||
<Button Command="{s:Action InstallPrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="CheckAll" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Install prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action RemovePrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Delete" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Remove prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay, FallbackValue=Collapsed}">
|
||||
<ProgressBar Style="{StaticResource MaterialDesignCircularProgressBar}" Value="0" IsIndeterminate="True" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,200 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.Plugins;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
{
|
||||
public class PluginFeatureViewModel : Screen
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private bool _enabling;
|
||||
|
||||
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
|
||||
bool showShield,
|
||||
ICoreService coreService,
|
||||
IDialogService dialogService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IMessageService messageService)
|
||||
{
|
||||
_coreService = coreService;
|
||||
_dialogService = dialogService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_messageService = messageService;
|
||||
|
||||
FeatureInfo = pluginFeatureInfo;
|
||||
ShowShield = FeatureInfo.Plugin.Info.RequiresAdmin && showShield;
|
||||
}
|
||||
|
||||
public PluginFeatureInfo FeatureInfo { get; }
|
||||
public Exception LoadException => FeatureInfo.LoadException;
|
||||
|
||||
public bool ShowShield { get; }
|
||||
|
||||
public bool Enabling
|
||||
{
|
||||
get => _enabling;
|
||||
set => SetAndNotify(ref _enabling, value);
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled;
|
||||
set => Task.Run(() => UpdateEnabled(value));
|
||||
}
|
||||
|
||||
public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled;
|
||||
public bool CanInstallPrerequisites => FeatureInfo.Prerequisites.Any();
|
||||
public bool CanRemovePrerequisites => FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any());
|
||||
public bool IsPopupEnabled => CanInstallPrerequisites || CanRemovePrerequisites;
|
||||
|
||||
public void ShowLogsFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void ViewLoadException()
|
||||
{
|
||||
if (LoadException == null)
|
||||
return;
|
||||
|
||||
_dialogService.ShowExceptionDialog("Feature failed to enable", LoadException);
|
||||
}
|
||||
|
||||
public async Task InstallPrerequisites()
|
||||
{
|
||||
if (FeatureInfo.Prerequisites.Any())
|
||||
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo});
|
||||
}
|
||||
|
||||
public async Task RemovePrerequisites()
|
||||
{
|
||||
if (FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any()))
|
||||
{
|
||||
await PluginPrerequisitesUninstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo});
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
_pluginManagementService.PluginFeatureEnabling += OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled += OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed += OnFeatureEnableStopped;
|
||||
|
||||
FeatureInfo.Plugin.Enabled += PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled += PluginOnToggled;
|
||||
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
_pluginManagementService.PluginFeatureEnabling -= OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed -= OnFeatureEnableStopped;
|
||||
|
||||
FeatureInfo.Plugin.Enabled -= PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled -= PluginOnToggled;
|
||||
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
private async Task UpdateEnabled(bool enable)
|
||||
{
|
||||
if (IsEnabled == enable)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FeatureInfo.Instance == null)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
_messageService.ShowMessage($"Feature '{FeatureInfo.Name}' is in a broken state and cannot enable.", "VIEW LOGS", ShowLogsFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
{
|
||||
Enabling = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (FeatureInfo.Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("Enable feature", "The plugin of this feature requires admin rights, are you sure you want to enable it?");
|
||||
if (!confirmed)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all prerequisites are met async
|
||||
if (!FeatureInfo.ArePrerequisitesMet())
|
||||
{
|
||||
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, new List<IPrerequisitesSubject> {FeatureInfo});
|
||||
if (!FeatureInfo.ArePrerequisitesMet())
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance!, true));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_messageService.ShowMessage($"Failed to enable '{FeatureInfo.Name}'.\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabling = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pluginManagementService.DisablePluginFeature(FeatureInfo.Instance, true);
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature != FeatureInfo.Instance) return;
|
||||
Enabling = true;
|
||||
}
|
||||
|
||||
private void OnFeatureEnableStopped(object sender, PluginFeatureEventArgs e)
|
||||
{
|
||||
if (e.PluginFeature != FeatureInfo.Instance) return;
|
||||
Enabling = false;
|
||||
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(LoadException));
|
||||
}
|
||||
|
||||
private void PluginOnToggled(object sender, EventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(CanToggleEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Plugins.PluginSettingsTabView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:PluginSettingsTabViewModel}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="0 15" Width="910">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="200" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" TextWrapping="Wrap">
|
||||
The list below shows all loaded plugins. <LineBreak />
|
||||
If you're missing something, view your logs folder.
|
||||
</TextBlock>
|
||||
<materialDesign:PackIcon Grid.Column="1"
|
||||
Kind="Search"
|
||||
VerticalAlignment="Top"
|
||||
Margin="15 8 0 0" />
|
||||
<TextBox Grid.Column="2"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||
materialDesign:HintAssist.Hint="Search plugin"
|
||||
materialDesign:HintAssist.IsFloating="False"
|
||||
VerticalAlignment="Top"
|
||||
Margin="5 0"
|
||||
Text="{Binding SearchPluginInput, Delay=300, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<Button Grid.Column="3" Style="{DynamicResource MaterialDesignFlatButton}"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{s:Action OpenUrl}"
|
||||
CommandParameter="https://wiki.artemis-rgb.com/en/guides/user/plugins">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="OpenInBrowser" />
|
||||
<TextBlock Margin="8 0 0 0" VerticalAlignment="Center">Get more plugins</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="4"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Command="{s:Action ImportPlugin}"
|
||||
Margin="15 0">
|
||||
IMPORT PLUGIN
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ListBox Grid.Row="1"
|
||||
ItemsSource="{Binding Items}"
|
||||
VirtualizingPanel.ScrollUnit="Pixel"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
Margin="15 0 15 15">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}" Margin="5" HorizontalAlignment="Center" IsTabStop="False" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListBoxItem}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ListBoxItem}">
|
||||
<ContentPresenter />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,119 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
{
|
||||
public class PluginSettingsTabViewModel : Conductor<PluginSettingsViewModel>.Collection.AllActive
|
||||
{
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||
private string _searchPluginInput;
|
||||
private List<PluginSettingsViewModel> _instances;
|
||||
|
||||
public PluginSettingsTabViewModel(IPluginManagementService pluginManagementService, IMessageService messageService, ISettingsVmFactory settingsVmFactory)
|
||||
{
|
||||
DisplayName = "PLUGINS";
|
||||
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_messageService = messageService;
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
}
|
||||
|
||||
public string SearchPluginInput
|
||||
{
|
||||
get => _searchPluginInput;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _searchPluginInput, value)) return;
|
||||
UpdatePluginSearch();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePluginSearch()
|
||||
{
|
||||
if (_instances == null)
|
||||
return;
|
||||
|
||||
List<PluginSettingsViewModel> instances = _instances;
|
||||
string search = SearchPluginInput?.ToLower();
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
instances = instances.Where(i => i.Plugin.Info.Name.ToLower().Contains(search) ||
|
||||
i.Plugin.Info.Description != null && i.Plugin.Info.Description.ToLower().Contains(search)).ToList();
|
||||
|
||||
foreach (PluginSettingsViewModel pluginSettingsViewModel in instances)
|
||||
{
|
||||
if (!Items.Contains(pluginSettingsViewModel))
|
||||
Items.Add(pluginSettingsViewModel);
|
||||
}
|
||||
|
||||
foreach (PluginSettingsViewModel pluginSettingsViewModel in Items.ToList())
|
||||
{
|
||||
if (!instances.Contains(pluginSettingsViewModel))
|
||||
Items.Remove(pluginSettingsViewModel);
|
||||
}
|
||||
|
||||
((BindableCollection<PluginSettingsViewModel>) Items).Sort(i => i.Plugin.Info.Name);
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
// Take it off the UI thread to avoid freezing on tab change
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(200);
|
||||
GetPluginInstances();
|
||||
});
|
||||
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
public void OpenUrl(string url)
|
||||
{
|
||||
Core.Utilities.OpenUrl(url);
|
||||
}
|
||||
|
||||
public async Task ImportPlugin()
|
||||
{
|
||||
VistaOpenFileDialog dialog = new() {Filter = "ZIP files (*.zip)|*.zip", Title = "Import Artemis plugin"};
|
||||
bool? result = dialog.ShowDialog();
|
||||
if (result != true)
|
||||
return;
|
||||
|
||||
// Take the actual import off of the UI thread
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Plugin plugin = _pluginManagementService.ImportPlugin(dialog.FileName);
|
||||
|
||||
GetPluginInstances();
|
||||
SearchPluginInput = plugin.Info.Name;
|
||||
|
||||
// Enable it via the VM to enable the prerequisite dialog
|
||||
PluginSettingsViewModel pluginViewModel = Items.FirstOrDefault(i => i.Plugin == plugin);
|
||||
if (pluginViewModel is {IsEnabled: false})
|
||||
pluginViewModel.IsEnabled = true;
|
||||
|
||||
_messageService.ShowMessage($"Imported plugin: {plugin.Info.Name}");
|
||||
});
|
||||
}
|
||||
|
||||
public void GetPluginInstances()
|
||||
{
|
||||
_instances = _pluginManagementService.GetAllPlugins()
|
||||
.Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
|
||||
.OrderBy(i => i.Plugin.Info.Name)
|
||||
.ToList();
|
||||
|
||||
UpdatePluginSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.Plugins.PluginSettingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:devices="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:behaviors="clr-namespace:Artemis.UI.Behaviors"
|
||||
d:DataContext="{d:DesignInstance devices:PluginSettingsViewModel}"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<materialDesign:Card Width="900">
|
||||
<Grid Margin="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="4*" />
|
||||
<ColumnDefinition Width="5*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" Margin="0 10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<shared:ArtemisIcon Icon="{Binding Plugin.Info.ResolvedIcon}"
|
||||
Width="48"
|
||||
Height="48"
|
||||
Margin="0 5 0 0"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Style="{StaticResource MaterialDesignBody2TextBlock}"
|
||||
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}"
|
||||
behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Name}"
|
||||
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
|
||||
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignBody2TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}"
|
||||
Text="{Binding Plugin.Info.Author}"
|
||||
Visibility="{Binding Plugin.Info.Author, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="2"
|
||||
TextWrapping="Wrap"
|
||||
behaviors:HighlightTermBehavior.TermToBeHighlighted="{Binding Parent.SearchPluginInput}"
|
||||
behaviors:HighlightTermBehavior.Text="{Binding Plugin.Info.Description}"
|
||||
behaviors:HighlightTermBehavior.HighlightForeground="{StaticResource Primary600Foreground}"
|
||||
behaviors:HighlightTermBehavior.HighlightBackground="{StaticResource Primary600}"
|
||||
Style="{StaticResource MaterialDesignTextBlock}"
|
||||
Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel VerticalAlignment="Bottom" Orientation="Horizontal">
|
||||
<Button VerticalAlignment="Bottom"
|
||||
Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
ToolTip="Open the plugins settings window"
|
||||
Margin="4"
|
||||
Command="{s:Action OpenSettings}">
|
||||
SETTINGS
|
||||
</Button>
|
||||
|
||||
<materialDesign:PopupBox Style="{StaticResource MaterialDesignToolPopupBox}"
|
||||
Padding="2 0 2 0"
|
||||
Foreground="{StaticResource MaterialDesignBody}"
|
||||
IsPopupOpen="{Binding IsSettingsPopupOpen, Mode=TwoWay}">
|
||||
<StackPanel>
|
||||
<Button Command="{s:Action OpenPluginDirectory}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="FolderOpen" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Open plugin directory</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Reload}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Reload" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Reload plugin</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Command="{s:Action InstallPrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="CheckAll" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Install prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action RemovePrerequisites}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="Delete" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Remove prerequisites</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Command="{s:Action RemoveSettings}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="DatabaseRemove" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Clear plugin settings</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{s:Action Remove}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="DeleteForever" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center">Remove plugin</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="{Binding Plugin.Info.Website}"
|
||||
Visibility="{Binding Plugin.Info.Website, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}"
|
||||
Command="{s:Action OpenUri}"
|
||||
CommandParameter="{Binding Plugin.Info.Website}">
|
||||
<materialDesign:PackIcon Kind="Web" Width="20" Height="20" />
|
||||
</Button>
|
||||
|
||||
<Button Height="40"
|
||||
Width="40"
|
||||
Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||
ToolTip="{Binding Plugin.Info.Repository}"
|
||||
Visibility="{Binding Plugin.Info.Repository, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}"
|
||||
Command="{s:Action OpenUri}"
|
||||
CommandParameter="{Binding Plugin.Info.Repository}">
|
||||
<materialDesign:PackIcon Kind="Git" Width="20" Height="20" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="8"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"
|
||||
Style="{StaticResource MaterialDesignAccentCheckBox}" IsChecked="{Binding IsEnabled}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock>Plugin enabled</TextBlock>
|
||||
<materialDesign:PackIcon Kind="ShieldHalfFull"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip="Plugin requires admin rights"
|
||||
Visibility="{Binding Plugin.Info.RequiresAdmin, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</CheckBox>
|
||||
|
||||
<ProgressBar Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="8"
|
||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"
|
||||
Style="{StaticResource MaterialDesignCircularProgressBar}" Value="0"
|
||||
IsIndeterminate="True" />
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" BorderBrush="{StaticResource MaterialDesignDivider}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Margin="10 10 0 5" Style="{StaticResource MaterialDesignBody2TextBlock}">Plugin features</TextBlock>
|
||||
<ListBox Grid.Row="1"
|
||||
MaxHeight="135"
|
||||
ItemsSource="{Binding Items}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VirtualizingPanel.ScrollUnit="Pixel">
|
||||
<b:Interaction.Behaviors>
|
||||
<shared:ScrollParentWhenAtMax />
|
||||
</b:Interaction.Behaviors>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding IsAsync=True}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
</UserControl>
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for PluginSettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class PluginSettingsView : UserControl
|
||||
{
|
||||
public PluginSettingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.Plugins;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Ninject;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
{
|
||||
public class PluginSettingsViewModel : Conductor<PluginFeatureViewModel>.Collection.AllActive
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||
private readonly IWindowManager _windowManager;
|
||||
private bool _canInstallPrerequisites;
|
||||
private bool _canRemovePrerequisites;
|
||||
private bool _enabling;
|
||||
private bool _isSettingsPopupOpen;
|
||||
private Plugin _plugin;
|
||||
|
||||
public PluginSettingsViewModel(Plugin plugin,
|
||||
ISettingsVmFactory settingsVmFactory,
|
||||
ICoreService coreService,
|
||||
IWindowManager windowManager,
|
||||
IDialogService dialogService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IMessageService messageService)
|
||||
{
|
||||
Plugin = plugin;
|
||||
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
_coreService = coreService;
|
||||
_windowManager = windowManager;
|
||||
_dialogService = dialogService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_messageService = messageService;
|
||||
}
|
||||
|
||||
public Plugin Plugin
|
||||
{
|
||||
get => _plugin;
|
||||
set => SetAndNotify(ref _plugin, value);
|
||||
}
|
||||
|
||||
public bool Enabling
|
||||
{
|
||||
get => _enabling;
|
||||
set => SetAndNotify(ref _enabling, value);
|
||||
}
|
||||
|
||||
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
|
||||
public bool CanOpenSettings => IsEnabled && Plugin.ConfigurationDialog != null;
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => Plugin.IsEnabled;
|
||||
set => Task.Run(() => UpdateEnabled(value));
|
||||
}
|
||||
|
||||
public bool IsSettingsPopupOpen
|
||||
{
|
||||
get => _isSettingsPopupOpen;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _isSettingsPopupOpen, value)) return;
|
||||
CheckPrerequisites();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanInstallPrerequisites
|
||||
{
|
||||
get => _canInstallPrerequisites;
|
||||
set => SetAndNotify(ref _canInstallPrerequisites, value);
|
||||
}
|
||||
|
||||
public bool CanRemovePrerequisites
|
||||
{
|
||||
get => _canRemovePrerequisites;
|
||||
set => SetAndNotify(ref _canRemovePrerequisites, value);
|
||||
}
|
||||
|
||||
public void OpenSettings()
|
||||
{
|
||||
PluginConfigurationDialog configurationViewModel = (PluginConfigurationDialog) Plugin.ConfigurationDialog;
|
||||
if (configurationViewModel == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
PluginConfigurationViewModel viewModel = (PluginConfigurationViewModel) Plugin.Kernel.Get(configurationViewModel.Type);
|
||||
_windowManager.ShowWindow(new PluginSettingsWindowViewModel(viewModel));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("An exception occured while trying to show the plugin's settings window", e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenPluginDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Plugin.Directory.FullName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn't open the device's plugin folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Reload()
|
||||
{
|
||||
bool wasEnabled = IsEnabled;
|
||||
|
||||
_pluginManagementService.UnloadPlugin(Plugin);
|
||||
Items.Clear();
|
||||
|
||||
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
|
||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||
|
||||
if (wasEnabled)
|
||||
await UpdateEnabled(true);
|
||||
|
||||
_messageService.ShowMessage("Reloaded plugin.");
|
||||
}
|
||||
|
||||
public async Task InstallPrerequisites()
|
||||
{
|
||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
|
||||
|
||||
if (subjects.Any(s => s.Prerequisites.Any()))
|
||||
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, subjects);
|
||||
}
|
||||
|
||||
public async Task RemovePrerequisites(bool forPluginRemoval = false)
|
||||
{
|
||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||
subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features);
|
||||
|
||||
if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any())))
|
||||
{
|
||||
await PluginPrerequisitesUninstallDialogViewModel.Show(_dialogService, subjects, forPluginRemoval ? "SKIP, REMOVE PLUGIN" : "CANCEL");
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoveSettings()
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?");
|
||||
if (!confirmed)
|
||||
return;
|
||||
|
||||
bool wasEnabled = IsEnabled;
|
||||
|
||||
if (IsEnabled)
|
||||
await UpdateEnabled(false);
|
||||
|
||||
_pluginManagementService.RemovePluginSettings(Plugin);
|
||||
|
||||
if (wasEnabled)
|
||||
await UpdateEnabled(true);
|
||||
|
||||
_messageService.ShowMessage("Cleared plugin settings.");
|
||||
}
|
||||
|
||||
public async Task Remove()
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("Remove plugin", "Are you sure you want to remove this plugin?");
|
||||
if (!confirmed)
|
||||
return;
|
||||
|
||||
// If the plugin or any of its features has uninstall actions, offer to run these
|
||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||
subjects.AddRange(Plugin.Features);
|
||||
if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any())))
|
||||
await RemovePrerequisites(true);
|
||||
|
||||
try
|
||||
{
|
||||
_pluginManagementService.RemovePlugin(Plugin, false);
|
||||
((PluginSettingsTabViewModel) Parent).GetPluginInstances();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Failed to remove plugin", e);
|
||||
throw;
|
||||
}
|
||||
|
||||
_messageService.ShowMessage("Removed plugin.");
|
||||
}
|
||||
|
||||
public void ShowLogsFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(Constants.DataFolder, "Logs"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_dialogService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenUri(Uri uri)
|
||||
{
|
||||
Core.Utilities.OpenUrl(uri.ToString());
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginToggled(object sender, PluginEventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
|
||||
private async Task UpdateEnabled(bool enable)
|
||||
{
|
||||
if (IsEnabled == enable)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
{
|
||||
Enabling = true;
|
||||
|
||||
if (Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
|
||||
{
|
||||
bool confirmed = await _dialogService.ShowConfirmDialog("Enable plugin", "This plugin requires admin rights, are you sure you want to enable it?");
|
||||
if (!confirmed)
|
||||
{
|
||||
CancelEnable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all prerequisites are met async
|
||||
List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
|
||||
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled || f.EnabledInStorage));
|
||||
|
||||
if (subjects.Any(s => !s.ArePrerequisitesMet()))
|
||||
{
|
||||
await PluginPrerequisitesInstallDialogViewModel.Show(_dialogService, subjects);
|
||||
if (!subjects.All(s => s.ArePrerequisitesMet()))
|
||||
{
|
||||
CancelEnable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_pluginManagementService.EnablePlugin(Plugin, true, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_messageService.ShowMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabling = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
_pluginManagementService.DisablePlugin(Plugin, true);
|
||||
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
|
||||
private void CancelEnable()
|
||||
{
|
||||
Enabling = false;
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||
}
|
||||
|
||||
private void CheckPrerequisites()
|
||||
{
|
||||
CanInstallPrerequisites = Plugin.Info.Prerequisites.Any() ||
|
||||
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.Prerequisites.Any());
|
||||
CanRemovePrerequisites = Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()) ||
|
||||
Plugin.Features.Where(f => f.AlwaysEnabled).Any(f => f.Prerequisites.Any(p => p.UninstallActions.Any()));
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||
Items.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||
|
||||
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginToggled;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<controls:MaterialWindow x:Class="Artemis.UI.Screens.Settings.Tabs.Plugins.PluginSettingsWindowView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.Settings.Tabs.Plugins"
|
||||
xmlns:controls="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
Title="Plugin Configuration | Artemis"
|
||||
Background="{DynamicResource MaterialDesignPaper}"
|
||||
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
|
||||
UseLayoutRounding="True"
|
||||
Width="800"
|
||||
Height="800"
|
||||
d:DesignHeight="800"
|
||||
d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:PluginSettingsWindowViewModel}"
|
||||
Icon="/Resources/Images/Logo/bow.ico">
|
||||
<materialDesign:DialogHost IsTabStop="False"
|
||||
Focusable="False"
|
||||
Identifier="PluginSettingsDialog"
|
||||
DialogTheme="Inherit">
|
||||
<DockPanel>
|
||||
<controls:AppBar Type="Dense" Title="{Binding ActiveItem.Plugin.Info.Name}" DockPanel.Dock="Top" Margin="-18 0 0 0" ShowShadow="False">
|
||||
<controls:AppBar.AppIcon>
|
||||
<shared:ArtemisIcon Icon="{Binding Plugin.Info.ResolvedIcon}"
|
||||
Margin="0 5 0 0"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top" />
|
||||
</controls:AppBar.AppIcon>
|
||||
</controls:AppBar>
|
||||
|
||||
<ContentControl s:View.Model="{Binding ActiveItem}" />
|
||||
</DockPanel>
|
||||
</materialDesign:DialogHost>
|
||||
</controls:MaterialWindow>
|
||||
@@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
||||
{
|
||||
public class PluginSettingsWindowViewModel : Conductor<PluginConfigurationViewModel>
|
||||
{
|
||||
private readonly PluginConfigurationViewModel _configurationViewModel;
|
||||
|
||||
public PluginSettingsWindowViewModel(PluginConfigurationViewModel configurationViewModel)
|
||||
{
|
||||
_configurationViewModel = configurationViewModel ?? throw new ArgumentNullException(nameof(configurationViewModel));
|
||||
Plugin = configurationViewModel.Plugin;
|
||||
}
|
||||
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
ActiveItem = _configurationViewModel;
|
||||
ActiveItem.Closed += ActiveItemOnClosed;
|
||||
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
private void ActiveItemOnClosed(object sender, CloseEventArgs e)
|
||||
{
|
||||
ActiveItem.Closed -= ActiveItemOnClosed;
|
||||
RequestClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Settings.PluginsTabView">
|
||||
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,*" Width="900">
|
||||
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding SearchPluginInput}" Watermark="Search plugins" Margin="0 10" />
|
||||
<ScrollViewer Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl Items="{Binding Plugins}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public partial class PluginsTabView : ReactiveUserControl<PluginsTabViewModel>
|
||||
{
|
||||
public PluginsTabView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.Plugins;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Settings
|
||||
{
|
||||
public class PluginsTabViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||
private readonly IWindowService _windowService;
|
||||
private List<PluginSettingsViewModel>? _instances;
|
||||
private string? _searchPluginInput;
|
||||
|
||||
public PluginsTabViewModel(IPluginManagementService pluginManagementService, INotificationService notificationService, IWindowService windowService, ISettingsVmFactory settingsVmFactory)
|
||||
{
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_notificationService = notificationService;
|
||||
_windowService = windowService;
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
|
||||
DisplayName = "Plugins";
|
||||
Plugins = new ObservableCollection<PluginSettingsViewModel>();
|
||||
|
||||
this.WhenAnyValue(x => x.SearchPluginInput).Throttle(TimeSpan.FromMilliseconds(100)).Subscribe(SearchPlugins);
|
||||
this.WhenActivated((CompositeDisposable _) => GetPluginInstances());
|
||||
}
|
||||
|
||||
public ObservableCollection<PluginSettingsViewModel> Plugins { get; }
|
||||
|
||||
public string? SearchPluginInput
|
||||
{
|
||||
get => _searchPluginInput;
|
||||
set => RaiseAndSetIfChanged(ref _searchPluginInput, value);
|
||||
}
|
||||
|
||||
public void OpenUrl(string url)
|
||||
{
|
||||
Utilities.OpenUrl(url);
|
||||
}
|
||||
|
||||
public async Task ImportPlugin()
|
||||
{
|
||||
string[]? files = await _windowService.CreateOpenFileDialog().WithTitle("Import Artemis plugin").HavingFilter(f => f.WithExtension("zip").WithName("ZIP files")).ShowAsync();
|
||||
if (files == null)
|
||||
return;
|
||||
|
||||
// Take the actual import off of the UI thread
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Plugin plugin = _pluginManagementService.ImportPlugin(files[0]);
|
||||
|
||||
GetPluginInstances();
|
||||
SearchPluginInput = plugin.Info.Name;
|
||||
|
||||
// Enable it via the VM to enable the prerequisite dialog
|
||||
PluginSettingsViewModel? pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
|
||||
if (pluginViewModel is {IsEnabled: false})
|
||||
pluginViewModel.IsEnabled = true;
|
||||
|
||||
_notificationService.CreateNotification()
|
||||
.WithTitle("Success")
|
||||
.WithMessage($"Imported plugin: {plugin.Info.Name}")
|
||||
.WithSeverity(NotificationSeverity.Success)
|
||||
.Show();
|
||||
});
|
||||
}
|
||||
|
||||
public void GetPluginInstances()
|
||||
{
|
||||
Plugins.Clear();
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
_instances = _pluginManagementService.GetAllPlugins()
|
||||
.Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
|
||||
.OrderBy(i => i.Plugin.Info.Name)
|
||||
.ToList();
|
||||
|
||||
SearchPlugins(SearchPluginInput);
|
||||
}, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private void SearchPlugins(string? searchPluginInput)
|
||||
{
|
||||
if (_instances == null)
|
||||
return;
|
||||
|
||||
List<PluginSettingsViewModel> instances = _instances;
|
||||
string? search = searchPluginInput?.ToLower();
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
instances = instances.Where(i => i.Plugin.Info.Name.ToLower().Contains(search) ||
|
||||
i.Plugin.Info.Description != null && i.Plugin.Info.Description.ToLower().Contains(search)).ToList();
|
||||
|
||||
foreach (PluginSettingsViewModel pluginSettingsViewModel in instances)
|
||||
{
|
||||
if (!Plugins.Contains(pluginSettingsViewModel))
|
||||
Plugins.Add(pluginSettingsViewModel);
|
||||
}
|
||||
|
||||
foreach (PluginSettingsViewModel pluginSettingsViewModel in Plugins.ToList())
|
||||
{
|
||||
if (!instances.Contains(pluginSettingsViewModel))
|
||||
Plugins.Remove(pluginSettingsViewModel);
|
||||
}
|
||||
|
||||
Plugins.Sort(i => i.Plugin.Info.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user