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

UI - Moved color scheme code away from tray VM

UI - Cleaned up settings page code
Core - Changed default framerate to 30
This commit is contained in:
Robert 2021-09-18 20:40:57 +02:00
parent da123e2fe2
commit ef4e5b4c3b
14 changed files with 504 additions and 463 deletions

View File

@ -11,18 +11,14 @@ namespace Artemis.Core
/// Represents a setting tied to a plugin of type <typeparamref name="T" />
/// </summary>
/// <typeparam name="T">The value type of the setting</typeparam>
public class PluginSetting<T> : CorePropertyChanged
public class PluginSetting<T> : CorePropertyChanged, IPluginSetting
{
// TODO: Why? Should have included that...
// ReSharper disable once NotAccessedField.Local
private readonly Plugin _plugin;
private readonly IPluginRepository _pluginRepository;
private readonly PluginSettingEntity _pluginSettingEntity;
private T _value;
internal PluginSetting(Plugin plugin, IPluginRepository pluginRepository, PluginSettingEntity pluginSettingEntity)
internal PluginSetting(IPluginRepository pluginRepository, PluginSettingEntity pluginSettingEntity)
{
_plugin = plugin;
_pluginRepository = pluginRepository;
_pluginSettingEntity = pluginSettingEntity;
@ -37,9 +33,7 @@ namespace Artemis.Core
}
}
/// <summary>
/// The name of the setting, unique to this plugin
/// </summary>
/// <inheritdoc />
public string Name { get; }
/// <summary>
@ -63,28 +57,19 @@ namespace Artemis.Core
}
}
/// <summary>
/// Determines whether the setting has been changed
/// </summary>
/// <inheritdoc />
public bool HasChanged => CoreJson.SerializeObject(Value) != _pluginSettingEntity.Value;
/// <summary>
/// Gets or sets whether changes must automatically be saved
/// <para>Note: When set to <c>true</c> <see cref="HasChanged" /> is always <c>false</c></para>
/// </summary>
/// <inheritdoc />
public bool AutoSave { get; set; }
/// <summary>
/// Resets the setting to the last saved value
/// </summary>
/// <inheritdoc />
public void RejectChanges()
{
Value = CoreJson.DeserializeObject<T>(_pluginSettingEntity.Value);
}
/// <summary>
/// Saves the setting
/// </summary>
/// <inheritdoc />
public void Save()
{
if (!HasChanged)
@ -95,14 +80,10 @@ namespace Artemis.Core
OnSettingSaved();
}
/// <summary>
/// Occurs when the value of the setting has been changed
/// </summary>
/// <inheritdoc />
public event EventHandler? SettingChanged;
/// <summary>
/// Occurs when the value of the setting has been saved
/// </summary>
/// <inheritdoc />
public event EventHandler? SettingSaved;
/// <inheritdoc />
@ -127,4 +108,46 @@ namespace Artemis.Core
SettingSaved?.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
/// Represents a setting tied to a plugin
/// </summary>
public interface IPluginSetting
{
/// <summary>
/// The name of the setting, unique to this plugin
/// </summary>
string Name { get; }
/// <summary>
/// Determines whether the setting has been changed
/// </summary>
bool HasChanged { get; }
/// <summary>
/// Gets or sets whether changes must automatically be saved
/// <para>Note: When set to <c>true</c> <see cref="HasChanged" /> is always <c>false</c></para>
/// </summary>
bool AutoSave { get; set; }
/// <summary>
/// Resets the setting to the last saved value
/// </summary>
void RejectChanges();
/// <summary>
/// Saves the setting
/// </summary>
void Save();
/// <summary>
/// Occurs when the value of the setting has been changed
/// </summary>
event EventHandler? SettingChanged;
/// <summary>
/// Occurs when the value of the setting has been saved
/// </summary>
event EventHandler? SettingSaved;
}
}

View File

@ -1,7 +1,6 @@
using System.Collections.Generic;
using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json;
namespace Artemis.Core
{
@ -12,7 +11,7 @@ namespace Artemis.Core
public class PluginSettings
{
private readonly IPluginRepository _pluginRepository;
private readonly Dictionary<string, object> _settingEntities;
private readonly Dictionary<string, IPluginSetting> _settingEntities;
internal PluginSettings(Plugin plugin, IPluginRepository pluginRepository)
{
@ -20,7 +19,7 @@ namespace Artemis.Core
Plugin.Settings = this;
_pluginRepository = pluginRepository;
_settingEntities = new Dictionary<string, object>();
_settingEntities = new Dictionary<string, IPluginSetting>();
}
/// <summary>
@ -56,7 +55,7 @@ namespace Artemis.Core
_pluginRepository.AddSetting(settingEntity);
}
PluginSetting<T> pluginSetting = new(Plugin, _pluginRepository, settingEntity);
PluginSetting<T> pluginSetting = new(_pluginRepository, settingEntity);
// This overrides null with the default value, I'm not sure if that's desirable because you
// might expect something to go null and you might not
@ -68,6 +67,15 @@ namespace Artemis.Core
}
}
/// <summary>
/// Saves all currently loaded settings
/// </summary>
public void SaveAllSettings()
{
foreach (var (_, pluginSetting) in _settingEntities)
pluginSetting.Save();
}
internal void ClearSettings()
{
_settingEntities.Clear();

View File

@ -35,7 +35,7 @@ namespace Artemis.Core.Services
_logger = logger;
_pluginManagementService = pluginManagementService;
_deviceRepository = deviceRepository;
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 25);
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.25);
Surface = new RGBSurface();

View File

@ -16,6 +16,12 @@ namespace Artemis.Core.Services
{
return _pluginSettings.GetSetting(name, defaultValue);
}
/// <inheritdoc />
public void SaveAllSettings()
{
_pluginSettings.SaveAllSettings();
}
}
/// <summary>
@ -32,5 +38,10 @@ namespace Artemis.Core.Services
/// <param name="defaultValue">The default value to use if the setting does not exist yet</param>
/// <returns></returns>
PluginSetting<T> GetSetting<T>(string name, T? defaultValue = default);
/// <summary>
/// Saves all settings, obviously
/// </summary>
void SaveAllSettings();
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Artemis.UI.Converters
{
[ValueConversion(typeof(double), typeof(double))]
public class NormalizedPercentageConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double number)
return number * 100.0;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double number)
return number / 100.0;
return value;
}
#endregion
}
}

View File

@ -1,15 +1,16 @@
using System;
using Artemis.UI.Services;
using Artemis.UI.Utilities;
namespace Artemis.UI.Events
{
public class WindowsThemeEventArgs : EventArgs
{
public WindowsThemeEventArgs(ThemeWatcher.WindowsTheme theme)
public WindowsThemeEventArgs(IThemeService.WindowsTheme theme)
{
Theme = theme;
}
public ThemeWatcher.WindowsTheme Theme { get; set; }
public IThemeService.WindowsTheme Theme { get; set; }
}
}

View File

@ -4,8 +4,8 @@ using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Windows.UI.Notifications;
using Artemis.UI.Services;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities;
using MaterialDesignThemes.Wpf;
using Microsoft.Toolkit.Uwp.Notifications;
using Stylet;
@ -14,11 +14,11 @@ namespace Artemis.UI.Providers
{
public class ToastNotificationProvider : INotificationProvider
{
private ThemeWatcher _themeWatcher;
private readonly IThemeService _themeService;
public ToastNotificationProvider()
public ToastNotificationProvider(IThemeService themeService)
{
_themeWatcher = new ThemeWatcher();
_themeService = themeService;
}
public static PngBitmapEncoder GetEncoderForIcon(PackIconKind icon, Color color)
@ -71,7 +71,7 @@ namespace Artemis.UI.Providers
Execute.OnUIThreadSync(() =>
{
using FileStream stream = File.OpenWrite(imagePath);
GetEncoderForIcon(icon, _themeWatcher.GetSystemTheme() == ThemeWatcher.WindowsTheme.Dark ? Colors.White : Colors.Black).Save(stream);
GetEncoderForIcon(icon, _themeService.GetSystemTheme() == IThemeService.WindowsTheme.Dark ? Colors.White : Colors.Black).Save(stream);
});
new ToastContentBuilder()
@ -88,14 +88,10 @@ namespace Artemis.UI.Providers
#endregion
#region IDisposable
/// <inheritdoc />
public void Dispose()
{
ToastNotificationManagerCompat.Uninstall();
}
#endregion
}
}

View File

@ -512,7 +512,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
if (SelectedProfileElement == null)
return;
double frameTime = 1000.0 / SettingsService.GetSetting("Core.TargetFrameRate", 25).Value;
double frameTime = 1000.0 / SettingsService.GetSetting("Core.TargetFrameRate", 30).Value;
double newTime = Math.Max(0, Math.Round((ProfileEditorService.CurrentTime.TotalMilliseconds - frameTime) / frameTime) * frameTime);
ProfileEditorService.CurrentTime = TimeSpan.FromMilliseconds(newTime);
}
@ -522,7 +522,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
if (SelectedProfileElement == null)
return;
double frameTime = 1000.0 / SettingsService.GetSetting("Core.TargetFrameRate", 25).Value;
double frameTime = 1000.0 / SettingsService.GetSetting("Core.TargetFrameRate", 30).Value;
double newTime = Math.Round((ProfileEditorService.CurrentTime.TotalMilliseconds + frameTime) / frameTime) * frameTime;
newTime = Math.Min(newTime, SelectedProfileElement.Timeline.EndSegmentEndPosition.TotalMilliseconds);
ProfileEditorService.CurrentTime = TimeSpan.FromMilliseconds(newTime);

View File

@ -1,4 +1,4 @@
<UserControl x:Class="Artemis.UI.Screens.Settings.Tabs.General.GeneralSettingsTabView"
<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"
@ -8,20 +8,25 @@
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 local:GeneralSettingsTabViewModel}">
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">General</TextBlock>
<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>
@ -37,7 +42,7 @@
<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 StartWithWindows}" />
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding UIAutoRun.Value}" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
@ -55,7 +60,9 @@
<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 StartMinimized}" IsEnabled="{Binding StartWithWindows}" />
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}"
IsChecked="{Binding UIShowOnStartup.Value, Converter={StaticResource InverseBooleanConverter}}"
IsEnabled="{Binding UIAutoRun.Value}" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
@ -72,14 +79,14 @@
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Startup delay</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
Set the amount of seconds to wait before running Artemis with Windows. <LineBreak />
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
<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 AutoRunDelay}"
IsEnabled="{Binding StartWithWindows}"
Text="{Binding UIAutoRunDelay.Value}"
IsEnabled="{Binding UIAutoRun.Value}"
Width="100"
materialDesign:TextFieldAssist.SuffixText="sec"
materialDesign:HintAssist.IsFloating="false" />
@ -105,7 +112,7 @@
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
Width="100"
SelectedValue="{Binding SelectedColorScheme}"
SelectedValue="{Binding UIColorScheme.Value}"
ItemsSource="{Binding ColorSchemes}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
@ -124,7 +131,9 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Log level</TextBlock>
<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>
@ -132,7 +141,7 @@
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ComboBox Style="{StaticResource MaterialDesignFilledComboBox}"
Width="100"
SelectedValue="{Binding SelectedLogLevel}"
SelectedValue="{Binding CoreLoggingLevel.Value}"
ItemsSource="{Binding LogLevels}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
@ -151,22 +160,24 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Logs</TextBlock>
<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">
SHOW LOGS
</Button>
<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>
<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>
@ -179,14 +190,18 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Web server port</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
Web server port
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
Artemis runs a local web server that can be used to externally interact with the application. <LineBreak />
This web server can only be accessed by applications running on your own computer, e.g. supported games.
<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 WebServerPortSetting.Value}" Width="100"
<TextBox Style="{StaticResource MaterialDesignFilledTextBox}"
Text="{Binding WebServerPort.Value}"
Width="100"
materialDesign:HintAssist.IsFloating="false" />
</StackPanel>
</Grid>
@ -194,7 +209,9 @@
</materialDesign:Card>
<!-- Update settings -->
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Updating</TextBlock>
<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>
@ -207,13 +224,15 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Check for updates</TextBlock>
<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 CheckForUpdates}" />
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding UICheckForUpdates.Value}" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
@ -228,22 +247,24 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Update</TextBlock>
<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">
CHECK NOW
</Button>
<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">Profile editor</TextBlock>
<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>
@ -256,16 +277,17 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Show condition data model values</TextBlock>
<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
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 ShowDataModelValues}" />
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding ProfileEditorShowDataModelValues.Value}" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
@ -278,7 +300,9 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Default brush</TextBlock>
<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>
@ -290,18 +314,18 @@
materialDesign:ValidationAssist.UsePopup="True"
materialDesign:HintAssist.IsFloating="false"
HorizontalAlignment="Left"
ItemsSource="{Binding Path=LayerBrushDescriptors}"
SelectedValue="{Binding Path=SelectedLayerBrushDescriptor}"
ItemTemplateSelector="{dataTemplateSelectors:ComboBoxTemplateSelector
SelectedItemTemplate={StaticResource SimpleLayerBrushDescriptorTemplate},
DropdownItemsTemplate={StaticResource ExtendedLayerBrushDescriptorTemplate}}" />
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>
<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>
@ -314,15 +338,17 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Preferred render method</TextBlock>
<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
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 PreferredGraphicsContext}"
SelectedItem="{Binding CorePreferredGraphicsContext.Value}"
materialDesign:HintAssist.IsFloating="false">
<system:String>Software</system:String>
<system:String>Vulkan</system:String>
@ -341,7 +367,9 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Render scale</TextBlock>
<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>
@ -367,9 +395,12 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Target framerate</TextBlock>
<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.
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">
@ -386,7 +417,9 @@
<!-- Tools -->
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Tools</TextBlock>
<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>
@ -399,15 +432,15 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Setup wizard</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">
<Run Text="Setup wizard" />
</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
Opens the startup wizard usually shown when Artemis first starts.
<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">
SHOW WIZARD
</Button>
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowSetupWizard}" Width="150" Content="SHOW WIZARD" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
@ -422,15 +455,15 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Debugger</TextBlock>
<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">
SHOW DEBUGGER
</Button>
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowDebugger}" Width="150" Content="SHOW DEBUGGER" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
@ -445,15 +478,15 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Application files</TextBlock>
<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">
SHOW APP FILES
</Button>
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action ShowDataFolder}" Width="150" Content="SHOW APP FILES" />
</StackPanel>
</Grid>
</StackPanel>

View File

@ -30,8 +30,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
private readonly IUpdateService _updateService;
private readonly IWindowManager _windowManager;
private bool _canOfferUpdatesIfFound = true;
private List<Tuple<string, double>> _renderScales;
private List<Tuple<string, int>> _targetFrameRates;
public GeneralSettingsTabViewModel(
IKernel kernel,
@ -41,9 +39,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
ISettingsService settingsService,
IUpdateService updateService,
IPluginManagementService pluginManagementService,
IMessageService messageService,
IRegistrationService registrationService,
ICoreService coreService
IMessageService messageService
)
{
DisplayName = "GENERAL";
@ -54,38 +51,34 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
_debugService = debugService;
_settingsService = settingsService;
_updateService = updateService;
_messageService = messageService;
_registrationService = registrationService;
_messageService = messageService;
LogLevels = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel)));
ColorSchemes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(ApplicationColorScheme)));
RenderScales = new List<Tuple<string, double>>
RenderScales = new BindableCollection<Tuple<string, double>>
{
new("25%", 0.25),
new("50%", 0.5),
new("100%", 1),
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)
};
TargetFrameRates = new List<Tuple<string, int>>();
for (int i = 10; i <= 30; i += 5)
TargetFrameRates.Add(new Tuple<string, int>(i + " FPS", i));
if (coreService.StartupArguments.Contains("--pcmr"))
{
TargetFrameRates.Add(new Tuple<string, int>("60 FPS (lol)", 60));
TargetFrameRates.Add(new Tuple<string, int>("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"
});
WebServerPortSetting = _settingsService.GetSetting("WebServer.Port", 9696);
WebServerPortSetting.AutoSave = true;
}
public BindableCollection<LayerBrushDescriptor> LayerBrushDescriptors { get; }
@ -93,148 +86,87 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
public LayerBrushDescriptor SelectedLayerBrushDescriptor
{
get => LayerBrushDescriptors.FirstOrDefault(d => d.MatchesLayerBrushReference(_defaultLayerBrushDescriptor.Value));
set
{
_defaultLayerBrushDescriptor.Value = new LayerBrushReference(value);
_defaultLayerBrushDescriptor.Save();
}
set => _defaultLayerBrushDescriptor.Value = new LayerBrushReference(value);
}
public BindableCollection<ValueDescription> LogLevels { get; }
public BindableCollection<ValueDescription> ColorSchemes { get; }
public List<Tuple<string, int>> TargetFrameRates
{
get => _targetFrameRates;
set => SetAndNotify(ref _targetFrameRates, value);
}
public List<Tuple<string, double>> RenderScales
{
get => _renderScales;
set => SetAndNotify(ref _renderScales, value);
}
public bool StartWithWindows
{
get => _settingsService.GetSetting("UI.AutoRun", false).Value;
set
{
_settingsService.GetSetting("UI.AutoRun", false).Value = value;
_settingsService.GetSetting("UI.AutoRun", false).Save();
NotifyOfPropertyChange(nameof(StartWithWindows));
Task.Run(() => ApplyAutorun(false));
}
}
public int AutoRunDelay
{
get => _settingsService.GetSetting("UI.AutoRunDelay", 15).Value;
set
{
_settingsService.GetSetting("UI.AutoRunDelay", 15).Value = value;
_settingsService.GetSetting("UI.AutoRunDelay", 15).Save();
NotifyOfPropertyChange(nameof(AutoRunDelay));
Task.Run(() => ApplyAutorun(true));
}
}
public bool StartMinimized
{
get => !_settingsService.GetSetting("UI.ShowOnStartup", true).Value;
set
{
_settingsService.GetSetting("UI.ShowOnStartup", true).Value = !value;
_settingsService.GetSetting("UI.ShowOnStartup", true).Save();
NotifyOfPropertyChange(nameof(StartMinimized));
}
}
public bool CheckForUpdates
{
get => _settingsService.GetSetting("UI.CheckForUpdates", true).Value;
set
{
_settingsService.GetSetting("UI.CheckForUpdates", true).Value = value;
_settingsService.GetSetting("UI.CheckForUpdates", true).Save();
NotifyOfPropertyChange(nameof(CheckForUpdates));
}
}
public bool ShowDataModelValues
{
get => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false).Value;
set
{
_settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false).Value = value;
_settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false).Save();
}
}
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 - RenderScale) < 0.01);
set => RenderScale = value.Item2;
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(t => Math.Abs(t.Item2 - TargetFrameRate) < 0.01);
set => TargetFrameRate = value.Item2;
get => TargetFrameRates.FirstOrDefault(s => s.Item2 == CoreTargetFrameRate.Value);
set => CoreTargetFrameRate.Value = value.Item2;
}
public LogEventLevel SelectedLogLevel
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)
{
get => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information).Value;
set
{
_settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information).Value = value;
_settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information).Save();
}
Task.Run(() => ApplyAutorun(false));
}
public ApplicationColorScheme SelectedColorScheme
private void UIAutoRunDelayOnSettingChanged(object sender, EventArgs e)
{
get => _settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic).Value;
set
{
_settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic).Value = value;
_settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic).Save();
}
Task.Run(() => ApplyAutorun(true));
}
public string PreferredGraphicsContext
private void CorePreferredGraphicsContextOnSettingChanged(object sender, EventArgs e)
{
get => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Value;
set
{
_settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Value = value;
_settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Save();
_registrationService.ApplyPreferredGraphicsContext();
}
}
public double RenderScale
private void ApplyAutorun(bool recreate)
{
get => _settingsService.GetSetting("Core.RenderScale", 0.25).Value;
set
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
{
_settingsService.GetSetting("Core.RenderScale", 0.25).Value = value;
_settingsService.GetSetting("Core.RenderScale", 0.25).Save();
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;
}
}
public int TargetFrameRate
{
get => _settingsService.GetSetting("Core.TargetFrameRate", 25).Value;
set
{
_settingsService.GetSetting("Core.TargetFrameRate", 25).Value = value;
_settingsService.GetSetting("Core.TargetFrameRate", 25).Save();
}
}
public PluginSetting<int> WebServerPortSetting { get; }
#region View methods
public bool CanOfferUpdatesIfFound
{
@ -298,44 +230,32 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
}
}
#endregion
#region Overrides of Screen
protected override void OnInitialActivate()
{
Task.Run(() => ApplyAutorun(false));
UIAutoRun.SettingChanged += UIAutoRunOnSettingChanged;
UIAutoRunDelay.SettingChanged += UIAutoRunDelayOnSettingChanged;
CorePreferredGraphicsContext.SettingChanged += CorePreferredGraphicsContextOnSettingChanged;
base.OnInitialActivate();
}
private void ApplyAutorun(bool recreate)
protected override void OnClose()
{
if (!StartWithWindows)
StartMinimized = false;
UIAutoRun.SettingChanged -= UIAutoRunOnSettingChanged;
UIAutoRunDelay.SettingChanged -= UIAutoRunDelayOnSettingChanged;
CorePreferredGraphicsContext.SettingChanged -= CorePreferredGraphicsContextOnSettingChanged;
// 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 (StartWithWindows && !taskCreated)
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(AutoRunDelay));
else if (!StartWithWindows && taskCreated)
SettingsUtilities.RemoveAutoRunTask();
}
catch (Exception e)
{
Execute.PostToUIThread(() => _dialogService.ShowExceptionDialog("An exception occured while trying to apply the auto run setting", e));
throw;
}
}
_settingsService.SaveAllSettings();
base.OnClose();
}
#endregion
}
public enum ApplicationColorScheme
{

View File

@ -4,16 +4,12 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Screens.Settings.Tabs.General;
using Artemis.UI.Screens.Splash;
using Artemis.UI.Services;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities;
using Hardcodet.Wpf.TaskbarNotification;
using MaterialDesignThemes.Wpf;
using Ninject;
using Stylet;
@ -21,11 +17,10 @@ namespace Artemis.UI.Screens
{
public class TrayViewModel : Screen, IMainWindowProvider
{
private readonly PluginSetting<ApplicationColorScheme> _colorScheme;
private readonly IDebugService _debugService;
private readonly IEventAggregator _eventAggregator;
private readonly IKernel _kernel;
private readonly ThemeWatcher _themeWatcher;
private readonly IThemeService _themeService;
private readonly IWindowManager _windowManager;
private ImageSource _icon;
private bool _openingMainWindow;
@ -40,24 +35,20 @@ namespace Artemis.UI.Screens
IEventAggregator eventAggregator,
ICoreService coreService,
IDebugService debugService,
ISettingsService settingsService)
ISettingsService settingsService,
IThemeService themeService)
{
_kernel = kernel;
_windowManager = windowManager;
_eventAggregator = eventAggregator;
_debugService = debugService;
_themeService = themeService;
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnShutdownRequested;
_themeWatcher = new ThemeWatcher();
_colorScheme = settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic);
_colorScheme.SettingChanged += ColorSchemeOnSettingChanged;
_themeWatcher.SystemThemeChanged += _themeWatcher_SystemThemeChanged;
_themeWatcher.AppsThemeChanged += _themeWatcher_AppsThemeChanged;
ApplyColorSchemeSetting();
ApplyTrayIconTheme(_themeWatcher.GetSystemTheme());
_themeService.SystemThemeChanged += ThemeServiceOnSystemThemeChanged;
ApplyTrayIconTheme(_themeService.GetSystemTheme());
windowService.ConfigureMainWindowProvider(this);
bool autoRunning = Bootstrapper.StartupArguments.Contains("--autorun");
@ -191,60 +182,21 @@ namespace Artemis.UI.Screens
#region Theme
private void ApplyColorSchemeSetting()
{
if (_colorScheme.Value == ApplicationColorScheme.Automatic)
ApplyUITheme(_themeWatcher.GetAppsTheme());
else
ChangeMaterialColors(_colorScheme.Value);
}
private void ApplyUITheme(ThemeWatcher.WindowsTheme theme)
{
if (_colorScheme.Value != ApplicationColorScheme.Automatic)
return;
if (theme == ThemeWatcher.WindowsTheme.Dark)
ChangeMaterialColors(ApplicationColorScheme.Dark);
else
ChangeMaterialColors(ApplicationColorScheme.Light);
}
private void ApplyTrayIconTheme(ThemeWatcher.WindowsTheme theme)
private void ApplyTrayIconTheme(IThemeService.WindowsTheme theme)
{
Execute.PostToUIThread(() =>
{
Icon = theme == ThemeWatcher.WindowsTheme.Dark
Icon = theme == IThemeService.WindowsTheme.Dark
? new BitmapImage(new Uri("pack://application:,,,/Artemis.UI;component/Resources/Images/Logo/bow-white.ico"))
: new BitmapImage(new Uri("pack://application:,,,/Artemis.UI;component/Resources/Images/Logo/bow-black.ico"));
});
}
private void ChangeMaterialColors(ApplicationColorScheme colorScheme)
{
PaletteHelper paletteHelper = new();
ITheme theme = paletteHelper.GetTheme();
theme.SetBaseTheme(colorScheme == ApplicationColorScheme.Dark ? Theme.Dark : Theme.Light);
paletteHelper.SetTheme(theme);
MaterialDesignExtensions.Themes.PaletteHelper extensionsPaletteHelper = new();
extensionsPaletteHelper.SetLightDark(colorScheme == ApplicationColorScheme.Dark);
}
private void _themeWatcher_AppsThemeChanged(object sender, WindowsThemeEventArgs e)
{
ApplyUITheme(e.Theme);
}
private void _themeWatcher_SystemThemeChanged(object sender, WindowsThemeEventArgs e)
private void ThemeServiceOnSystemThemeChanged(object sender, WindowsThemeEventArgs e)
{
ApplyTrayIconTheme(e.Theme);
}
private void ColorSchemeOnSettingChanged(object sender, EventArgs e)
{
ApplyColorSchemeSetting();
}
#endregion
#region Implementation of IMainWindowProvider

View File

@ -26,6 +26,7 @@ namespace Artemis.UI.Services
private readonly IWebServerService _webServerService;
private readonly IRgbService _rgbService;
private readonly ISettingsService _settingsService;
private readonly IThemeService _themeService;
private bool _registeredBuiltInDataModelDisplays;
private bool _registeredBuiltInDataModelInputs;
private bool _registeredBuiltInPropertyEditors;
@ -40,7 +41,8 @@ namespace Artemis.UI.Services
IMessageService messageService,
IWebServerService webServerService,
IRgbService rgbService,
ISettingsService settingsService)
ISettingsService settingsService,
IThemeService themeService)
{
_logger = logger;
_coreService = coreService;
@ -52,6 +54,7 @@ namespace Artemis.UI.Services
_webServerService = webServerService;
_rgbService = rgbService;
_settingsService = settingsService;
_themeService = themeService;
LoadPluginModules();
pluginManagementService.PluginEnabling += PluginServiceOnPluginEnabling;
@ -105,7 +108,7 @@ namespace Artemis.UI.Services
public void RegisterProviders()
{
_inputService.AddInputProvider(new NativeWindowInputProvider(_logger, _inputService));
_messageService.SetNotificationProvider(new ToastNotificationProvider());
_messageService.SetNotificationProvider(new ToastNotificationProvider(_themeService));
}
public void RegisterControllers()

View File

@ -0,0 +1,176 @@
using System;
using System.Globalization;
using System.Management;
using System.Security.Principal;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Screens.Settings.Tabs.General;
using MaterialDesignThemes.Wpf;
using Microsoft.Win32;
namespace Artemis.UI.Services
{
public class ThemeService : IThemeService
{
private readonly PluginSetting<ApplicationColorScheme> _colorScheme;
private const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string AppsThemeRegistryValueName = "AppsUseLightTheme";
private const string SystemThemeRegistryValueName = "SystemUsesLightTheme";
public ThemeService(ISettingsService settingsService)
{
WatchTheme();
_colorScheme = settingsService.GetSetting("UI.ColorScheme", ApplicationColorScheme.Automatic);
_colorScheme.SettingChanged += ColorSchemeOnSettingChanged;
ApplyColorSchemeSetting();
AppsThemeChanged += OnAppsThemeChanged;
}
public IThemeService.WindowsTheme GetAppsTheme()
{
return GetTheme(AppsThemeRegistryValueName);
}
public IThemeService.WindowsTheme GetSystemTheme()
{
return GetTheme(SystemThemeRegistryValueName);
}
private void ApplyColorSchemeSetting()
{
if (_colorScheme.Value == ApplicationColorScheme.Automatic)
ApplyUITheme(GetAppsTheme());
else
ChangeMaterialColors(_colorScheme.Value);
}
private void ChangeMaterialColors(ApplicationColorScheme colorScheme)
{
PaletteHelper paletteHelper = new();
ITheme theme = paletteHelper.GetTheme();
theme.SetBaseTheme(colorScheme == ApplicationColorScheme.Dark ? Theme.Dark : Theme.Light);
paletteHelper.SetTheme(theme);
MaterialDesignExtensions.Themes.PaletteHelper extensionsPaletteHelper = new();
extensionsPaletteHelper.SetLightDark(colorScheme == ApplicationColorScheme.Dark);
}
private void ApplyUITheme(IThemeService.WindowsTheme theme)
{
if (_colorScheme.Value != ApplicationColorScheme.Automatic)
return;
if (theme == IThemeService.WindowsTheme.Dark)
ChangeMaterialColors(ApplicationColorScheme.Dark);
else
ChangeMaterialColors(ApplicationColorScheme.Light);
}
private void WatchTheme()
{
WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
string appsThemequery = string.Format(
CultureInfo.InvariantCulture,
@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'",
currentUser.User.Value,
RegistryKeyPath.Replace(@"\", @"\\"),
AppsThemeRegistryValueName);
string systemThemequery = string.Format(
CultureInfo.InvariantCulture,
@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'",
currentUser.User.Value,
RegistryKeyPath.Replace(@"\", @"\\"),
SystemThemeRegistryValueName);
try
{
// For Apps theme
ManagementEventWatcher appsThemWatcher = new(appsThemequery);
appsThemWatcher.EventArrived += (_, _) =>
{
IThemeService.WindowsTheme newWindowsTheme = GetAppsTheme();
OnAppsThemeChanged(new WindowsThemeEventArgs(newWindowsTheme));
};
// Start listening for apps theme events
appsThemWatcher.Start();
// For System theme
ManagementEventWatcher systemThemWatcher = new(systemThemequery);
systemThemWatcher.EventArrived += (_, _) =>
{
IThemeService.WindowsTheme newWindowsTheme = GetSystemTheme();
OnSystemThemeChanged(new WindowsThemeEventArgs(newWindowsTheme));
};
// Start listening for system theme events
systemThemWatcher.Start();
}
catch (Exception)
{
// This can fail on Windows 7
}
}
private IThemeService.WindowsTheme GetTheme(string themeKeyName)
{
using RegistryKey key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath);
object registryValueObject = key?.GetValue(themeKeyName);
if (registryValueObject == null) return IThemeService.WindowsTheme.Light;
int registryValue = (int) registryValueObject;
return registryValue > 0 ? IThemeService.WindowsTheme.Light : IThemeService.WindowsTheme.Dark;
}
#region Events
public event EventHandler<WindowsThemeEventArgs> AppsThemeChanged;
public event EventHandler<WindowsThemeEventArgs> SystemThemeChanged;
protected virtual void OnAppsThemeChanged(WindowsThemeEventArgs e)
{
AppsThemeChanged?.Invoke(this, e);
}
protected virtual void OnSystemThemeChanged(WindowsThemeEventArgs e)
{
SystemThemeChanged?.Invoke(this, e);
}
#endregion
#region Event handlers
private void ColorSchemeOnSettingChanged(object sender, EventArgs e)
{
ApplyColorSchemeSetting();
}
private void OnAppsThemeChanged(object sender, WindowsThemeEventArgs e)
{
ApplyUITheme(e.Theme);
}
#endregion
}
public interface IThemeService : IArtemisUIService
{
WindowsTheme GetAppsTheme();
WindowsTheme GetSystemTheme();
event EventHandler<WindowsThemeEventArgs> AppsThemeChanged;
event EventHandler<WindowsThemeEventArgs> SystemThemeChanged;
enum WindowsTheme
{
Light,
Dark
}
}
}

View File

@ -1,112 +0,0 @@
using System;
using System.Globalization;
using System.Management;
using System.Security.Principal;
using Artemis.UI.Events;
using Microsoft.Win32;
namespace Artemis.UI.Utilities
{
public class ThemeWatcher
{
private const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string appsThemeRegistryValueName = "AppsUseLightTheme";
private const string systemThemeRegistryValueName = "SystemUsesLightTheme";
public ThemeWatcher()
{
WatchTheme();
}
public void WatchTheme()
{
WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
string appsThemequery = string.Format(
CultureInfo.InvariantCulture,
@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'",
currentUser.User.Value,
RegistryKeyPath.Replace(@"\", @"\\"),
appsThemeRegistryValueName);
string systemThemequery = string.Format(
CultureInfo.InvariantCulture,
@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'",
currentUser.User.Value,
RegistryKeyPath.Replace(@"\", @"\\"),
systemThemeRegistryValueName);
try
{
// For Apps theme
ManagementEventWatcher appsThemWatcher = new(appsThemequery);
appsThemWatcher.EventArrived += (_, _) =>
{
WindowsTheme newWindowsTheme = GetAppsTheme();
OnAppsThemeChanged(new WindowsThemeEventArgs(newWindowsTheme));
};
// Start listening for apps theme events
appsThemWatcher.Start();
// For System theme
ManagementEventWatcher systemThemWatcher = new(systemThemequery);
systemThemWatcher.EventArrived += (_, _) =>
{
WindowsTheme newWindowsTheme = GetSystemTheme();
OnSystemThemeChanged(new WindowsThemeEventArgs(newWindowsTheme));
};
// Start listening for system theme events
systemThemWatcher.Start();
}
catch (Exception)
{
// This can fail on Windows 7
}
}
private WindowsTheme GetTheme(string themeKeyName)
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath))
{
object registryValueObject = key?.GetValue(themeKeyName);
if (registryValueObject == null) return WindowsTheme.Light;
int registryValue = (int)registryValueObject;
return registryValue > 0 ? WindowsTheme.Light : WindowsTheme.Dark;
}
}
public WindowsTheme GetAppsTheme()
{
return GetTheme(appsThemeRegistryValueName);
}
public WindowsTheme GetSystemTheme()
{
return GetTheme(systemThemeRegistryValueName);
}
public event EventHandler<WindowsThemeEventArgs> AppsThemeChanged;
public event EventHandler<WindowsThemeEventArgs> SystemThemeChanged;
protected virtual void OnAppsThemeChanged(WindowsThemeEventArgs e)
{
AppsThemeChanged?.Invoke(this, e);
}
protected virtual void OnSystemThemeChanged(WindowsThemeEventArgs e)
{
SystemThemeChanged?.Invoke(this, e);
}
public enum WindowsTheme
{
Light,
Dark
}
}
}