added multilang and settings
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CursorLang.Models;
|
||||
|
||||
namespace CursorLang.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Содержимое подсказки у курсора.
|
||||
/// Содержимое подсказки у курсора. Внешний вид берётся прямо из настроек,
|
||||
/// поэтому их правка применяется без перезапуска.
|
||||
/// </summary>
|
||||
public sealed partial class LayoutPopupViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string _shortName = "—";
|
||||
|
||||
public LayoutPopupViewModel(AppSettings settings) => Settings = settings;
|
||||
|
||||
public AppSettings Settings { get; }
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CursorLang.Models;
|
||||
using CursorLang.Services;
|
||||
|
||||
namespace CursorLang.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Показывает текущую раскладку в главном окне и просит показать подсказку
|
||||
/// у курсора, когда пользователь переключил раскладку сам.
|
||||
/// </summary>
|
||||
public sealed partial class MainViewModel : ObservableObject, IDisposable
|
||||
{
|
||||
private readonly IKeyboardLayoutService _layoutService;
|
||||
private readonly ILayoutPopupService _popupService;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _currentLayout;
|
||||
|
||||
public MainViewModel(IKeyboardLayoutService layoutService, ILayoutPopupService popupService)
|
||||
{
|
||||
_layoutService = layoutService;
|
||||
_popupService = popupService;
|
||||
|
||||
_currentLayout = layoutService.Current.DisplayName;
|
||||
_layoutService.LayoutChanged += OnLayoutChanged;
|
||||
}
|
||||
|
||||
public void Dispose() => _layoutService.LayoutChanged -= OnLayoutChanged;
|
||||
|
||||
private void OnLayoutChanged(object? sender, LayoutChangedEventArgs e)
|
||||
{
|
||||
CurrentLayout = e.Layout.DisplayName;
|
||||
|
||||
// При переходе в другое приложение раскладка меняется без участия
|
||||
// пользователя, и всплывающая подсказка была бы навязчивой
|
||||
if (e.Reason == LayoutChangeReason.UserSwitched)
|
||||
{
|
||||
_popupService.Show(e.Layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CursorLang.Models;
|
||||
using CursorLang.Services;
|
||||
|
||||
namespace CursorLang.ViewModels;
|
||||
|
||||
/// <summary>Вариант выбора в списке: значение и его подпись на текущем языке.</summary>
|
||||
public sealed record EnumOption<T>(T Value, string Display);
|
||||
|
||||
/// <summary>
|
||||
/// Окно настроек. Значения правятся прямо в <see cref="AppSettings"/>,
|
||||
/// поэтому подсказка подхватывает их сразу, без кнопки «Применить».
|
||||
/// </summary>
|
||||
public sealed partial class SettingsViewModel : ObservableObject, IDisposable
|
||||
{
|
||||
private static readonly Color[] Palette =
|
||||
[
|
||||
Color.FromRgb(0x20, 0x20, 0x20),
|
||||
Color.FromRgb(0x00, 0x00, 0x00),
|
||||
Color.FromRgb(0x1E, 0x3A, 0x8A),
|
||||
Color.FromRgb(0x0F, 0x76, 0x6E),
|
||||
Color.FromRgb(0x7C, 0x2D, 0x12),
|
||||
Color.FromRgb(0x86, 0x19, 0x8F),
|
||||
Color.FromRgb(0xB9, 0x1C, 0x1C),
|
||||
Color.FromRgb(0xF5, 0xF5, 0xF5),
|
||||
];
|
||||
|
||||
public SettingsViewModel(AppSettings settings, ILocalizationService localization)
|
||||
{
|
||||
Settings = settings;
|
||||
Localization = localization;
|
||||
|
||||
// Язык интерфейса — такая же настройка, как остальные, и хранится там же
|
||||
Localization.CurrentLanguage = settings.Language;
|
||||
Settings.PropertyChanged += OnSettingsChanged;
|
||||
Localization.PropertyChanged += OnLocalizationChanged;
|
||||
|
||||
BuildLocalizedOptions();
|
||||
}
|
||||
|
||||
public AppSettings Settings { get; }
|
||||
|
||||
public ILocalizationService Localization { get; }
|
||||
|
||||
public IReadOnlyList<Color> BackgroundPalette { get; } = Palette;
|
||||
|
||||
public IReadOnlyList<EnumOption<PopupPlacementMode>> PlacementModes { get; private set; } = [];
|
||||
|
||||
public IReadOnlyList<EnumOption<CursorCorner>> CursorCorners { get; private set; } = [];
|
||||
|
||||
public IReadOnlyList<EnumOption<ScreenPosition>> ScreenPositions { get; private set; } = [];
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Settings.PropertyChanged -= OnSettingsChanged;
|
||||
Localization.PropertyChanged -= OnLocalizationChanged;
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(AppSettings.Language))
|
||||
{
|
||||
Localization.CurrentLanguage = Settings.Language;
|
||||
}
|
||||
}
|
||||
|
||||
// Подписи вариантов приходят из ресурсов, поэтому при смене языка
|
||||
// списки нужно собрать заново — привязки сами перечитают их
|
||||
private void OnLocalizationChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == Binding.IndexerName)
|
||||
{
|
||||
BuildLocalizedOptions();
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildLocalizedOptions()
|
||||
{
|
||||
PlacementModes = BuildOptions<PopupPlacementMode>(nameof(PopupPlacementMode));
|
||||
CursorCorners = BuildOptions<CursorCorner>(nameof(CursorCorner));
|
||||
ScreenPositions = BuildOptions<ScreenPosition>(nameof(ScreenPosition));
|
||||
|
||||
OnPropertyChanged(nameof(PlacementModes));
|
||||
OnPropertyChanged(nameof(CursorCorners));
|
||||
OnPropertyChanged(nameof(ScreenPositions));
|
||||
}
|
||||
|
||||
private EnumOption<T>[] BuildOptions<T>(string resourcePrefix) where T : struct, Enum =>
|
||||
[.. Enum.GetValues<T>().Select(value => new EnumOption<T>(value, Localization[$"{resourcePrefix}_{value}"]))];
|
||||
}
|
||||
Reference in New Issue
Block a user