144 lines
4.5 KiB
C#
144 lines
4.5 KiB
C#
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Windows.Media;
|
|
using System.Windows.Threading;
|
|
using CursorLang.Models;
|
|
|
|
namespace CursorLang.Services;
|
|
|
|
/// <summary>
|
|
/// Хранит настройки в %APPDATA%\CursorLang\settings.json.
|
|
/// </summary>
|
|
public sealed class SettingsService : IDisposable
|
|
{
|
|
private static readonly JsonSerializerOptions SerializerOptions = new()
|
|
{
|
|
WriteIndented = true,
|
|
Converters = { new ColorJsonConverter(), new JsonStringEnumConverter() },
|
|
};
|
|
|
|
private readonly string _filePath;
|
|
private readonly DispatcherTimer _saveTimer;
|
|
private AppSettings? _settings;
|
|
|
|
public SettingsService()
|
|
{
|
|
string folder = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"CursorLang");
|
|
_filePath = Path.Combine(folder, "settings.json");
|
|
|
|
// Ползунки меняют значения непрерывно, поэтому запись на диск
|
|
// откладывается до паузы в изменениях
|
|
_saveTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) };
|
|
_saveTimer.Tick += OnSaveTimerTick;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Читает настройки с диска либо отдаёт значения по умолчанию,
|
|
/// и дальше сам сохраняет любые изменения.
|
|
/// </summary>
|
|
public AppSettings Load()
|
|
{
|
|
_settings = ReadFile() ?? CreateDefault();
|
|
_settings.PropertyChanged += OnSettingsChanged;
|
|
return _settings;
|
|
}
|
|
|
|
public void Save()
|
|
{
|
|
if (_settings is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(_filePath)!);
|
|
File.WriteAllText(_filePath, JsonSerializer.Serialize(_settings, SerializerOptions));
|
|
}
|
|
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
|
|
{
|
|
// Настройки — не тот случай, ради которого стоит ронять приложение
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_saveTimer.Stop();
|
|
_saveTimer.Tick -= OnSaveTimerTick;
|
|
|
|
if (_settings is not null)
|
|
{
|
|
_settings.PropertyChanged -= OnSettingsChanged;
|
|
Save();
|
|
}
|
|
}
|
|
|
|
private AppSettings? ReadFile()
|
|
{
|
|
try
|
|
{
|
|
if (!File.Exists(_filePath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return JsonSerializer.Deserialize<AppSettings>(File.ReadAllText(_filePath), SerializerOptions);
|
|
}
|
|
catch (Exception e) when (e is IOException or UnauthorizedAccessException or JsonException)
|
|
{
|
|
// Испорченный файл не должен мешать запуску: начнём с умолчаний
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Язык по умолчанию берём системный, если он поддерживается
|
|
private static AppSettings CreateDefault()
|
|
{
|
|
string uiLanguage = System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
|
|
return new AppSettings { Language = uiLanguage == "ru" ? "ru" : "en" };
|
|
}
|
|
|
|
private void OnSettingsChanged(object? sender, PropertyChangedEventArgs e)
|
|
{
|
|
_saveTimer.Stop();
|
|
_saveTimer.Start();
|
|
}
|
|
|
|
private void OnSaveTimerTick(object? sender, EventArgs e)
|
|
{
|
|
_saveTimer.Stop();
|
|
Save();
|
|
}
|
|
|
|
// Color не сериализуется штатно, а хранить его читаемым в файле удобно
|
|
private sealed class ColorJsonConverter : JsonConverter<Color>
|
|
{
|
|
public override Color Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
{
|
|
string? value = reader.GetString();
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
return Colors.Black;
|
|
}
|
|
|
|
try
|
|
{
|
|
return (Color)ColorConverter.ConvertFromString(value)!;
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
return Colors.Black;
|
|
}
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, Color value, JsonSerializerOptions options)
|
|
{
|
|
writer.WriteStringValue(value.ToString());
|
|
}
|
|
}
|
|
}
|