mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-17 18:01:26 +08:00
169
src/MaaWpfGui/Configuration/ConfigFactory.cs
Normal file
169
src/MaaWpfGui/Configuration/ConfigFactory.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using MaaWpfGui.Helper;
|
||||
using ObservableCollections;
|
||||
using Serilog;
|
||||
|
||||
[assembly: PropertyChanged.FilterType("MaaWpfGui.Configuration.")]
|
||||
|
||||
namespace MaaWpfGui.Configuration
|
||||
{
|
||||
public class ConfigFactory
|
||||
{
|
||||
// TODO: delete test in main branch
|
||||
private static readonly string _configurationFile = Path.Combine(Environment.CurrentDirectory, "config/gui.test.json");
|
||||
|
||||
// TODO: write backup method
|
||||
private static readonly string _configurationBakFile = Path.Combine(Environment.CurrentDirectory, "config/gui.test.json.bak");
|
||||
|
||||
private static readonly ILogger _logger = Log.ForContext<ConfigurationHelper>();
|
||||
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
public delegate void ConfigurationUpdateEventHandler(string key, object oldValue, object newValue);
|
||||
|
||||
public static event ConfigurationUpdateEventHandler ConfigurationUpdateEvent;
|
||||
|
||||
private static readonly JsonSerializerOptions _options = new JsonSerializerOptions {WriteIndented = true, Converters = {new JsonStringEnumConverter()}};
|
||||
|
||||
private static readonly Lazy<RootConfig> _rootConfig = new Lazy<RootConfig>(() =>
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (Directory.Exists("config") is false)
|
||||
{
|
||||
Directory.CreateDirectory("config");
|
||||
}
|
||||
|
||||
RootConfig parsed = null;
|
||||
if (File.Exists(_configurationFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
parsed = JsonSerializer.Deserialize<RootConfig>(File.ReadAllText(_configurationFile), _options);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Information("Failed to parse configuration file: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed is null)
|
||||
{
|
||||
_logger.Information("Failed to load configuration file, creating a new one");
|
||||
parsed = new RootConfig();
|
||||
}
|
||||
|
||||
if (parsed.CurrentConfig is null)
|
||||
{
|
||||
parsed.CurrentConfig = new SpecificConfig();
|
||||
}
|
||||
|
||||
parsed.PropertyChanged += OnPropertyChangedFactory("Configurations.");
|
||||
parsed.Configurations.CollectionChanged += (in NotifyCollectionChangedEventArgs<KeyValuePair<string, SpecificConfig>> args) =>
|
||||
{
|
||||
switch (args.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
if (args.IsSingleItem)
|
||||
{
|
||||
args.NewItem.Value.GuiConfig.PropertyChanged += OnPropertyChangedFactory("Configurations." + args.NewItem.Key, JsonSerializer.Serialize(args.NewItem.Value, _options), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var value in args.NewItems)
|
||||
{
|
||||
value.Value.GuiConfig.PropertyChanged += OnPropertyChangedFactory("Configurations." + value.Key, JsonSerializer.Serialize(value.Value, _options), null);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
OnPropertyChangedFactory("Configurations");
|
||||
};
|
||||
|
||||
foreach (var keyValue in parsed.Configurations)
|
||||
{
|
||||
keyValue.Value.GuiConfig.PropertyChanged += OnPropertyChangedFactory("Configurations." + keyValue.Key + ".GUIConfig.");
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
});
|
||||
|
||||
private static PropertyChangedEventHandler OnPropertyChangedFactory(string key, object oldValue, object newValue)
|
||||
{
|
||||
return (o, args) =>
|
||||
{
|
||||
var after = newValue;
|
||||
if (after == null && args is PropertyChangedEventDetailArgs)
|
||||
{
|
||||
after = (args as PropertyChangedEventDetailArgs).NewValue;
|
||||
}
|
||||
|
||||
OnPropertyChanged(key + args.PropertyName, oldValue, after);
|
||||
};
|
||||
}
|
||||
|
||||
private static PropertyChangedEventHandler OnPropertyChangedFactory(string key)
|
||||
{
|
||||
return (o, args) =>
|
||||
{
|
||||
object after = null;
|
||||
if (args is PropertyChangedEventDetailArgs)
|
||||
{
|
||||
after = (args as PropertyChangedEventDetailArgs).NewValue;
|
||||
}
|
||||
|
||||
OnPropertyChanged(key + args.PropertyName, null, after);
|
||||
};
|
||||
}
|
||||
|
||||
public static RootConfig RootConfig => _rootConfig.Value;
|
||||
|
||||
public static readonly SpecificConfig CurrentConfig = RootConfig.CurrentConfig;
|
||||
|
||||
public static async void OnPropertyChanged(string key, object oldValue, object newValue)
|
||||
{
|
||||
var result = await Save();
|
||||
if (result)
|
||||
{
|
||||
ConfigurationUpdateEvent?.Invoke(key, oldValue, newValue);
|
||||
_logger.Debug("Configuration {Key} has been set to {Value}", key, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warning("Failed to save configuration {Key} to {Value}", key, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> Save(string file = null)
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(file ?? _configurationFile, JsonSerializer.Serialize(RootConfig, _options));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Failed to save configuration file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/MaaWpfGui/Configuration/GUIConfig.cs
Normal file
41
src/MaaWpfGui/Configuration/GUIConfig.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MaaWpfGui.Configuration
|
||||
{
|
||||
public class GUIConfig : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public DarkModeType DarkMode { get; set; } = DarkModeType.SyncWithOS;
|
||||
|
||||
public bool UseNotify { get; set; } = true;
|
||||
|
||||
public void OnPropertyChanged(string propertyName, object before, object after)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventDetailArgs(propertyName, before, after));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示深色模式的类型。
|
||||
/// </summary>
|
||||
public enum DarkModeType
|
||||
{
|
||||
/// <summary>
|
||||
/// 明亮的主题。
|
||||
/// </summary>
|
||||
Light,
|
||||
|
||||
/// <summary>
|
||||
/// 暗黑的主题。
|
||||
/// </summary>
|
||||
Dark,
|
||||
|
||||
/// <summary>
|
||||
/// 与操作系统的深色模式同步。
|
||||
/// </summary>
|
||||
SyncWithOS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace MaaWpfGui.Configuration
|
||||
{
|
||||
// https://github.com/dotnet/runtime/issues/27252
|
||||
public class PropertyChangedEventDetailArgs : PropertyChangedEventArgs
|
||||
{
|
||||
public PropertyChangedEventDetailArgs(string propertyName, object oldValue, object newValue)
|
||||
: base(propertyName)
|
||||
{
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
}
|
||||
|
||||
public object OldValue { get; private set; }
|
||||
|
||||
public object NewValue { get; private set; }
|
||||
}
|
||||
}
|
||||
35
src/MaaWpfGui/Configuration/RootConfig.cs
Normal file
35
src/MaaWpfGui/Configuration/RootConfig.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json.Serialization;
|
||||
using ObservableCollections;
|
||||
|
||||
namespace MaaWpfGui.Configuration
|
||||
{
|
||||
public class RootConfig : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public ObservableDictionary<string, SpecificConfig> Configurations { get; set; } = new ObservableDictionary<string, SpecificConfig>();
|
||||
|
||||
public string Current { get; set; } = "Default";
|
||||
|
||||
[JsonIgnore]
|
||||
public SpecificConfig CurrentConfig
|
||||
{
|
||||
get
|
||||
{
|
||||
SpecificConfig result = null;
|
||||
Configurations.TryGetValue(Current, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
set => Configurations[Current] = value;
|
||||
}
|
||||
|
||||
public void OnPropertyChanged(string propertyName, object before, object after)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventDetailArgs(propertyName, before, after));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/MaaWpfGui/Configuration/SpecificConfig.cs
Normal file
11
src/MaaWpfGui/Configuration/SpecificConfig.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MaaWpfGui.Configuration
|
||||
{
|
||||
public class SpecificConfig
|
||||
{
|
||||
public GUIConfig GuiConfig { get; set; } = new GUIConfig();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ namespace MaaWpfGui.Constants
|
||||
public const string Localization = "GUI.Localization";
|
||||
public const string MinimizeToTray = "GUI.MinimizeToTray";
|
||||
public const string HideCloseButton = "GUI.HideCloseButton";
|
||||
public const string UseNotify = "GUI.UseNotify";
|
||||
public const string UseLogItemDateFormat = "GUI.UseLogItemDateFormat";
|
||||
public const string LogItemDateFormat = "GUI.LogItemDateFormatString";
|
||||
public const string WindowPlacement = "GUI.Placement";
|
||||
@@ -39,7 +38,6 @@ namespace MaaWpfGui.Constants
|
||||
public const string CustomStageCode = "GUI.CustomStageCode";
|
||||
public const string InverseClearMode = "GUI.InverseClearMode";
|
||||
public const string WindowTitlePrefix = "GUI.WindowTitlePrefix";
|
||||
public const string DarkMode = "GUI.DarkMode";
|
||||
|
||||
public const string AddressHistory = "Connect.AddressHistory";
|
||||
public const string AutoDetect = "Connect.AutoDetect";
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Costura />
|
||||
</Weavers>
|
||||
<PropertyChanged />
|
||||
</Weavers>
|
||||
|
||||
@@ -22,6 +22,7 @@ using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using MaaWpfGui.Configuration;
|
||||
using MaaWpfGui.Constants;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Microsoft.Win32;
|
||||
@@ -394,7 +395,7 @@ namespace MaaWpfGui.Helper
|
||||
NotificationSounds sound = NotificationSounds.Notification,
|
||||
NotificationContent notificationContent = null)
|
||||
{
|
||||
if (!Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.UseNotify, bool.TrueString)))
|
||||
if (!ConfigFactory.CurrentConfig.GuiConfig.UseNotify)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -108,6 +108,12 @@
|
||||
<PackageReference Include="Notification.Wpf">
|
||||
<Version>6.1.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ObservableCollections">
|
||||
<Version>1.1.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PropertyChanged.Fody">
|
||||
<Version>4.1.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Semver">
|
||||
<Version>2.2.0</Version>
|
||||
</PackageReference>
|
||||
|
||||
@@ -29,6 +29,7 @@ using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using HandyControl.Controls;
|
||||
using HandyControl.Data;
|
||||
using MaaWpfGui.Configuration;
|
||||
using MaaWpfGui.Constants;
|
||||
using MaaWpfGui.Extensions;
|
||||
using MaaWpfGui.Helper;
|
||||
@@ -3130,18 +3131,16 @@ namespace MaaWpfGui.ViewModels.UI
|
||||
}
|
||||
}
|
||||
|
||||
private bool _useNotify = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.UseNotify, bool.TrueString));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use notification.
|
||||
/// </summary>
|
||||
public bool UseNotify
|
||||
{
|
||||
get => _useNotify;
|
||||
get => ConfigFactory.CurrentConfig.GuiConfig.UseNotify;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _useNotify, value);
|
||||
ConfigurationHelper.SetValue(ConfigurationKeys.UseNotify, value.ToString());
|
||||
ConfigFactory.CurrentConfig.GuiConfig.UseNotify = value;
|
||||
NotifyOfPropertyChange();
|
||||
if (value)
|
||||
{
|
||||
Execute.OnUIThread(() =>
|
||||
@@ -3271,39 +3270,12 @@ namespace MaaWpfGui.ViewModels.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示深色模式的类型。
|
||||
/// </summary>
|
||||
public enum DarkModeType
|
||||
{
|
||||
/// <summary>
|
||||
/// 明亮的主题。
|
||||
/// </summary>
|
||||
Light,
|
||||
|
||||
/// <summary>
|
||||
/// 暗黑的主题。
|
||||
/// </summary>
|
||||
Dark,
|
||||
|
||||
/// <summary>
|
||||
/// 与操作系统的深色模式同步。
|
||||
/// </summary>
|
||||
SyncWithOS,
|
||||
}
|
||||
|
||||
private DarkModeType _darkModeType =
|
||||
Enum.TryParse(ConfigurationHelper.GetValue(ConfigurationKeys.DarkMode, DarkModeType.Light.ToString()),
|
||||
out DarkModeType temp)
|
||||
? temp
|
||||
: DarkModeType.Light;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dark mode.
|
||||
/// </summary>
|
||||
public string DarkMode
|
||||
{
|
||||
get => _darkModeType.ToString();
|
||||
get => ConfigFactory.CurrentConfig.GuiConfig.DarkMode.ToString();
|
||||
set
|
||||
{
|
||||
if (!Enum.TryParse(value, out DarkModeType tempEnumValue))
|
||||
@@ -3311,8 +3283,8 @@ namespace MaaWpfGui.ViewModels.UI
|
||||
return;
|
||||
}
|
||||
|
||||
SetAndNotify(ref _darkModeType, tempEnumValue);
|
||||
ConfigurationHelper.SetValue(ConfigurationKeys.DarkMode, value);
|
||||
ConfigFactory.CurrentConfig.GuiConfig.DarkMode = tempEnumValue;
|
||||
NotifyOfPropertyChange();
|
||||
SwitchDarkMode();
|
||||
|
||||
/*
|
||||
@@ -3331,10 +3303,7 @@ namespace MaaWpfGui.ViewModels.UI
|
||||
|
||||
public void SwitchDarkMode()
|
||||
{
|
||||
DarkModeType darkModeType =
|
||||
Enum.TryParse(ConfigurationHelper.GetValue(ConfigurationKeys.DarkMode, DarkModeType.Light.ToString()),
|
||||
out DarkModeType temp)
|
||||
? temp : DarkModeType.Light;
|
||||
DarkModeType darkModeType = ConfigFactory.CurrentConfig.GuiConfig.DarkMode;
|
||||
switch (darkModeType)
|
||||
{
|
||||
case DarkModeType.Light:
|
||||
|
||||
Reference in New Issue
Block a user