From 6bbe6b6bbedc745b652d6a1188cc8ba3dbf0c32a Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 19 Oct 2021 21:32:21 +0200 Subject: [PATCH] UI - Settings General tab --- .../Plugins/Settings/PluginSettings.cs | 1 - src/Artemis.Core/Utilities/EnumUtilities.cs | 36 ++ .../Controls/DeviceVisualizer.cs | 6 +- .../Controls/EnumComboBox.axaml | 14 + .../Controls/EnumComboBox.axaml.cs | 109 ++++++ .../Interfaces/IArtemisSharedUIService.cs | 9 + src/Artemis.UI.Avalonia/App.axaml.cs | 4 + .../Artemis - Backup.UI.Avalonia.csproj | 76 ++++ .../Artemis.UI.Avalonia.csproj | 2 +- src/Artemis.UI.Avalonia/Ninject/UIModule.cs | 11 + src/Artemis.UI.Avalonia/Program.cs | 3 - .../ViewModels/SidebarCategoryViewModel.cs | 7 +- .../Root/ViewModels/SidebarScreenViewModel.cs | 1 - .../ViewModels/GeneralTabViewModel.cs | 154 +++++++- .../Settings/Views/GeneralTabView.axaml | 354 +++++++++++++++++- .../Settings/Views/GeneralTabView.axaml.cs | 4 +- .../Services/DebugService.cs | 33 ++ .../Services/Interfaces/IArtemisUIService.cs | 6 + .../Services/Interfaces/IDebugService.cs | 13 + .../EnumPropertyInputViewModel.cs | 1 + .../DataBindings/DataBindingViewModel.cs | 1 + .../CategoryAdaptionHintViewModel.cs | 1 + .../DeviceAdaptionHintViewModel.cs | 1 + .../KeyboardSectionAdaptionHintViewModel.cs | 1 + .../General/GeneralSettingsTabViewModel.cs | 2 - .../ProfileEdit/ProfileEditViewModel.cs | 1 + .../Steps/SettingsStepViewModel.cs | 1 + 27 files changed, 831 insertions(+), 21 deletions(-) create mode 100644 src/Artemis.Core/Utilities/EnumUtilities.cs create mode 100644 src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml create mode 100644 src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml.cs create mode 100644 src/Artemis.UI.Avalonia.Shared/Services/Interfaces/IArtemisSharedUIService.cs create mode 100644 src/Artemis.UI.Avalonia/Artemis - Backup.UI.Avalonia.csproj create mode 100644 src/Artemis.UI.Avalonia/Services/DebugService.cs create mode 100644 src/Artemis.UI.Avalonia/Services/Interfaces/IArtemisUIService.cs create mode 100644 src/Artemis.UI.Avalonia/Services/Interfaces/IDebugService.cs diff --git a/src/Artemis.Core/Plugins/Settings/PluginSettings.cs b/src/Artemis.Core/Plugins/Settings/PluginSettings.cs index 5b7a7409b..203b6185b 100644 --- a/src/Artemis.Core/Plugins/Settings/PluginSettings.cs +++ b/src/Artemis.Core/Plugins/Settings/PluginSettings.cs @@ -33,7 +33,6 @@ namespace Artemis.Core /// The type of the setting, can be any serializable type /// The name of the setting /// The default value to use if the setting does not exist yet - /// public PluginSetting GetSetting(string name, T? defaultValue = default) { lock (_settingEntities) diff --git a/src/Artemis.Core/Utilities/EnumUtilities.cs b/src/Artemis.Core/Utilities/EnumUtilities.cs new file mode 100644 index 000000000..def9e4a47 --- /dev/null +++ b/src/Artemis.Core/Utilities/EnumUtilities.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Humanizer; + +namespace Artemis.Core +{ + /// + /// Provides utilities for display enums in a human readable form + /// + public static class EnumUtilities + { + /// + /// Creates a list containing a tuple for each value in the enum type + /// + /// The enum type to create value descriptions for + /// A list containing a value-description tuple for each value in the enum type + public static List<(T, string)> GetAllValuesAndDescriptions() where T : struct, Enum + { + return Enum.GetValues().Select(e => (e, e.Humanize())).ToList(); + } + + /// + /// Creates a list containing a tuple for each value in the enum type + /// + /// The enum type to create value descriptions for + /// A list containing a value-description tuple for each value in the enum type + public static List<(Enum, string)> GetAllValuesAndDescriptions(Type t) + { + if (!t.IsEnum) + throw new ArgumentException($"{t} must be an enum type"); + + return Enum.GetValues(t).Cast().Select(e => (e, e.Humanize())).ToList(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Avalonia.Shared/Controls/DeviceVisualizer.cs index 76c83efc8..f0bff897a 100644 --- a/src/Artemis.UI.Avalonia.Shared/Controls/DeviceVisualizer.cs +++ b/src/Artemis.UI.Avalonia.Shared/Controls/DeviceVisualizer.cs @@ -142,7 +142,7 @@ namespace Artemis.UI.Avalonia.Shared.Controls /// Gets or sets the to display /// public static readonly StyledProperty DeviceProperty = - AvaloniaProperty.Register(nameof(Device)); + AvaloniaProperty.Register(nameof(Device)); /// /// Gets or sets the to display @@ -157,7 +157,7 @@ namespace Artemis.UI.Avalonia.Shared.Controls /// Gets or sets boolean indicating whether or not to show per-LED colors /// public static readonly StyledProperty ShowColorsProperty = - AvaloniaProperty.Register(nameof(ShowColors)); + AvaloniaProperty.Register(nameof(ShowColors)); /// /// Gets or sets a boolean indicating whether or not to show per-LED colors @@ -172,7 +172,7 @@ namespace Artemis.UI.Avalonia.Shared.Controls /// Gets or sets a list of LEDs to highlight /// public static readonly StyledProperty?> HighlightedLedsProperty = - AvaloniaProperty.Register?>(nameof(HighlightedLeds)); + AvaloniaProperty.Register?>(nameof(HighlightedLeds)); /// /// Gets or sets a list of LEDs to highlight diff --git a/src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml b/src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml new file mode 100644 index 000000000..04bcd5264 --- /dev/null +++ b/src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml.cs b/src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml.cs new file mode 100644 index 000000000..5616cc718 --- /dev/null +++ b/src/Artemis.UI.Avalonia.Shared/Controls/EnumComboBox.axaml.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Artemis.Core; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.LogicalTree; +using Avalonia.Markup.Xaml; + +namespace Artemis.UI.Avalonia.Shared.Controls +{ + public partial class EnumComboBox : UserControl + { + /// + /// Gets or sets the currently selected value + /// + public static readonly StyledProperty ValueProperty = + AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay, notifying: ValueChanged); + + /// + /// Gets or sets the currently selected value + /// + public object? Value + { + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); + } + + private ComboBox? _enumComboBox; + private readonly ObservableCollection<(Enum, string)> _currentValues = new(); + + private static void ValueChanged(IAvaloniaObject sender, bool before) + { + if (sender is EnumComboBox enumCombo && !before) + { + enumCombo.UpdateValues(); + enumCombo.UpdateSelection(); + } + } + + public EnumComboBox() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (_enumComboBox == null) + return; + + var (enumValue, _) = _currentValues[_enumComboBox.SelectedIndex]; + if (!Equals(Value, enumValue)) + Value = enumValue; + } + + private void UpdateValues() + { + Type? newType = Value?.GetType(); + if (_enumComboBox == null || _currentValues.Any() || newType is not {IsEnum: true}) + return; + + foreach ((Enum, string) valueDesc in EnumUtilities.GetAllValuesAndDescriptions(newType)) + _currentValues.Add(valueDesc); + } + + private void UpdateSelection() + { + if (_enumComboBox == null || Value is not Enum) + return; + + (Enum, string) value = _currentValues.FirstOrDefault(v => v.Item1.Equals(Value)); + if (!Equals(value.Item1, _enumComboBox.SelectedItem)) + _enumComboBox.SelectedItem = value; + } + + #region Overrides of TemplatedControl + + /// + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + _enumComboBox = this.Get("EnumComboBox"); + _enumComboBox.Items = _currentValues; + + UpdateValues(); + UpdateSelection(); + _enumComboBox.SelectionChanged += OnSelectionChanged; + + base.OnAttachedToLogicalTree(e); + } + + /// + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if (_enumComboBox != null) + _enumComboBox.SelectionChanged -= OnSelectionChanged; + + base.OnDetachedFromLogicalTree(e); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia.Shared/Services/Interfaces/IArtemisSharedUIService.cs b/src/Artemis.UI.Avalonia.Shared/Services/Interfaces/IArtemisSharedUIService.cs new file mode 100644 index 000000000..464c6bc41 --- /dev/null +++ b/src/Artemis.UI.Avalonia.Shared/Services/Interfaces/IArtemisSharedUIService.cs @@ -0,0 +1,9 @@ +namespace Artemis.UI.Avalonia.Shared.Services.Interfaces +{ + /// + /// Represents a service provided by the Artemis Shared UI library + /// + public interface IArtemisSharedUIService + { + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/App.axaml.cs b/src/Artemis.UI.Avalonia/App.axaml.cs index 345424826..577d9f343 100644 --- a/src/Artemis.UI.Avalonia/App.axaml.cs +++ b/src/Artemis.UI.Avalonia/App.axaml.cs @@ -4,8 +4,10 @@ using Artemis.UI.Avalonia.Screens.Root.ViewModels; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; +using Avalonia.Threading; using FluentAvalonia.Styling; using Ninject; +using ReactiveUI; using Splat.Ninject; namespace Artemis.UI.Avalonia @@ -17,6 +19,8 @@ namespace Artemis.UI.Avalonia public override void Initialize() { InitializeNinject(); + RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; + AvaloniaXamlLoader.Load(this); } diff --git a/src/Artemis.UI.Avalonia/Artemis - Backup.UI.Avalonia.csproj b/src/Artemis.UI.Avalonia/Artemis - Backup.UI.Avalonia.csproj new file mode 100644 index 000000000..71dc1529f --- /dev/null +++ b/src/Artemis.UI.Avalonia/Artemis - Backup.UI.Avalonia.csproj @@ -0,0 +1,76 @@ + + + WinExe + net5.0 + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + %(Filename) + + + %(Filename) + + + %(Filename) + + + %(Filename) + + + %(Filename) + + + SidebarView.axaml + Code + + + RootView.axaml + Code + + + + + + + + + + + + + ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj b/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj index 71dc1529f..8017343d6 100644 --- a/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj +++ b/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj @@ -5,9 +5,9 @@ enable - + diff --git a/src/Artemis.UI.Avalonia/Ninject/UIModule.cs b/src/Artemis.UI.Avalonia/Ninject/UIModule.cs index a934f3b91..61eebd3ae 100644 --- a/src/Artemis.UI.Avalonia/Ninject/UIModule.cs +++ b/src/Artemis.UI.Avalonia/Ninject/UIModule.cs @@ -1,6 +1,7 @@ using System; using Artemis.UI.Avalonia.Ninject.Factories; using Artemis.UI.Avalonia.Screens; +using Artemis.UI.Avalonia.Services.Interfaces; using Ninject.Extensions.Conventions; using Ninject.Modules; using Ninject.Planning.Bindings.Resolvers; @@ -40,6 +41,16 @@ namespace Artemis.UI.Avalonia.Ninject .InheritedFrom() .BindToFactory(); }); + + // Bind all UI services as singletons + Kernel.Bind(x => + { + x.FromThisAssembly() + .SelectAllClasses() + .InheritedFrom() + .BindAllInterfaces() + .Configure(c => c.InSingletonScope()); + }); } } } \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Program.cs b/src/Artemis.UI.Avalonia/Program.cs index 486440ee6..3609fd618 100644 --- a/src/Artemis.UI.Avalonia/Program.cs +++ b/src/Artemis.UI.Avalonia/Program.cs @@ -1,13 +1,10 @@ using Avalonia; using Avalonia.ReactiveUI; -using Ninject; namespace Artemis.UI.Avalonia { internal class Program { - private static StandardKernel _kernel; - // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. diff --git a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarCategoryViewModel.cs b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarCategoryViewModel.cs index 0d975bb74..9ec9fe247 100644 --- a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarCategoryViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarCategoryViewModel.cs @@ -11,8 +11,7 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels { private readonly IProfileService _profileService; private readonly ISidebarVmFactory _vmFactory; - private SidebarProfileConfigurationViewModel _selectedProfileConfiguration; - public ProfileCategory ProfileCategory { get; } + private SidebarProfileConfigurationViewModel? _selectedProfileConfiguration; public SidebarCategoryViewModel(ProfileCategory profileCategory, IProfileService profileService, ISidebarVmFactory vmFactory) { @@ -25,9 +24,11 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels CreateProfileViewModels(); } + public ProfileCategory ProfileCategory { get; } + public ObservableCollection ProfileConfigurations { get; } = new(); - public SidebarProfileConfigurationViewModel SelectedProfileConfiguration + public SidebarProfileConfigurationViewModel? SelectedProfileConfiguration { get => _selectedProfileConfiguration; set => this.RaiseAndSetIfChanged(ref _selectedProfileConfiguration, value); diff --git a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs index 051393e44..e8f997159 100644 --- a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs @@ -29,7 +29,6 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels } public MaterialIconKind Icon { get; } - public string DisplayName { get; } public abstract Type ScreenType { get; } public abstract MainScreenViewModel CreateInstance(IKernel kernel, IScreen screen); diff --git a/src/Artemis.UI.Avalonia/Screens/Settings/ViewModels/GeneralTabViewModel.cs b/src/Artemis.UI.Avalonia/Screens/Settings/ViewModels/GeneralTabViewModel.cs index 340935a80..c43a60339 100644 --- a/src/Artemis.UI.Avalonia/Screens/Settings/ViewModels/GeneralTabViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/Settings/ViewModels/GeneralTabViewModel.cs @@ -1,16 +1,164 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; using System.Linq; -using System.Text; +using System.Reactive; +using System.Threading; using System.Threading.Tasks; +using Artemis.Core; +using Artemis.Core.LayerBrushes; +using Artemis.Core.Services; +using Artemis.UI.Avalonia.Services.Interfaces; +using ReactiveUI; +using Serilog.Events; namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels { public class GeneralTabViewModel : ActivatableViewModelBase { - public GeneralTabViewModel() + private readonly PluginSetting _defaultLayerBrushDescriptor; + private readonly ISettingsService _settingsService; + + public GeneralTabViewModel(ISettingsService settingsService, IPluginManagementService pluginManagementService, IDebugService debuggerService) { DisplayName = "General"; + _settingsService = settingsService; + + List layerBrushProviders = pluginManagementService.GetFeaturesOfType(); + LayerBrushDescriptors = new ObservableCollection(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); + } + + public ReactiveCommand ShowLogs { get; } + public ReactiveCommand CheckForUpdate { get; } + public ReactiveCommand ShowSetupWizard { get; } + public ReactiveCommand ShowDebugger { get; } + public ReactiveCommand ShowDataFolder { get; } + + public ObservableCollection LayerBrushDescriptors { get; } + + public ObservableCollection GraphicsContexts { get; } = new() + { + "Software", + "Vulkan" + }; + + public ObservableCollection<(string, double)> RenderScales { get; } = new() + { + new ValueTuple("25%", 0.25), + new ValueTuple("50%", 0.5), + new ValueTuple("100%", 1) + }; + + public ObservableCollection<(string, int)> TargetFrameRates { get; } = new() + { + new ValueTuple("10 FPS", 10), + new ValueTuple("20 FPS", 20), + new ValueTuple("30 FPS", 30), + new ValueTuple("45 FPS", 45), + new ValueTuple("60 FPS (lol)", 60), + new ValueTuple("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 UIAutoRun => _settingsService.GetSetting("UI.AutoRun", false); + public PluginSetting UIAutoRunDelay => _settingsService.GetSetting("UI.AutoRunDelay", 15); + public PluginSetting UIShowOnStartup => _settingsService.GetSetting("UI.ShowOnStartup", true); + public PluginSetting UICheckForUpdates => _settingsService.GetSetting("UI.CheckForUpdates", true); + public PluginSetting UIColorScheme => _settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic); + public PluginSetting ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false); + public PluginSetting CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information); + public PluginSetting CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan"); + public PluginSetting CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.25); + public PluginSetting CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30); + public PluginSetting WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696); + + #region General + + private void ExecuteShowLogs() + { + OpenFolder(Path.Combine(Constants.DataFolder, "Logs")); + } + + #endregion + + #region Updating + + private Task ExecuteCheckForUpdate(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + #endregion + + #region Tools + + private void ExecuteShowSetupWizard() + { + } + + private void ExecuteShowDebugger() + { + } + + 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 + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/Settings/Views/GeneralTabView.axaml b/src/Artemis.UI.Avalonia/Screens/Settings/Views/GeneralTabView.axaml index 1aef81e68..75e81d393 100644 --- a/src/Artemis.UI.Avalonia/Screens/Settings/Views/GeneralTabView.axaml +++ b/src/Artemis.UI.Avalonia/Screens/Settings/Views/GeneralTabView.axaml @@ -2,7 +2,355 @@ 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" + xmlns:system="clr-namespace:System;assembly=System.Runtime" + xmlns:controls="clr-namespace:Artemis.UI.Avalonia.Shared.Controls;assembly=Artemis.UI.Avalonia.Shared" + xmlns:layerBrushes="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core" + xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" + mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="2400" x:Class="Artemis.UI.Avalonia.Screens.Settings.Views.GeneralTabView"> - Welcome to Avalonia! - + + + + + General + + + + + + Auto-run on startup + + + + + + + + Hide window on auto-run + + + + + + + + + + Startup delay + + Set the amount of seconds to wait before auto-running Artemis. + + + If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value. + + + + + sec + + + + + + + Color scheme + + Pick between a light and dark color scheme, the automatic option copies your system settings. + + + + + + + + + + + + Log level + + + Sets the logging level, a higher logging level will result in more log files. + + + + + + + + + + + + Logs + + + Opens the directory where logs are stored. + + + +